From 8d92c55c021afbe3985846207a85ec8ab4a417e5 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Fri, 15 May 2026 13:56:07 -0400 Subject: [PATCH 01/94] feat(monorepo-toolbox): add helper script for managing monorepo packages Introduced a utility script to streamline monorepo package management. Supports listing packages in default, JSON, or pretty formats. --- monorepo | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100755 monorepo diff --git a/monorepo b/monorepo new file mode 100755 index 000000000..478d90581 --- /dev/null +++ b/monorepo @@ -0,0 +1,88 @@ +#!/bin/sh + +# Monorepo utility toolbox - Helper script for managing monorepo packages + +# Function to list all monorepo packages in pretty format +packages_pretty() { + echo "Monorepo packages:" + find packages -maxdepth 2 -name "composer.json" -type f | while read -r composer_file; do + package_dir=$(dirname "$composer_file") + package_name=$(basename "$package_dir") + echo " - $package_name" + done +} + +# Function to list packages as JSON array +packages_json() { + packages=$(find packages -maxdepth 2 -name "composer.json" -type f | while read -r composer_file; do + package_dir=$(dirname "$composer_file") + basename "$package_dir" + done | awk 'BEGIN{printf "["} {printf "%s\"%s\"", (NR>1?",":""), $0} END{printf "]\n"}') + echo "$packages" +} + +# Function to list package names only (space-separated) - default +packages_default() { + find packages -maxdepth 2 -name "composer.json" -type f | while read -r composer_file; do + package_dir=$(dirname "$composer_file") + basename "$package_dir" + done | tr '\n' ' ' | sed 's/ $/\n/' +} + +# Parse command and options +command="${1:-}" +type_flag="" + +# Parse flags for packages command +if [ "$command" = "packages" ]; then + shift + while [ $# -gt 0 ]; do + case "$1" in + --type=*) + type_flag="${1#*=}" + shift + ;; + -t) + type_flag="$2" + shift 2 + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac + done +fi + +# Execute command +case "$command" in + packages) + case "$type_flag" in + json) + packages_json + ;; + pretty) + packages_pretty + ;; + "") + packages_default + ;; + *) + echo "Unknown type: $type_flag" + echo "Valid types: json, pretty" + exit 1 + ;; + esac + ;; + *) + echo "Monorepo utility toolbox - Helper script for managing monorepo packages" + echo "" + echo "Usage: $0 packages [--type=TYPE|-t TYPE]" + echo "Commands:" + echo " packages List package names (space-separated, default)" + echo " packages -t json List packages as JSON array" + echo " packages -t pretty List all monorepo packages in pretty format" + exit 1 + ;; +esac + From f5d3ae160b4eb848479e50ec8ad90d93700f0a34 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Fri, 15 May 2026 14:01:08 -0400 Subject: [PATCH 02/94] ci(workflows): update PHP versions and monorepo command in GitHub Actions --- .github/workflows/php.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index f2cd9f660..765cf24d4 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -28,7 +28,7 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: 8.0 + php-version: 8.4 coverage: none - name: Install Composer dependencies @@ -36,7 +36,7 @@ jobs: - name: Output list of packages as JSON id: output_data - run: echo "matrix=$(vendor/bin/monorepo-builder packages-json)" >> $GITHUB_OUTPUT + run: echo "matrix=$(./monorepo packages -t json)" >> $GITHUB_OUTPUT outputs: matrix: ${{ steps.output_data.outputs.matrix }} @@ -48,7 +48,7 @@ jobs: strategy: matrix: - php-versions: ['7.4', '8.0'] + php-versions: ['8.3', '8.4', '8.5'] package: ${{fromJson(needs.provide_packages_json.outputs.matrix)}} steps: From 3ad70a9e1bf9d5464cd21ce5d256cfa20c860a09 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Fri, 15 May 2026 14:05:28 -0400 Subject: [PATCH 03/94] ci(workflows): update PHP version in GitHub Actions to 8.5 --- .github/workflows/php.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 765cf24d4..981c1e2fd 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -2,7 +2,7 @@ name: PHP Testing on: push: - branches: [ main ] +# branches: [ main ] pull_request: branches: [ main ] @@ -28,7 +28,7 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: 8.4 + php-version: 8.5 coverage: none - name: Install Composer dependencies From 9f4efa5d0c25dc712fcd65e69fa3fa5fe9a17ab0 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Fri, 15 May 2026 14:06:58 -0400 Subject: [PATCH 04/94] chore(dependencies): update composer dependencies to latest versions --- composer.json | 11 +- composer.lock | 3628 +++++++++++++++++++++++++++++++++---------------- 2 files changed, 2490 insertions(+), 1149 deletions(-) diff --git a/composer.json b/composer.json index 5fa2a7da2..ec7521317 100644 --- a/composer.json +++ b/composer.json @@ -32,17 +32,17 @@ } ], "require": { - "php": "^7.4 || ^8.0", + "php": "^8.5", "ext-fileinfo": "*", "ext-json": "*", "ext-mbstring": "*", "ext-pdo": "*", "ext-simplexml": "*", "ext-spl": "*", + "assetic/framework": "^3.1", "barryvdh/elfinder-flysystem-driver": "^0.3", "erusev/parsedown": "^1.7", "guzzlehttp/guzzle": "^6.0 || ^7.0", - "kriswallsmith/assetic": "^1.4", "laminas/laminas-permissions-acl": "^2.8", "league/climate": "^3.2", "league/flysystem": "^1.0", @@ -56,7 +56,8 @@ "psr/log": "^1.0", "seld/jsonlint": "^1.9", "slim/slim": "^3.7", - "studio-42/elfinder": "2.1.64", + "studio-42/elfinder": "^2.1.64", + "symfony/polyfill-php85": "^1.37", "symfony/translation": "^3.4", "tedivm/stash": "~0.16", "vlucas/phpdotenv": "^5.4" @@ -71,10 +72,10 @@ "mustache/mustache": "^2.11", "php-coveralls/php-coveralls": "^2.2", "phpstan/phpstan": "^1.6", - "phpunit/phpunit": "^9.5", + "phpunit/phpunit": "^13.1", "squizlabs/php_codesniffer": "^3.5", "symfony/yaml": "^3.0", - "symplify/monorepo-builder": "^10.2", + "symplify/monorepo-builder": "^12.7", "twig/twig": "^3.4" }, "autoload": { diff --git a/composer.lock b/composer.lock index fdd9567eb..c9cbe2955 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,97 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "29faccd918f465a35b04d246acab37e4", + "content-hash": "f1f21bd1a0b5f23ba077fbe1873c4e43", "packages": [ + { + "name": "assetic/framework", + "version": "v3.1.5", + "source": { + "type": "git", + "url": "https://github.com/assetic-php/assetic.git", + "reference": "0dc41d117ac679dc21f46b7022d393ef02b3f781" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/assetic-php/assetic/zipball/0dc41d117ac679dc21f46b7022d393ef02b3f781", + "reference": "0dc41d117ac679dc21f46b7022d393ef02b3f781", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "ext-simplexml": "*", + "php": "^7.3 || ^8.0", + "symfony/deprecation-contracts": "^2.2.0 || ^3.0", + "symfony/process": "^3.4 || ^4.0 || ^5.0 || ^6.0 || ^7.0" + }, + "replace": { + "kriswallsmith/assetic": "1.4.0" + }, + "require-dev": { + "meenie/javascript-packer": "^1.1", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.5.8", + "psr/log": "^1.0", + "ptachoire/cssembed": "^1.0", + "scssphp/scssphp": "^1.0", + "symfony/phpunit-bridge": "^3.4 || ^4.0 || ^5.0 || ^6.0 || ^7.0", + "twig/twig": "^2.11", + "wikimedia/less.php": "^3.0 || ^5.0", + "wikimedia/minify": "^2.2" + }, + "suggest": { + "meenie/javascript-packer": "The Assetic\\Filter\\PackerFilter requires meenie/javascript-packer", + "ptachoire/cssembed": "The Assetic\\Filter\\PhpCssEmbedFilter requires ptachoire/cssembed", + "scssphp/scssphp": "The Assetic\\Filter\\ScssphpFilter requires scssphp/scssphp", + "twig/twig": "Assetic provides an integration with the Twig templating engine", + "wikimedia/less.php": "The Assetic\\Filter\\LessphpFilter requires wikimedia/less.php", + "wikimedia/minify": "The Assetic\\Filter\\JavaScriptMinifierFilter && Assetic\\Filter\\CSSMinFilter requires wikimedia/minify" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php", + "src/aliasing.php" + ], + "psr-0": { + "Assetic": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kris Wallsmith", + "email": "kris.wallsmith@gmail.com", + "homepage": "http://kriswallsmith.net/" + }, + { + "name": "Jack Wilkinson", + "email": "me@jackwilky.com", + "homepage": "https://jackwilky.com/" + }, + { + "name": "Luke Towers", + "email": "octobercms@luketowers.ca", + "homepage": "https://luketowers.ca" + } + ], + "description": "Asset Management for PHP", + "homepage": "https://github.com/assetic-php/assetic", + "keywords": [ + "assets", + "compression", + "minification" + ], + "support": { + "issues": "https://github.com/assetic-php/assetic/issues", + "source": "https://github.com/assetic-php/assetic/tree/v3.1.5" + }, + "time": "2026-04-23T03:30:22+00:00" + }, { "name": "barryvdh/elfinder-flysystem-driver", "version": "v0.3.0", @@ -68,24 +157,24 @@ }, { "name": "erusev/parsedown", - "version": "1.7.4", + "version": "1.8.0", "source": { "type": "git", "url": "https://github.com/erusev/parsedown.git", - "reference": "cb17b6477dfff935958ba01325f2e8a2bfa6dab3" + "reference": "96baaad00f71ba04d76e45b4620f54d3beabd6f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/erusev/parsedown/zipball/cb17b6477dfff935958ba01325f2e8a2bfa6dab3", - "reference": "cb17b6477dfff935958ba01325f2e8a2bfa6dab3", + "url": "https://api.github.com/repos/erusev/parsedown/zipball/96baaad00f71ba04d76e45b4620f54d3beabd6f7", + "reference": "96baaad00f71ba04d76e45b4620f54d3beabd6f7", "shasum": "" }, "require": { "ext-mbstring": "*", - "php": ">=5.3.0" + "php": ">=7.1" }, "require-dev": { - "phpunit/phpunit": "^4.8.35" + "phpunit/phpunit": "^7.5|^8.5|^9.6" }, "type": "library", "autoload": { @@ -112,30 +201,36 @@ ], "support": { "issues": "https://github.com/erusev/parsedown/issues", - "source": "https://github.com/erusev/parsedown/tree/1.7.x" + "source": "https://github.com/erusev/parsedown/tree/1.8.0" }, - "time": "2019-12-30T22:54:17+00:00" + "funding": [ + { + "url": "https://github.com/erusev", + "type": "github" + } + ], + "time": "2026-02-16T11:41:01+00:00" }, { "name": "graham-campbell/result-type", - "version": "v1.1.2", + "version": "v1.1.4", "source": { "type": "git", "url": "https://github.com/GrahamCampbell/Result-Type.git", - "reference": "fbd48bce38f73f8a4ec8583362e732e4095e5862" + "reference": "e01f4a821471308ba86aa202fed6698b6b695e3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/fbd48bce38f73f8a4ec8583362e732e4095e5862", - "reference": "fbd48bce38f73f8a4ec8583362e732e4095e5862", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/e01f4a821471308ba86aa202fed6698b6b695e3b", + "reference": "e01f4a821471308ba86aa202fed6698b6b695e3b", "shasum": "" }, "require": { "php": "^7.2.5 || ^8.0", - "phpoption/phpoption": "^1.9.2" + "phpoption/phpoption": "^1.9.5" }, "require-dev": { - "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" + "phpunit/phpunit": "^8.5.41 || ^9.6.22 || ^10.5.45 || ^11.5.7" }, "type": "library", "autoload": { @@ -164,7 +259,7 @@ ], "support": { "issues": "https://github.com/GrahamCampbell/Result-Type/issues", - "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.2" + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.4" }, "funding": [ { @@ -176,26 +271,26 @@ "type": "tidelift" } ], - "time": "2023-11-12T22:16:48+00:00" + "time": "2025-12-27T19:43:20+00:00" }, { "name": "guzzlehttp/guzzle", - "version": "7.8.1", + "version": "7.10.0", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "41042bc7ab002487b876a0683fc8dce04ddce104" + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/41042bc7ab002487b876a0683fc8dce04ddce104", - "reference": "41042bc7ab002487b876a0683fc8dce04ddce104", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^1.5.3 || ^2.0.1", - "guzzlehttp/psr7": "^1.9.1 || ^2.5.1", + "guzzlehttp/promises": "^2.3", + "guzzlehttp/psr7": "^2.8", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", "symfony/deprecation-contracts": "^2.2 || ^3.0" @@ -206,9 +301,9 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", "ext-curl": "*", - "php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999", + "guzzle/client-integration-tests": "3.0.2", "php-http/message-factory": "^1.1", - "phpunit/phpunit": "^8.5.36 || ^9.6.15", + "phpunit/phpunit": "^8.5.39 || ^9.6.20", "psr/log": "^1.1 || ^2.0 || ^3.0" }, "suggest": { @@ -286,7 +381,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.8.1" + "source": "https://github.com/guzzle/guzzle/tree/7.10.0" }, "funding": [ { @@ -302,20 +397,20 @@ "type": "tidelift" } ], - "time": "2023-12-03T20:35:24+00:00" + "time": "2025-08-23T22:36:01+00:00" }, { "name": "guzzlehttp/promises", - "version": "2.0.2", + "version": "2.3.0", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "bbff78d96034045e58e13dedd6ad91b5d1253223" + "reference": "481557b130ef3790cf82b713667b43030dc9c957" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/bbff78d96034045e58e13dedd6ad91b5d1253223", - "reference": "bbff78d96034045e58e13dedd6ad91b5d1253223", + "url": "https://api.github.com/repos/guzzle/promises/zipball/481557b130ef3790cf82b713667b43030dc9c957", + "reference": "481557b130ef3790cf82b713667b43030dc9c957", "shasum": "" }, "require": { @@ -323,7 +418,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.36 || ^9.6.15" + "phpunit/phpunit": "^8.5.44 || ^9.6.25" }, "type": "library", "extra": { @@ -369,7 +464,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.0.2" + "source": "https://github.com/guzzle/promises/tree/2.3.0" }, "funding": [ { @@ -385,20 +480,20 @@ "type": "tidelift" } ], - "time": "2023-12-03T20:19:20+00:00" + "time": "2025-08-22T14:34:08+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.6.2", + "version": "2.9.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221" + "reference": "7d0ed42f28e42d61352a7a79de682e5e67fec884" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/45b30f99ac27b5ca93cb4831afe16285f57b8221", - "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/7d0ed42f28e42d61352a7a79de682e5e67fec884", + "reference": "7d0ed42f28e42d61352a7a79de682e5e67fec884", "shasum": "" }, "require": { @@ -413,8 +508,9 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "http-interop/http-factory-tests": "^0.9", - "phpunit/phpunit": "^8.5.36 || ^9.6.15" + "http-interop/http-factory-tests": "0.9.0", + "jshttp/mime-db": "1.54.0.1", + "phpunit/phpunit": "^8.5.44 || ^9.6.25" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" @@ -485,7 +581,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.6.2" + "source": "https://github.com/guzzle/psr7/tree/2.9.0" }, "funding": [ { @@ -501,7 +597,7 @@ "type": "tidelift" } ], - "time": "2023-12-03T20:05:35+00:00" + "time": "2026-03-10T16:41:02+00:00" }, { "name": "intervention/image", @@ -533,16 +629,16 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "2.4-dev" - }, "laravel": { - "providers": [ - "Intervention\\Image\\ImageServiceProvider" - ], "aliases": { "Image": "Intervention\\Image\\Facades\\Image" - } + }, + "providers": [ + "Intervention\\Image\\ImageServiceProvider" + ] + }, + "branch-alias": { + "dev-master": "2.4-dev" } }, "autoload": { @@ -587,114 +683,34 @@ ], "time": "2022-05-21T17:30:32+00:00" }, - { - "name": "kriswallsmith/assetic", - "version": "v1.4.0", - "source": { - "type": "git", - "url": "https://github.com/kriswallsmith/assetic.git", - "reference": "e911c437dbdf006a8f62c2f59b15b2d69a5e0aa1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/kriswallsmith/assetic/zipball/e911c437dbdf006a8f62c2f59b15b2d69a5e0aa1", - "reference": "e911c437dbdf006a8f62c2f59b15b2d69a5e0aa1", - "shasum": "" - }, - "require": { - "php": ">=5.3.1", - "symfony/process": "~2.1|~3.0" - }, - "conflict": { - "twig/twig": "<1.27" - }, - "require-dev": { - "leafo/lessphp": "^0.3.7", - "leafo/scssphp": "~0.1", - "meenie/javascript-packer": "^1.1", - "mrclay/minify": "<2.3", - "natxet/cssmin": "3.0.4", - "patchwork/jsqueeze": "~1.0|~2.0", - "phpunit/phpunit": "~4.8 || ^5.6", - "psr/log": "~1.0", - "ptachoire/cssembed": "~1.0", - "symfony/phpunit-bridge": "~2.7|~3.0", - "twig/twig": "~1.23|~2.0", - "yfix/packager": "dev-master" - }, - "suggest": { - "leafo/lessphp": "Assetic provides the integration with the lessphp LESS compiler", - "leafo/scssphp": "Assetic provides the integration with the scssphp SCSS compiler", - "leafo/scssphp-compass": "Assetic provides the integration with the SCSS compass plugin", - "patchwork/jsqueeze": "Assetic provides the integration with the JSqueeze JavaScript compressor", - "ptachoire/cssembed": "Assetic provides the integration with phpcssembed to embed data uris", - "twig/twig": "Assetic provides the integration with the Twig templating engine" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4-dev" - } - }, - "autoload": { - "files": [ - "src/functions.php" - ], - "psr-0": { - "Assetic": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Kris Wallsmith", - "email": "kris.wallsmith@gmail.com", - "homepage": "http://kriswallsmith.net/" - } - ], - "description": "Asset Management for PHP", - "homepage": "https://github.com/kriswallsmith/assetic", - "keywords": [ - "assets", - "compression", - "minification" - ], - "support": { - "issues": "https://github.com/kriswallsmith/assetic/issues", - "source": "https://github.com/kriswallsmith/assetic/tree/master" - }, - "time": "2016-11-11T18:43:20+00:00" - }, { "name": "laminas/laminas-permissions-acl", - "version": "2.10.0", + "version": "2.18.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-permissions-acl.git", - "reference": "e927ae0a3001655fea97eb240eeea9d10638e82f" + "reference": "5940f6e7b9e2e3eba671f13dd26e610d2fe9acc3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-permissions-acl/zipball/e927ae0a3001655fea97eb240eeea9d10638e82f", - "reference": "e927ae0a3001655fea97eb240eeea9d10638e82f", + "url": "https://api.github.com/repos/laminas/laminas-permissions-acl/zipball/5940f6e7b9e2e3eba671f13dd26e610d2fe9acc3", + "reference": "5940f6e7b9e2e3eba671f13dd26e610d2fe9acc3", "shasum": "" }, "require": { - "php": "^7.4 || ~8.0.0 || ~8.1.0" + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" }, "conflict": { "laminas/laminas-servicemanager": "<3.0", "zendframework/zend-permissions-acl": "*" }, "require-dev": { - "laminas/laminas-coding-standard": "~2.3.0", - "laminas/laminas-servicemanager": "^3.15.1", - "phpunit/phpunit": "^9.5.0", - "psalm/plugin-phpunit": "^0.17.0", - "vimeo/psalm": "^4.24.0" + "laminas/laminas-coding-standard": "^3.0.1", + "laminas/laminas-servicemanager": "^3.21", + "phpbench/phpbench": "^1.2.10", + "phpunit/phpunit": "^10.5.58", + "psalm/plugin-phpunit": "^0.19.0", + "vimeo/psalm": "^6.13.1" }, "suggest": { "laminas/laminas-servicemanager": "To support Laminas\\Permissions\\Acl\\Assertion\\AssertionManager plugin manager usage" @@ -729,20 +745,20 @@ "type": "community_bridge" } ], - "time": "2022-07-21T09:23:39+00:00" + "time": "2025-11-03T09:15:20+00:00" }, { "name": "league/climate", - "version": "3.8.2", + "version": "3.10.1", "source": { "type": "git", "url": "https://github.com/thephpleague/climate.git", - "reference": "a785a3ac8f584eed4abd45e4e16fe64c46659a28" + "reference": "f2d78fbc504740bcd0209e40a4586c886567ddc9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/climate/zipball/a785a3ac8f584eed4abd45e4e16fe64c46659a28", - "reference": "a785a3ac8f584eed4abd45e4e16fe64c46659a28", + "url": "https://api.github.com/repos/thephpleague/climate/zipball/f2d78fbc504740bcd0209e40a4586c886567ddc9", + "reference": "f2d78fbc504740bcd0209e40a4586c886567ddc9", "shasum": "" }, "require": { @@ -751,9 +767,10 @@ "seld/cli-prompt": "^1.0" }, "require-dev": { - "mikey179/vfsstream": "^1.6.10", - "mockery/mockery": "^1.4.2", - "phpunit/phpunit": "^9.5.10" + "mikey179/vfsstream": "^1.6.12", + "mockery/mockery": "^1.6.12", + "phpunit/phpunit": "^9.6.21", + "squizlabs/php_codesniffer": "^3.10" }, "suggest": { "ext-mbstring": "If ext-mbstring is not available you MUST install symfony/polyfill-mbstring" @@ -792,9 +809,9 @@ ], "support": { "issues": "https://github.com/thephpleague/climate/issues", - "source": "https://github.com/thephpleague/climate/tree/3.8.2" + "source": "https://github.com/thephpleague/climate/tree/3.10.1" }, - "time": "2022-06-18T14:42:08+00:00" + "time": "2026-03-19T19:32:55+00:00" }, { "name": "league/flysystem", @@ -943,16 +960,16 @@ }, { "name": "league/mime-type-detection", - "version": "1.14.0", + "version": "1.16.0", "source": { "type": "git", "url": "https://github.com/thephpleague/mime-type-detection.git", - "reference": "b6a5854368533df0295c5761a0253656a2e52d9e" + "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/b6a5854368533df0295c5761a0253656a2e52d9e", - "reference": "b6a5854368533df0295c5761a0253656a2e52d9e", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/2d6702ff215bf922936ccc1ad31007edc76451b9", + "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9", "shasum": "" }, "require": { @@ -983,7 +1000,7 @@ "description": "Mime-type detection for Flysystem", "support": { "issues": "https://github.com/thephpleague/mime-type-detection/issues", - "source": "https://github.com/thephpleague/mime-type-detection/tree/1.14.0" + "source": "https://github.com/thephpleague/mime-type-detection/tree/1.16.0" }, "funding": [ { @@ -995,25 +1012,25 @@ "type": "tidelift" } ], - "time": "2023-10-17T14:13:20+00:00" + "time": "2024-09-21T08:32:55+00:00" }, { "name": "mcaskill/php-html-build-attributes", - "version": "v1.3.0", + "version": "v1.4.0", "source": { "type": "git", "url": "https://github.com/mcaskill/php-html-build-attributes.git", - "reference": "2f0390b856610b7da2c235263a5a7d90bade64e4" + "reference": "ae8753fcfccec6f5aa08392b95a4fc949ce870cb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mcaskill/php-html-build-attributes/zipball/2f0390b856610b7da2c235263a5a7d90bade64e4", - "reference": "2f0390b856610b7da2c235263a5a7d90bade64e4", + "url": "https://api.github.com/repos/mcaskill/php-html-build-attributes/zipball/ae8753fcfccec6f5aa08392b95a4fc949ce870cb", + "reference": "ae8753fcfccec6f5aa08392b95a4fc949ce870cb", "shasum": "" }, "require": { "ext-json": "*", - "php": ">=5.4.0" + "php": ">=7.1.0" }, "require-dev": { "pestphp/pest": "^1.22", @@ -1044,9 +1061,9 @@ ], "support": { "issues": "https://github.com/mcaskill/php-html-build-attributes/issues", - "source": "https://github.com/mcaskill/php-html-build-attributes/tree/v1.3.0" + "source": "https://github.com/mcaskill/php-html-build-attributes/tree/v1.4.0" }, - "time": "2023-08-09T03:09:43+00:00" + "time": "2025-07-18T17:25:44+00:00" }, { "name": "monolog/monolog", @@ -1186,16 +1203,16 @@ }, { "name": "phpmailer/phpmailer", - "version": "v6.9.1", + "version": "v6.12.0", "source": { "type": "git", "url": "https://github.com/PHPMailer/PHPMailer.git", - "reference": "039de174cd9c17a8389754d3b877a2ed22743e18" + "reference": "d1ac35d784bf9f5e61b424901d5a014967f15b12" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/039de174cd9c17a8389754d3b877a2ed22743e18", - "reference": "039de174cd9c17a8389754d3b877a2ed22743e18", + "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/d1ac35d784bf9f5e61b424901d5a014967f15b12", + "reference": "d1ac35d784bf9f5e61b424901d5a014967f15b12", "shasum": "" }, "require": { @@ -1255,7 +1272,7 @@ "description": "PHPMailer is a full-featured email creation and transfer class for PHP", "support": { "issues": "https://github.com/PHPMailer/PHPMailer/issues", - "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.9.1" + "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.12.0" }, "funding": [ { @@ -1263,20 +1280,20 @@ "type": "github" } ], - "time": "2023-11-25T22:23:28+00:00" + "time": "2025-10-15T16:49:08+00:00" }, { "name": "phpoption/phpoption", - "version": "1.9.2", + "version": "1.9.5", "source": { "type": "git", "url": "https://github.com/schmittjoh/php-option.git", - "reference": "80735db690fe4fc5c76dfa7f9b770634285fa820" + "reference": "75365b91986c2405cf5e1e012c5595cd487a98be" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/80735db690fe4fc5c76dfa7f9b770634285fa820", - "reference": "80735db690fe4fc5c76dfa7f9b770634285fa820", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/75365b91986c2405cf5e1e012c5595cd487a98be", + "reference": "75365b91986c2405cf5e1e012c5595cd487a98be", "shasum": "" }, "require": { @@ -1284,13 +1301,13 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" + "phpunit/phpunit": "^8.5.44 || ^9.6.25 || ^10.5.53 || ^11.5.34" }, "type": "library", "extra": { "bamarni-bin": { "bin-links": true, - "forward-command": true + "forward-command": false }, "branch-alias": { "dev-master": "1.9-dev" @@ -1326,7 +1343,7 @@ ], "support": { "issues": "https://github.com/schmittjoh/php-option/issues", - "source": "https://github.com/schmittjoh/php-option/tree/1.9.2" + "source": "https://github.com/schmittjoh/php-option/tree/1.9.5" }, "funding": [ { @@ -1338,20 +1355,20 @@ "type": "tidelift" } ], - "time": "2023-11-12T21:59:55+00:00" + "time": "2025-12-27T19:41:33+00:00" }, { "name": "pimple/pimple", - "version": "v3.5.0", + "version": "v3.6.2", "source": { "type": "git", "url": "https://github.com/silexphp/Pimple.git", - "reference": "a94b3a4db7fb774b3d78dad2315ddc07629e1bed" + "reference": "8cfe7f74ac22a433d303914eba9ea4c2a834edce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/silexphp/Pimple/zipball/a94b3a4db7fb774b3d78dad2315ddc07629e1bed", - "reference": "a94b3a4db7fb774b3d78dad2315ddc07629e1bed", + "url": "https://api.github.com/repos/silexphp/Pimple/zipball/8cfe7f74ac22a433d303914eba9ea4c2a834edce", + "reference": "8cfe7f74ac22a433d303914eba9ea4c2a834edce", "shasum": "" }, "require": { @@ -1359,12 +1376,12 @@ "psr/container": "^1.1 || ^2.0" }, "require-dev": { - "symfony/phpunit-bridge": "^5.4@dev" + "phpunit/phpunit": "*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.4.x-dev" + "dev-master": "3.x-dev" } }, "autoload": { @@ -1389,9 +1406,9 @@ "dependency injection" ], "support": { - "source": "https://github.com/silexphp/Pimple/tree/v3.5.0" + "source": "https://github.com/silexphp/Pimple/tree/v3.6.2" }, - "time": "2021-10-28T11:13:42+00:00" + "time": "2026-02-26T08:23:44+00:00" }, { "name": "psr/cache", @@ -1544,20 +1561,20 @@ }, { "name": "psr/http-factory", - "version": "1.0.2", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/php-fig/http-factory.git", - "reference": "e616d01114759c4c489f93b099585439f795fe35" + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35", - "reference": "e616d01114759c4c489f93b099585439f795fe35", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", "shasum": "" }, "require": { - "php": ">=7.0.0", + "php": ">=7.1", "psr/http-message": "^1.0 || ^2.0" }, "type": "library", @@ -1581,7 +1598,7 @@ "homepage": "https://www.php-fig.org/" } ], - "description": "Common interfaces for PSR-7 HTTP message factories", + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", "keywords": [ "factory", "http", @@ -1593,9 +1610,9 @@ "response" ], "support": { - "source": "https://github.com/php-fig/http-factory/tree/1.0.2" + "source": "https://github.com/php-fig/http-factory" }, - "time": "2023-04-10T20:10:41+00:00" + "time": "2024-04-15T12:06:14+00:00" }, { "name": "psr/http-message", @@ -1801,23 +1818,23 @@ }, { "name": "seld/jsonlint", - "version": "1.10.1", + "version": "1.11.0", "source": { "type": "git", "url": "https://github.com/Seldaek/jsonlint.git", - "reference": "76d449a358ece77d6f1d6331c68453e657172202" + "reference": "1748aaf847fc731cfad7725aec413ee46f0cc3a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/76d449a358ece77d6f1d6331c68453e657172202", - "reference": "76d449a358ece77d6f1d6331c68453e657172202", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/1748aaf847fc731cfad7725aec413ee46f0cc3a2", + "reference": "1748aaf847fc731cfad7725aec413ee46f0cc3a2", "shasum": "" }, "require": { "php": "^5.3 || ^7.0 || ^8.0" }, "require-dev": { - "phpstan/phpstan": "^1.5", + "phpstan/phpstan": "^1.11", "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^8.5.13" }, "bin": [ @@ -1849,7 +1866,7 @@ ], "support": { "issues": "https://github.com/Seldaek/jsonlint/issues", - "source": "https://github.com/Seldaek/jsonlint/tree/1.10.1" + "source": "https://github.com/Seldaek/jsonlint/tree/1.11.0" }, "funding": [ { @@ -1861,20 +1878,20 @@ "type": "tidelift" } ], - "time": "2023-12-18T13:03:25+00:00" + "time": "2024-07-11T14:55:45+00:00" }, { "name": "slim/slim", - "version": "3.12.5", + "version": "3.13.0", "source": { "type": "git", "url": "https://github.com/slimphp/Slim.git", - "reference": "565632b2d9b64ecedf89546edbbf4f3648089f0c" + "reference": "f899a6e0be000b9e56d164a4241aa60ab8f33c7f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slimphp/Slim/zipball/565632b2d9b64ecedf89546edbbf4f3648089f0c", - "reference": "565632b2d9b64ecedf89546edbbf4f3648089f0c", + "url": "https://api.github.com/repos/slimphp/Slim/zipball/f899a6e0be000b9e56d164a4241aa60ab8f33c7f", + "reference": "f899a6e0be000b9e56d164a4241aa60ab8f33c7f", "shasum": "" }, "require": { @@ -1882,7 +1899,7 @@ "ext-libxml": "*", "ext-simplexml": "*", "nikic/fast-route": "^1.0", - "php": ">=5.5.0", + "php": "^8.1", "pimple/pimple": "^3.0", "psr/container": "^1.0", "psr/http-message": "^1.0" @@ -1891,7 +1908,8 @@ "psr/http-message-implementation": "1.0" }, "require-dev": { - "phpunit/phpunit": "^4.0", + "phpstan/phpstan": "^2.1", + "phpunit/phpunit": "^10.5", "squizlabs/php_codesniffer": "^3.6.0" }, "type": "library", @@ -1936,7 +1954,7 @@ ], "support": { "issues": "https://github.com/slimphp/Slim/issues", - "source": "https://github.com/slimphp/Slim/tree/3.12.5" + "source": "https://github.com/slimphp/Slim/tree/3.13.0" }, "funding": [ { @@ -1948,20 +1966,20 @@ "type": "tidelift" } ], - "time": "2023-07-23T04:32:51+00:00" + "time": "2026-04-28T08:53:26+00:00" }, { "name": "studio-42/elfinder", - "version": "2.1.64", + "version": "2.1.69", "source": { "type": "git", "url": "https://github.com/Studio-42/elFinder.git", - "reference": "deaa6797df1c6f288238b47284c9b593174bfc0f" + "reference": "8f2c3ffafcdd52cf4515f1eec172f4eee44552ad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Studio-42/elFinder/zipball/deaa6797df1c6f288238b47284c9b593174bfc0f", - "reference": "deaa6797df1c6f288238b47284c9b593174bfc0f", + "url": "https://api.github.com/repos/Studio-42/elFinder/zipball/8f2c3ffafcdd52cf4515f1eec172f4eee44552ad", + "reference": "8f2c3ffafcdd52cf4515f1eec172f4eee44552ad", "shasum": "" }, "require": { @@ -2008,7 +2026,7 @@ "homepage": "http://elfinder.org", "support": { "issues": "https://github.com/Studio-42/elFinder/issues", - "source": "https://github.com/Studio-42/elFinder/tree/2.1.64" + "source": "https://github.com/Studio-42/elFinder/tree/2.1.69" }, "funding": [ { @@ -2016,33 +2034,33 @@ "type": "github" } ], - "time": "2023-12-20T07:43:10+00:00" + "time": "2026-05-07T12:53:30+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v2.5.2", + "version": "v3.7.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66" + "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e8b495ea28c1d97b5e0c121748d6f9b53d075c66", - "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/50f59d1f3ca46d41ac911f97a78626b6756af35b", + "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=8.1" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.7-dev" } }, "autoload": { @@ -2067,7 +2085,7 @@ "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" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.0" }, "funding": [ { @@ -2078,29 +2096,33 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2022-01-02T09:53:40+00:00" + "time": "2026-04-13T15:52:40+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.28.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb" + "reference": "141046a8f9477948ff284fa65be2095baafb94f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", - "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/141046a8f9477948ff284fa65be2095baafb94f2", + "reference": "141046a8f9477948ff284fa65be2095baafb94f2", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-ctype": "*" @@ -2110,12 +2132,9 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -2149,7 +2168,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.37.0" }, "funding": [ { @@ -2160,29 +2179,34 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2023-01-26T09:26:14+00:00" + "time": "2026-04-10T16:19:22+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.28.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "42292d99c55abe617799667f454222c54c60e229" + "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", - "reference": "42292d99c55abe617799667f454222c54c60e229", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6a21eb99c6973357967f6ce3708cd55a6bec6315", + "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315", "shasum": "" }, "require": { - "php": ">=7.1" + "ext-iconv": "*", + "php": ">=7.2" }, "provide": { "ext-mbstring": "*" @@ -2192,12 +2216,9 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -2232,7 +2253,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.37.0" }, "funding": [ { @@ -2243,38 +2264,39 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2023-07-28T09:04:16+00:00" + "time": "2026-04-10T17:25:58+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.28.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5" + "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5", - "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dfb55726c3a76ea3b6459fcfda1ec2d80a682411", + "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -2315,7 +2337,87 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.37.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-10T16:19:22+00:00" + }, + { + "name": "symfony/polyfill-php85", + "version": "v1.37.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php85.git", + "reference": "fcfa4973a9917cef23f2e38774da74a2b7d115ee" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/fcfa4973a9917cef23f2e38774da74a2b7d115ee", + "reference": "fcfa4973a9917cef23f2e38774da74a2b7d115ee", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php85\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.5+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php85/tree/v1.37.0" }, "funding": [ { @@ -2326,29 +2428,33 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2023-01-26T09:26:14+00:00" + "time": "2026-04-26T13:10:57+00:00" }, { "name": "symfony/process", - "version": "v3.4.47", + "version": "v7.4.11", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "b8648cf1d5af12a44a51d07ef9bf980921f15fca" + "reference": "d9593c9efa40499eb078b81144de42cbc28a31f0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/b8648cf1d5af12a44a51d07ef9bf980921f15fca", - "reference": "b8648cf1d5af12a44a51d07ef9bf980921f15fca", + "url": "https://api.github.com/repos/symfony/process/zipball/d9593c9efa40499eb078b81144de42cbc28a31f0", + "reference": "d9593c9efa40499eb078b81144de42cbc28a31f0", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8" + "php": ">=8.2" }, "type": "library", "autoload": { @@ -2373,10 +2479,10 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Process Component", + "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v3.4.47" + "source": "https://github.com/symfony/process/tree/v7.4.11" }, "funding": [ { @@ -2387,12 +2493,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2020-10-24T10:57:07+00:00" + "time": "2026-05-11T16:55:21+00:00" }, { "name": "symfony/translation", @@ -2554,26 +2664,26 @@ }, { "name": "vlucas/phpdotenv", - "version": "v5.6.0", + "version": "v5.6.3", "source": { "type": "git", "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4" + "reference": "955e7815d677a3eaa7075231212f2110983adecc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4", - "reference": "2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/955e7815d677a3eaa7075231212f2110983adecc", + "reference": "955e7815d677a3eaa7075231212f2110983adecc", "shasum": "" }, "require": { "ext-pcre": "*", - "graham-campbell/result-type": "^1.1.2", + "graham-campbell/result-type": "^1.1.4", "php": "^7.2.5 || ^8.0", - "phpoption/phpoption": "^1.9.2", - "symfony/polyfill-ctype": "^1.24", - "symfony/polyfill-mbstring": "^1.24", - "symfony/polyfill-php80": "^1.24" + "phpoption/phpoption": "^1.9.5", + "symfony/polyfill-ctype": "^1.26", + "symfony/polyfill-mbstring": "^1.26", + "symfony/polyfill-php80": "^1.26" }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", @@ -2587,7 +2697,7 @@ "extra": { "bamarni-bin": { "bin-links": true, - "forward-command": true + "forward-command": false }, "branch-alias": { "dev-master": "5.6-dev" @@ -2622,7 +2732,7 @@ ], "support": { "issues": "https://github.com/vlucas/phpdotenv/issues", - "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.0" + "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.3" }, "funding": [ { @@ -2634,22 +2744,22 @@ "type": "tidelift" } ], - "time": "2023-11-12T22:43:29+00:00" + "time": "2025-12-27T19:49:13+00:00" } ], "packages-dev": [ { "name": "aws/aws-crt-php", - "version": "v1.2.4", + "version": "v1.2.7", "source": { "type": "git", "url": "https://github.com/awslabs/aws-crt-php.git", - "reference": "eb0c6e4e142224a10b08f49ebf87f32611d162b2" + "reference": "d71d9906c7bb63a28295447ba12e74723bd3730e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/awslabs/aws-crt-php/zipball/eb0c6e4e142224a10b08f49ebf87f32611d162b2", - "reference": "eb0c6e4e142224a10b08f49ebf87f32611d162b2", + "url": "https://api.github.com/repos/awslabs/aws-crt-php/zipball/d71d9906c7bb63a28295447ba12e74723bd3730e", + "reference": "d71d9906c7bb63a28295447ba12e74723bd3730e", "shasum": "" }, "require": { @@ -2688,22 +2798,22 @@ ], "support": { "issues": "https://github.com/awslabs/aws-crt-php/issues", - "source": "https://github.com/awslabs/aws-crt-php/tree/v1.2.4" + "source": "https://github.com/awslabs/aws-crt-php/tree/v1.2.7" }, - "time": "2023-11-08T00:42:13+00:00" + "time": "2024-10-18T22:15:13+00:00" }, { "name": "aws/aws-sdk-php", - "version": "3.294.2", + "version": "3.381.1", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "e6a63e39fed0fd9fb553af42e99aaf8d7c104c88" + "reference": "405affc23ea14950c378d5ae79420302fcb467dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/e6a63e39fed0fd9fb553af42e99aaf8d7c104c88", - "reference": "e6a63e39fed0fd9fb553af42e99aaf8d7c104c88", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/405affc23ea14950c378d5ae79420302fcb467dc", + "reference": "405affc23ea14950c378d5ae79420302fcb467dc", "shasum": "" }, "require": { @@ -2711,37 +2821,36 @@ "ext-json": "*", "ext-pcre": "*", "ext-simplexml": "*", - "guzzlehttp/guzzle": "^6.5.8 || ^7.4.5", - "guzzlehttp/promises": "^1.4.0 || ^2.0", - "guzzlehttp/psr7": "^1.9.1 || ^2.4.5", - "mtdowling/jmespath.php": "^2.6", - "php": ">=7.2.5", - "psr/http-message": "^1.0 || ^2.0" + "guzzlehttp/guzzle": "^7.4.5", + "guzzlehttp/promises": "^2.0", + "guzzlehttp/psr7": "^2.4.5", + "mtdowling/jmespath.php": "^2.8.0", + "php": ">=8.1", + "psr/http-message": "^1.0 || ^2.0", + "symfony/filesystem": "^v5.4.45 || ^v6.4.3 || ^v7.1.0 || ^v8.0.0" }, "require-dev": { "andrewsville/php-token-reflection": "^1.4", "aws/aws-php-sns-message-validator": "~1.0", "behat/behat": "~3.0", - "composer/composer": "^1.10.22", - "dms/phpunit-arraysubset-asserts": "^0.4.0", + "composer/composer": "^2.7.8", + "dms/phpunit-arraysubset-asserts": "^v0.5.0", "doctrine/cache": "~1.4", "ext-dom": "*", "ext-openssl": "*", - "ext-pcntl": "*", "ext-sockets": "*", - "nette/neon": "^2.3", - "paragonie/random_compat": ">= 2", - "phpunit/phpunit": "^5.6.3 || ^8.5 || ^9.5", - "psr/cache": "^1.0", - "psr/simple-cache": "^1.0", - "sebastian/comparator": "^1.2.3 || ^4.0", - "yoast/phpunit-polyfills": "^1.0" + "phpunit/phpunit": "^10.0", + "psr/cache": "^2.0 || ^3.0", + "psr/simple-cache": "^2.0 || ^3.0", + "sebastian/comparator": "^1.2.3 || ^4.0 || ^5.0", + "yoast/phpunit-polyfills": "^2.0" }, "suggest": { "aws/aws-php-sns-message-validator": "To validate incoming SNS notifications", "doctrine/cache": "To use the DoctrineCacheAdapter", "ext-curl": "To send requests using cURL", "ext-openssl": "Allows working with CloudFront private distributions and verifying received SNS messages", + "ext-pcntl": "To use client-side monitoring", "ext-sockets": "To use client-side monitoring" }, "type": "library", @@ -2756,7 +2865,10 @@ ], "psr-4": { "Aws\\": "src/" - } + }, + "exclude-from-classmap": [ + "src/data/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2765,11 +2877,11 @@ "authors": [ { "name": "Amazon Web Services", - "homepage": "http://aws.amazon.com" + "homepage": "https://aws.amazon.com" } ], "description": "AWS SDK for PHP - Use Amazon Web Services in your PHP project", - "homepage": "http://aws.amazon.com/sdkforphp", + "homepage": "https://aws.amazon.com/sdk-for-php", "keywords": [ "amazon", "aws", @@ -2781,11 +2893,11 @@ "sdk" ], "support": { - "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", + "forum": "https://github.com/aws/aws-sdk-php/discussions", "issues": "https://github.com/aws/aws-sdk-php/issues", - "source": "https://github.com/aws/aws-sdk-php/tree/3.294.2" + "source": "https://github.com/aws/aws-sdk-php/tree/3.381.1" }, - "time": "2023-12-18T19:11:16+00:00" + "time": "2026-05-14T18:08:28+00:00" }, { "name": "cache/adapter-common", @@ -3048,100 +3160,30 @@ "time": "2022-01-15T15:47:19+00:00" }, { - "name": "doctrine/instantiator", - "version": "1.5.0", + "name": "hamcrest/hamcrest-php", + "version": "v2.1.1", "source": { "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" + "url": "https://github.com/hamcrest/hamcrest-php.git", + "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", + "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" + "php": "^7.4|^8.0" + }, + "replace": { + "cordoval/hamcrest-php": "*", + "davedevelopment/hamcrest-php": "*", + "kodova/hamcrest-php": "*" }, "require-dev": { - "doctrine/coding-standard": "^9 || ^11", - "ext-pdo": "*", - "ext-phar": "*", - "phpbench/phpbench": "^0.16 || ^1", - "phpstan/phpstan": "^1.4", - "phpstan/phpstan-phpunit": "^1", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.30 || ^5.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "https://ocramius.github.io/" - } - ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://www.doctrine-project.org/projects/instantiator.html", - "keywords": [ - "constructor", - "instantiate" - ], - "support": { - "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.5.0" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", - "type": "tidelift" - } - ], - "time": "2022-12-30T00:15:36+00:00" - }, - { - "name": "hamcrest/hamcrest-php", - "version": "v2.0.1", - "source": { - "type": "git", - "url": "https://github.com/hamcrest/hamcrest-php.git", - "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", - "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", - "shasum": "" - }, - "require": { - "php": "^5.3|^7.0|^8.0" - }, - "replace": { - "cordoval/hamcrest-php": "*", - "davedevelopment/hamcrest-php": "*", - "kodova/hamcrest-php": "*" - }, - "require-dev": { - "phpunit/php-file-iterator": "^1.4 || ^2.0", - "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0" + "phpunit/php-file-iterator": "^1.4 || ^2.0 || ^3.0", + "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0 || ^8.0 || ^9.0" }, "type": "library", "extra": { @@ -3164,41 +3206,48 @@ ], "support": { "issues": "https://github.com/hamcrest/hamcrest-php/issues", - "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1" + "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.1.1" }, - "time": "2020-07-09T08:09:16+00:00" + "time": "2025-04-30T06:54:44+00:00" }, { "name": "league/csv", - "version": "9.8.0", + "version": "9.28.0", "source": { "type": "git", "url": "https://github.com/thephpleague/csv.git", - "reference": "9d2e0265c5d90f5dd601bc65ff717e05cec19b47" + "reference": "6582ace29ae09ba5b07049d40ea13eb19c8b5073" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/csv/zipball/9d2e0265c5d90f5dd601bc65ff717e05cec19b47", - "reference": "9d2e0265c5d90f5dd601bc65ff717e05cec19b47", + "url": "https://api.github.com/repos/thephpleague/csv/zipball/6582ace29ae09ba5b07049d40ea13eb19c8b5073", + "reference": "6582ace29ae09ba5b07049d40ea13eb19c8b5073", "shasum": "" }, "require": { - "ext-json": "*", - "ext-mbstring": "*", - "php": "^7.4 || ^8.0" + "ext-filter": "*", + "php": "^8.1.2" }, "require-dev": { - "ext-curl": "*", "ext-dom": "*", - "friendsofphp/php-cs-fixer": "^v3.4.0", - "phpstan/phpstan": "^1.3.0", - "phpstan/phpstan-phpunit": "^1.0.0", - "phpstan/phpstan-strict-rules": "^1.1.0", - "phpunit/phpunit": "^9.5.11" + "ext-xdebug": "*", + "friendsofphp/php-cs-fixer": "^3.92.3", + "phpbench/phpbench": "^1.4.3", + "phpstan/phpstan": "^1.12.32", + "phpstan/phpstan-deprecation-rules": "^1.2.1", + "phpstan/phpstan-phpunit": "^1.4.2", + "phpstan/phpstan-strict-rules": "^1.6.2", + "phpunit/phpunit": "^10.5.16 || ^11.5.22 || ^12.5.4", + "symfony/var-dumper": "^6.4.8 || ^7.4.0 || ^8.0" }, "suggest": { - "ext-dom": "Required to use the XMLConverter and or the HTMLConverter classes", - "ext-iconv": "Needed to ease transcoding CSV using iconv stream filters" + "ext-dom": "Required to use the XMLConverter and the HTMLConverter classes", + "ext-iconv": "Needed to ease transcoding CSV using iconv stream filters", + "ext-mbstring": "Needed to ease transcoding CSV using mb stream filters", + "ext-mysqli": "Requiered to use the package with the MySQLi extension", + "ext-pdo": "Required to use the package with the PDO extension", + "ext-pgsql": "Requiered to use the package with the PgSQL extension", + "ext-sqlite3": "Required to use the package with the SQLite3 extension" }, "type": "library", "extra": { @@ -3250,7 +3299,7 @@ "type": "github" } ], - "time": "2022-01-04T00:13:07+00:00" + "time": "2025-12-27T15:18:42+00:00" }, { "name": "league/flysystem-aws-s3-v3", @@ -3435,16 +3484,16 @@ }, { "name": "mockery/mockery", - "version": "1.6.7", + "version": "1.6.12", "source": { "type": "git", "url": "https://github.com/mockery/mockery.git", - "reference": "0cc058854b3195ba21dc6b1f7b1f60f4ef3a9c06" + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mockery/mockery/zipball/0cc058854b3195ba21dc6b1f7b1f60f4ef3a9c06", - "reference": "0cc058854b3195ba21dc6b1f7b1f60f4ef3a9c06", + "url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699", "shasum": "" }, "require": { @@ -3456,8 +3505,8 @@ "phpunit/phpunit": "<8.0" }, "require-dev": { - "phpunit/phpunit": "^8.5 || ^9.6.10", - "symplify/easy-coding-standard": "^12.0.8" + "phpunit/phpunit": "^8.5 || ^9.6.17", + "symplify/easy-coding-standard": "^12.1.14" }, "type": "library", "autoload": { @@ -3514,20 +3563,20 @@ "security": "https://github.com/mockery/mockery/security/advisories", "source": "https://github.com/mockery/mockery" }, - "time": "2023-12-10T02:24:34+00:00" + "time": "2024-05-16T03:13:13+00:00" }, { "name": "mtdowling/jmespath.php", - "version": "2.7.0", + "version": "2.8.0", "source": { "type": "git", "url": "https://github.com/jmespath/jmespath.php.git", - "reference": "bbb69a935c2cbb0c03d7f481a238027430f6440b" + "reference": "a2a865e05d5f420b50cc2f85bb78d565db12a6bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/bbb69a935c2cbb0c03d7f481a238027430f6440b", - "reference": "bbb69a935c2cbb0c03d7f481a238027430f6440b", + "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/a2a865e05d5f420b50cc2f85bb78d565db12a6bc", + "reference": "a2a865e05d5f420b50cc2f85bb78d565db12a6bc", "shasum": "" }, "require": { @@ -3544,7 +3593,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } }, "autoload": { @@ -3578,9 +3627,9 @@ ], "support": { "issues": "https://github.com/jmespath/jmespath.php/issues", - "source": "https://github.com/jmespath/jmespath.php/tree/2.7.0" + "source": "https://github.com/jmespath/jmespath.php/tree/2.8.0" }, - "time": "2023-08-25T10:54:48+00:00" + "time": "2024-09-04T18:46:31+00:00" }, { "name": "mustache/mustache", @@ -3634,16 +3683,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.11.1", + "version": "1.13.4", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", "shasum": "" }, "require": { @@ -3651,11 +3700,12 @@ }, "conflict": { "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3,<3.2.2" + "doctrine/common": "<2.13.3 || >=3 <3.2.2" }, "require-dev": { "doctrine/collections": "^1.6.8", "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, "type": "library", @@ -3681,7 +3731,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" }, "funding": [ { @@ -3689,29 +3739,122 @@ "type": "tidelift" } ], - "time": "2023-03-08T13:26:56+00:00" + "time": "2025-08-01T08:46:24+00:00" + }, + { + "name": "nette/utils", + "version": "v4.1.4", + "source": { + "type": "git", + "url": "https://github.com/nette/utils.git", + "reference": "7da6c396d7ebe142bc857c20479d5e70a5e1aac7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/utils/zipball/7da6c396d7ebe142bc857c20479d5e70a5e1aac7", + "reference": "7da6c396d7ebe142bc857c20479d5e70a5e1aac7", + "shasum": "" + }, + "require": { + "php": "8.2 - 8.5" + }, + "conflict": { + "nette/finder": "<3", + "nette/schema": "<1.2.2" + }, + "require-dev": { + "jetbrains/phpstorm-attributes": "^1.2", + "nette/phpstan-rules": "^1.0", + "nette/tester": "^2.5", + "phpstan/extension-installer": "^1.4@stable", + "phpstan/phpstan": "^2.1@stable", + "tracy/tracy": "^2.9" + }, + "suggest": { + "ext-gd": "to use Image", + "ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()", + "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()", + "ext-json": "to use Nette\\Utils\\Json", + "ext-mbstring": "to use Strings::lower() etc...", + "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "autoload": { + "psr-4": { + "Nette\\": "src" + }, + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.", + "homepage": "https://nette.org", + "keywords": [ + "array", + "core", + "datetime", + "images", + "json", + "nette", + "paginator", + "password", + "slugify", + "string", + "unicode", + "utf-8", + "utility", + "validation" + ], + "support": { + "issues": "https://github.com/nette/utils/issues", + "source": "https://github.com/nette/utils/tree/v4.1.4" + }, + "time": "2026-05-11T20:49:54+00:00" }, { "name": "nikic/php-parser", - "version": "v4.18.0", + "version": "v5.7.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999" + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/1bcbb2179f97633e98bbbc87044ee2611c7d7999", - "reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", "shasum": "" }, "require": { + "ext-ctype": "*", + "ext-json": "*", "ext-tokenizer": "*", - "php": ">=7.0" + "php": ">=7.4" }, "require-dev": { "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "^9.0" }, "bin": [ "bin/php-parse" @@ -3719,7 +3862,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.9-dev" + "dev-master": "5.x-dev" } }, "autoload": { @@ -3743,26 +3886,27 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.18.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" }, - "time": "2023-12-10T21:03:43+00:00" + "time": "2025-12-06T11:56:16+00:00" }, { "name": "phar-io/manifest", - "version": "2.0.3", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + "reference": "54750ef60c58e43759730615a392c31c80e23176" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", "shasum": "" }, "require": { "ext-dom": "*", + "ext-libxml": "*", "ext-phar": "*", "ext-xmlwriter": "*", "phar-io/version": "^3.0.1", @@ -3803,9 +3947,15 @@ "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", "support": { "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.3" + "source": "https://github.com/phar-io/manifest/tree/2.0.4" }, - "time": "2021-07-20T11:28:43+00:00" + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" }, { "name": "phar-io/version", @@ -3860,16 +4010,16 @@ }, { "name": "php-coveralls/php-coveralls", - "version": "v2.7.0", + "version": "v2.8.0", "source": { "type": "git", "url": "https://github.com/php-coveralls/php-coveralls.git", - "reference": "b36fa4394e519dafaddc04ae03976bc65a25ba15" + "reference": "00b9fce4d785a98760ca02f305c197f5fcfb6004" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-coveralls/php-coveralls/zipball/b36fa4394e519dafaddc04ae03976bc65a25ba15", - "reference": "b36fa4394e519dafaddc04ae03976bc65a25ba15", + "url": "https://api.github.com/repos/php-coveralls/php-coveralls/zipball/00b9fce4d785a98760ca02f305c197f5fcfb6004", + "reference": "00b9fce4d785a98760ca02f305c197f5fcfb6004", "shasum": "" }, "require": { @@ -3884,6 +4034,7 @@ "symfony/yaml": "^2.0.5 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0" }, "require-dev": { + "phpspec/prophecy-phpunit": "^1.1 || ^2.3", "phpunit/phpunit": "^4.8.35 || ^5.4.3 || ^6.0 || ^7.0 || >=8.0 <8.5.29 || >=9.0 <9.5.23", "sanmai/phpunit-legacy-adapter": "^6.1 || ^8.0" }, @@ -3937,22 +4088,22 @@ ], "support": { "issues": "https://github.com/php-coveralls/php-coveralls/issues", - "source": "https://github.com/php-coveralls/php-coveralls/tree/v2.7.0" + "source": "https://github.com/php-coveralls/php-coveralls/tree/v2.8.0" }, - "time": "2023-11-22T10:21:01+00:00" + "time": "2025-05-12T08:35:27+00:00" }, { "name": "phpseclib/phpseclib", - "version": "2.0.45", + "version": "2.0.54", "source": { "type": "git", "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "28d8f438a0064c9de80857e3270d071495544640" + "reference": "a96a835067c39ee7a709329fe70869817da18081" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/28d8f438a0064c9de80857e3270d071495544640", - "reference": "28d8f438a0064c9de80857e3270d071495544640", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/a96a835067c39ee7a709329fe70869817da18081", + "reference": "a96a835067c39ee7a709329fe70869817da18081", "shasum": "" }, "require": { @@ -3960,7 +4111,7 @@ }, "require-dev": { "phing/phing": "~2.7", - "phpunit/phpunit": "^4.8.35|^5.7|^6.0|^9.4", + "phpunit/phpunit": "^4.8.35|^5.7|^6.0|^8.5|^9.4", "squizlabs/php_codesniffer": "~2.0" }, "suggest": { @@ -4033,7 +4184,7 @@ ], "support": { "issues": "https://github.com/phpseclib/phpseclib/issues", - "source": "https://github.com/phpseclib/phpseclib/tree/2.0.45" + "source": "https://github.com/phpseclib/phpseclib/tree/2.0.54" }, "funding": [ { @@ -4049,20 +4200,15 @@ "type": "tidelift" } ], - "time": "2023-09-15T20:55:47+00:00" + "time": "2026-04-27T06:59:24+00:00" }, { "name": "phpstan/phpstan", - "version": "1.10.50", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpstan.git", - "reference": "06a98513ac72c03e8366b5a0cb00750b487032e4" - }, + "version": "1.12.33", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/06a98513ac72c03e8366b5a0cb00750b487032e4", - "reference": "06a98513ac72c03e8366b5a0cb00750b487032e4", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/37982d6fc7cbb746dda7773530cda557cdf119e1", + "reference": "37982d6fc7cbb746dda7773530cda557cdf119e1", "shasum": "" }, "require": { @@ -4105,45 +4251,41 @@ { "url": "https://github.com/phpstan", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", - "type": "tidelift" } ], - "time": "2023-12-13T10:59:42+00:00" + "time": "2026-02-28T20:30:03+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "9.2.29", + "version": "14.1.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76" + "reference": "031856c28c060e1c1d1fc94d256e3ffbe4230c91" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6a3a87ac2bbe33b25042753df8195ba4aa534c76", - "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/031856c28c060e1c1d1fc94d256e3ffbe4230c91", + "reference": "031856c28c060e1c1d1fc94d256e3ffbe4230c91", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", + "ext-mbstring": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.15", - "php": ">=7.3", - "phpunit/php-file-iterator": "^3.0.3", - "phpunit/php-text-template": "^2.0.2", - "sebastian/code-unit-reverse-lookup": "^2.0.2", - "sebastian/complexity": "^2.0", - "sebastian/environment": "^5.1.2", - "sebastian/lines-of-code": "^1.0.3", - "sebastian/version": "^3.0.1", - "theseer/tokenizer": "^1.2.0" + "nikic/php-parser": "^5.7.0", + "php": ">=8.4", + "phpunit/php-text-template": "^6.0", + "sebastian/complexity": "^6.0", + "sebastian/environment": "^9.2", + "sebastian/git-state": "^1.0", + "sebastian/lines-of-code": "^5.0", + "sebastian/version": "^7.0", + "theseer/tokenizer": "^2.0.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^13.1" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -4152,7 +4294,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.2-dev" + "dev-main": "14.1.x-dev" } }, "autoload": { @@ -4181,40 +4323,52 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.29" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/14.1.8" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage", + "type": "tidelift" } ], - "time": "2023-09-19T04:57:46+00:00" + "time": "2026-05-09T12:06:52+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "3.0.6", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + "reference": "6e5aa1fb0a95b1703d83e721299ee18bb4e2de50" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/6e5aa1fb0a95b1703d83e721299ee18bb4e2de50", + "reference": "6e5aa1fb0a95b1703d83e721299ee18bb4e2de50", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -4241,36 +4395,49 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/7.0.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-file-iterator", + "type": "tidelift" } ], - "time": "2021-12-02T12:48:52+00:00" + "time": "2026-02-06T04:33:26+00:00" }, { "name": "phpunit/php-invoker", - "version": "3.1.1", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + "reference": "42e5c5cae0c65df12d1b1a3ab52bf3f50f244d88" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/42e5c5cae0c65df12d1b1a3ab52bf3f50f244d88", + "reference": "42e5c5cae0c65df12d1b1a3ab52bf3f50f244d88", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.4" }, "require-dev": { "ext-pcntl": "*", - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^13.0" }, "suggest": { "ext-pcntl": "*" @@ -4278,7 +4445,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -4304,40 +4471,53 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/7.0.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-invoker", + "type": "tidelift" } ], - "time": "2020-09-28T05:58:55+00:00" + "time": "2026-02-06T04:34:47+00:00" }, { "name": "phpunit/php-text-template", - "version": "2.0.4", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + "reference": "a47af19f93f76aa3368303d752aa5272ca3299f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/a47af19f93f76aa3368303d752aa5272ca3299f4", + "reference": "a47af19f93f76aa3368303d752aa5272ca3299f4", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -4363,40 +4543,53 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/6.0.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-text-template", + "type": "tidelift" } ], - "time": "2020-10-26T05:33:50+00:00" + "time": "2026-02-06T04:36:37+00:00" }, { "name": "phpunit/php-timer", - "version": "5.0.3", + "version": "9.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + "reference": "a0e12065831f6ab0d83120dc61513eb8d9a966f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/a0e12065831f6ab0d83120dc61513eb8d9a966f6", + "reference": "a0e12065831f6ab0d83120dc61513eb8d9a966f6", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-main": "9.0-dev" } }, "autoload": { @@ -4422,62 +4615,71 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + "security": "https://github.com/sebastianbergmann/php-timer/security/policy", + "source": "https://github.com/sebastianbergmann/php-timer/tree/9.0.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-timer", + "type": "tidelift" } ], - "time": "2020-10-26T13:16:10+00:00" + "time": "2026-02-06T04:37:53+00:00" }, { "name": "phpunit/phpunit", - "version": "9.6.15", + "version": "13.1.10", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "05017b80304e0eb3f31d90194a563fd53a6021f1" + "reference": "38959098d3c10660a189afaa35a94290c1de67bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/05017b80304e0eb3f31d90194a563fd53a6021f1", - "reference": "05017b80304e0eb3f31d90194a563fd53a6021f1", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/38959098d3c10660a189afaa35a94290c1de67bb", + "reference": "38959098d3c10660a189afaa35a94290c1de67bb", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.3.1 || ^2", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", - "php": ">=7.3", - "phpunit/php-code-coverage": "^9.2.28", - "phpunit/php-file-iterator": "^3.0.5", - "phpunit/php-invoker": "^3.1.1", - "phpunit/php-text-template": "^2.0.3", - "phpunit/php-timer": "^5.0.2", - "sebastian/cli-parser": "^1.0.1", - "sebastian/code-unit": "^1.0.6", - "sebastian/comparator": "^4.0.8", - "sebastian/diff": "^4.0.3", - "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.5", - "sebastian/global-state": "^5.0.1", - "sebastian/object-enumerator": "^4.0.3", - "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^3.2", - "sebastian/version": "^3.0.2" - }, - "suggest": { - "ext-soap": "To be able to generate mocks based on WSDL files", - "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + "myclabs/deep-copy": "^1.13.4", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.4.1", + "phpunit/php-code-coverage": "^14.1.8", + "phpunit/php-file-iterator": "^7.0.0", + "phpunit/php-invoker": "^7.0.0", + "phpunit/php-text-template": "^6.0.0", + "phpunit/php-timer": "^9.0.0", + "sebastian/cli-parser": "^5.0.0", + "sebastian/comparator": "^8.1.3", + "sebastian/diff": "^8.3.0", + "sebastian/environment": "^9.3.0", + "sebastian/exporter": "^8.0.2", + "sebastian/git-state": "^1.0", + "sebastian/global-state": "^9.0.0", + "sebastian/object-enumerator": "^8.0.0", + "sebastian/recursion-context": "^8.0.0", + "sebastian/type": "^7.0.0", + "sebastian/version": "^7.0.0", + "staabm/side-effects-detector": "^1.0.5" }, "bin": [ "phpunit" @@ -4485,7 +4687,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.6-dev" + "dev-main": "13.1-dev" } }, "autoload": { @@ -4517,23 +4719,65 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.15" + "source": "https://github.com/sebastianbergmann/phpunit/tree/13.1.10" }, "funding": [ { - "url": "https://phpunit.de/sponsors.html", - "type": "custom" - }, - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, + "url": "https://phpunit.de/sponsoring.html", + "type": "other" + } + ], + "time": "2026-05-15T08:03:56+00:00" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ { - "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", - "type": "tidelift" + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" } ], - "time": "2023-12-01T16:55:19+00:00" + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" }, { "name": "psr/simple-cache", @@ -4588,28 +4832,28 @@ }, { "name": "sebastian/cli-parser", - "version": "1.0.1", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + "reference": "48a4654fa5e48c1c81214e9930048a572d4b23ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/48a4654fa5e48c1c81214e9930048a572d4b23ca", + "reference": "48a4654fa5e48c1c81214e9930048a572d4b23ca", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -4632,40 +4876,60 @@ "homepage": "https://github.com/sebastianbergmann/cli-parser", "support": { "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/5.0.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/cli-parser", + "type": "tidelift" } ], - "time": "2020-09-28T06:08:49+00:00" + "time": "2026-02-06T04:39:44+00:00" }, { - "name": "sebastian/code-unit", - "version": "1.0.8", + "name": "sebastian/comparator", + "version": "8.1.4", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "1edd557042bf4ff9978ec125d8131b147d5c8224" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1edd557042bf4ff9978ec125d8131b147d5c8224", + "reference": "1edd557042bf4ff9978ec125d8131b147d5c8224", "shasum": "" }, "require": { - "php": ">=7.3" + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.4", + "sebastian/diff": "^8.3", + "sebastian/exporter": "^8.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^13.0" + }, + "suggest": { + "ext-bcmath": "For comparing BcMath\\Number objects" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "8.1-dev" } }, "autoload": { @@ -4680,48 +4944,78 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" } ], - "description": "Collection of value objects that represent the PHP code units", - "homepage": "https://github.com/sebastianbergmann/code-unit", + "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/code-unit/issues", - "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/8.1.4" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" } ], - "time": "2020-10-26T13:08:54+00:00" + "time": "2026-05-15T08:30:51+00:00" }, { - "name": "sebastian/code-unit-reverse-lookup", - "version": "2.0.3", + "name": "sebastian/complexity", + "version": "6.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "c5651c795c98093480df79350cb050813fc7a2f3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/c5651c795c98093480df79350cb050813fc7a2f3", + "reference": "c5651c795c98093480df79350cb050813fc7a2f3", "shasum": "" }, "require": { - "php": ">=7.3" + "nikic/php-parser": "^5.0", + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -4736,179 +5030,62 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/6.0.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" - } - ], - "time": "2020-09-28T05:30:19+00:00" - }, - { - "name": "sebastian/comparator", - "version": "4.0.8", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a", - "shasum": "" - }, - "require": { - "php": ">=7.3", - "sebastian/diff": "^4.0", - "sebastian/exporter": "^4.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" }, { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" }, { - "name": "Volker Dusch", - "email": "github@wallbash.com" + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" }, { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - } - ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-09-14T12:41:17+00:00" - }, - { - "name": "sebastian/complexity", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^4.7", - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for calculating the complexity of PHP code units", - "homepage": "https://github.com/sebastianbergmann/complexity", - "support": { - "issues": "https://github.com/sebastianbergmann/complexity/issues", - "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" + "url": "https://tidelift.com/funding/github/packagist/sebastian/complexity", + "type": "tidelift" } ], - "time": "2020-10-26T15:52:27+00:00" + "time": "2026-02-06T04:41:32+00:00" }, { "name": "sebastian/diff", - "version": "4.0.5", + "version": "8.3.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" + "reference": "b36d33b6e796513de7cb7df053afb3f55eefcd47" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", - "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b36d33b6e796513de7cb7df053afb3f55eefcd47", + "reference": "b36d33b6e796513de7cb7df053afb3f55eefcd47", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^9.3", - "symfony/process": "^4.2 || ^5" + "phpunit/phpunit": "^13.0", + "symfony/process": "^7.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "8.3-dev" } }, "autoload": { @@ -4940,35 +5117,48 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5" + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/8.3.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/diff", + "type": "tidelift" } ], - "time": "2023-05-07T05:35:17+00:00" + "time": "2026-05-15T04:58:09+00:00" }, { "name": "sebastian/environment", - "version": "5.1.5", + "version": "9.3.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" + "reference": "6767059a30e4277ac95ee034809e793528464768" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6767059a30e4277ac95ee034809e793528464768", + "reference": "6767059a30e4277ac95ee034809e793528464768", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^13.0" }, "suggest": { "ext-posix": "*" @@ -4976,7 +5166,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.1-dev" + "dev-main": "9.3-dev" } }, "autoload": { @@ -4995,7 +5185,7 @@ } ], "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", + "homepage": "https://github.com/sebastianbergmann/environment", "keywords": [ "Xdebug", "environment", @@ -5003,42 +5193,55 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/9.3.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/environment", + "type": "tidelift" } ], - "time": "2023-02-03T06:03:51+00:00" + "time": "2026-04-15T12:14:03+00:00" }, { "name": "sebastian/exporter", - "version": "4.0.5", + "version": "8.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" + "reference": "9cee180ebe62259e3ed48df2212d1fc8cfd971bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/9cee180ebe62259e3ed48df2212d1fc8cfd971bb", + "reference": "9cee180ebe62259e3ed48df2212d1fc8cfd971bb", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/recursion-context": "^4.0" + "ext-mbstring": "*", + "php": ">=8.4", + "sebastian/recursion-context": "^8.0" }, "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "8.0-dev" } }, "autoload": { @@ -5080,46 +5283,125 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/8.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" + } + ], + "time": "2026-04-15T12:38:05+00:00" + }, + { + "name": "sebastian/git-state", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/git-state.git", + "reference": "792a952e0eba55b6960a48aeceb9f371aad1f76b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/git-state/zipball/792a952e0eba55b6960a48aeceb9f371aad1f76b", + "reference": "792a952e0eba55b6960a48aeceb9f371aad1f76b", + "shasum": "" + }, + "require": { + "php": ">=8.4" + }, + "require-dev": { + "phpunit/phpunit": "^13.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for describing the state of a Git checkout", + "homepage": "https://github.com/sebastianbergmann/git-state", + "support": { + "issues": "https://github.com/sebastianbergmann/git-state/issues", + "security": "https://github.com/sebastianbergmann/git-state/security/policy", + "source": "https://github.com/sebastianbergmann/git-state/tree/1.0.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/git-state", + "type": "tidelift" } ], - "time": "2022-09-14T06:03:37+00:00" + "time": "2026-03-21T12:54:28+00:00" }, { "name": "sebastian/global-state", - "version": "5.0.6", + "version": "9.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "bde739e7565280bda77be70044ac1047bc007e34" + "reference": "e52e3dc22441e6218c710afe72c3042f8fc41ea7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bde739e7565280bda77be70044ac1047bc007e34", - "reference": "bde739e7565280bda77be70044ac1047bc007e34", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e52e3dc22441e6218c710afe72c3042f8fc41ea7", + "reference": "e52e3dc22441e6218c710afe72c3042f8fc41ea7", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" + "php": ">=8.4", + "sebastian/object-reflector": "^6.0", + "sebastian/recursion-context": "^8.0" }, "require-dev": { "ext-dom": "*", - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-uopz": "*" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-main": "9.0-dev" } }, "autoload": { @@ -5138,47 +5420,60 @@ } ], "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", "keywords": [ "global state" ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.6" + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/9.0.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/global-state", + "type": "tidelift" } ], - "time": "2023-08-02T09:26:13+00:00" + "time": "2026-02-06T04:45:13+00:00" }, { "name": "sebastian/lines-of-code", - "version": "1.0.3", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" + "reference": "4f21bb7768e1c997722ccc7efb1d6b5c11bfd471" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/4f21bb7768e1c997722ccc7efb1d6b5c11bfd471", + "reference": "4f21bb7768e1c997722ccc7efb1d6b5c11bfd471", "shasum": "" }, "require": { - "nikic/php-parser": "^4.6", - "php": ">=7.3" + "nikic/php-parser": "^5.0", + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -5201,42 +5496,55 @@ "homepage": "https://github.com/sebastianbergmann/lines-of-code", "support": { "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/5.0.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/lines-of-code", + "type": "tidelift" } ], - "time": "2020-11-28T06:42:11+00:00" + "time": "2026-02-06T04:45:54+00:00" }, { "name": "sebastian/object-enumerator", - "version": "4.0.4", + "version": "8.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + "reference": "b39ab125fd9a7434b0ecbc4202eebce11a98cfc5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/b39ab125fd9a7434b0ecbc4202eebce11a98cfc5", + "reference": "b39ab125fd9a7434b0ecbc4202eebce11a98cfc5", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" + "php": ">=8.4", + "sebastian/object-reflector": "^6.0", + "sebastian/recursion-context": "^8.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "8.0-dev" } }, "autoload": { @@ -5258,40 +5566,53 @@ "homepage": "https://github.com/sebastianbergmann/object-enumerator/", "support": { "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/8.0.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/object-enumerator", + "type": "tidelift" } ], - "time": "2020-10-26T13:12:34+00:00" + "time": "2026-02-06T04:46:36+00:00" }, { "name": "sebastian/object-reflector", - "version": "2.0.4", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + "reference": "3ca042c2c60b0eab094f8a1b6a7093f4d4c72200" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/3ca042c2c60b0eab094f8a1b6a7093f4d4c72200", + "reference": "3ca042c2c60b0eab094f8a1b6a7093f4d4c72200", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -5313,40 +5634,53 @@ "homepage": "https://github.com/sebastianbergmann/object-reflector/", "support": { "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/6.0.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/object-reflector", + "type": "tidelift" } ], - "time": "2020-10-26T13:14:26+00:00" + "time": "2026-02-06T04:47:13+00:00" }, { "name": "sebastian/recursion-context", - "version": "4.0.5", + "version": "8.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + "reference": "74c5af21f6a5833e91767ca068c4d3dfec15317e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/74c5af21f6a5833e91767ca068c4d3dfec15317e", + "reference": "74c5af21f6a5833e91767ca068c4d3dfec15317e", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "8.0-dev" } }, "autoload": { @@ -5376,40 +5710,53 @@ "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/8.0.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" } ], - "time": "2023-02-03T06:07:39+00:00" + "time": "2026-02-06T04:51:28+00:00" }, { - "name": "sebastian/resource-operations", - "version": "3.0.3", + "name": "sebastian/type", + "version": "7.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "42412224607bd3931241bbd17f38e0f972f5a916" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/42412224607bd3931241bbd17f38e0f972f5a916", + "reference": "42412224607bd3931241bbd17f38e0f972f5a916", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^9.0" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -5424,48 +5771,58 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", "support": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + "issues": "https://github.com/sebastianbergmann/type/issues", + "security": "https://github.com/sebastianbergmann/type/security/policy", + "source": "https://github.com/sebastianbergmann/type/tree/7.0.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/type", + "type": "tidelift" } ], - "abandoned": true, - "time": "2020-09-28T06:45:17+00:00" + "time": "2026-02-06T04:52:09+00:00" }, { - "name": "sebastian/type", - "version": "3.2.1", + "name": "sebastian/version", + "version": "7.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/type.git", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "ad37a5552c8e2b88572249fdc19b6da7792e021b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/ad37a5552c8e2b88572249fdc19b6da7792e021b", + "reference": "ad37a5552c8e2b88572249fdc19b6da7792e021b", "shasum": "" }, "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.5" + "php": ">=8.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -5484,85 +5841,45 @@ "role": "lead" } ], - "description": "Collection of value objects that represent the types of the PHP type system", - "homepage": "https://github.com/sebastianbergmann/type", + "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/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + "issues": "https://github.com/sebastianbergmann/version/issues", + "security": "https://github.com/sebastianbergmann/version/security/policy", + "source": "https://github.com/sebastianbergmann/version/tree/7.0.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" - } - ], - "time": "2023-02-03T06:13:03+00:00" - }, - { - "name": "sebastian/version", - "version": "3.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c6c1022351a901512170118436c764e473f6de8c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", - "reference": "c6c1022351a901512170118436c764e473f6de8c", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ + }, { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" - }, - "funding": [ + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, { - "url": "https://github.com/sebastianbergmann", - "type": "github" + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/version", + "type": "tidelift" } ], - "time": "2020-09-28T06:39:44+00:00" + "time": "2026-02-06T04:52:52+00:00" }, { "name": "squizlabs/php_codesniffer", - "version": "3.8.0", + "version": "3.13.5", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "5805f7a4e4958dbb5e944ef1e6edae0a303765e7" + "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/5805f7a4e4958dbb5e944ef1e6edae0a303765e7", - "reference": "5805f7a4e4958dbb5e944ef1e6edae0a303765e7", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/0ca86845ce43291e8f5692c7356fccf3bcf02bf4", + "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4", "shasum": "" }, "require": { @@ -5572,18 +5889,13 @@ "php": ">=5.4.0" }, "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" }, "bin": [ - "bin/phpcs", - "bin/phpcbf" + "bin/phpcbf", + "bin/phpcs" ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" @@ -5627,53 +5939,926 @@ { "url": "https://opencollective.com/php_codesniffer", "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcsstandards", + "type": "thanks_dev" + } + ], + "time": "2025-11-04T16:30:35+00:00" + }, + { + "name": "staabm/side-effects-detector", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/staabm/side-effects-detector.git", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.6", + "phpunit/phpunit": "^9.6.21", + "symfony/var-dumper": "^5.4.43", + "tomasvotruba/type-coverage": "1.0.0", + "tomasvotruba/unused-public": "1.0.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A static analysis tool to detect side effects in PHP code", + "keywords": [ + "static analysis" + ], + "support": { + "issues": "https://github.com/staabm/side-effects-detector/issues", + "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" + }, + "funding": [ + { + "url": "https://github.com/staabm", + "type": "github" } ], - "time": "2023-12-08T12:32:31+00:00" + "time": "2024-10-20T05:08:20+00:00" }, { "name": "symfony/config", - "version": "v5.4.31", + "version": "v7.4.10", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "dd5ea39de228813aba0c23c3a4153da2a4cf3cd9" + "reference": "d91b6c7cd2a8c9a9c2b8d26c8f5ed48edf99ef57" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/dd5ea39de228813aba0c23c3a4153da2a4cf3cd9", - "reference": "dd5ea39de228813aba0c23c3a4153da2a4cf3cd9", + "url": "https://api.github.com/repos/symfony/config/zipball/d91b6c7cd2a8c9a9c2b8d26c8f5ed48edf99ef57", + "reference": "d91b6c7cd2a8c9a9c2b8d26c8f5ed48edf99ef57", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/filesystem": "^4.4|^5.0|^6.0", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-php80": "^1.16", - "symfony/polyfill-php81": "^1.22" + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/filesystem": "^7.1|^8.0", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/finder": "<6.4", + "symfony/service-contracts": "<2.5" + }, + "require-dev": { + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/finder": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Config\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/config/tree/v7.4.10" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-03T14:20:49+00:00" + }, + { + "name": "symfony/console", + "version": "v7.4.11", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "ed0107e43ab452aa77ae99e005b95e56b556e075" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/ed0107e43ab452aa77ae99e005b95e56b556e075", + "reference": "ed0107e43ab452aa77ae99e005b95e56b556e075", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^7.2|^8.0" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/lock": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0" + }, + "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/v7.4.11" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-13T12:04:42+00:00" + }, + { + "name": "symfony/dependency-injection", + "version": "v8.0.10", + "source": { + "type": "git", + "url": "https://github.com/symfony/dependency-injection.git", + "reference": "6fc374dae45a7633a5865da7fc2908baf29d4900" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/6fc374dae45a7633a5865da7fc2908baf29d4900", + "reference": "6fc374dae45a7633a5865da7fc2908baf29d4900", + "shasum": "" + }, + "require": { + "php": ">=8.4", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/service-contracts": "^3.6", + "symfony/var-exporter": "^7.4|^8.0" }, "conflict": { - "symfony/finder": "<4.4" + "ext-psr": "<1.1|>=2" + }, + "provide": { + "psr/container-implementation": "1.1|2.0", + "symfony/service-implementation": "1.1|2.0|3.0" }, "require-dev": { - "symfony/event-dispatcher": "^4.4|^5.0|^6.0", - "symfony/finder": "^4.4|^5.0|^6.0", - "symfony/messenger": "^4.4|^5.0|^6.0", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/yaml": "^4.4|^5.0|^6.0" + "symfony/config": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/yaml": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\DependencyInjection\\": "" + }, + "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": "Allows you to standardize and centralize the way objects are constructed in your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/dependency-injection/tree/v8.0.10" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-06T11:55:35+00:00" + }, + { + "name": "symfony/error-handler", + "version": "v8.0.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/error-handler.git", + "reference": "c1119fe8dcfc3825ec74ec061b96ef0c8f281517" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/c1119fe8dcfc3825ec74ec061b96ef0c8f281517", + "reference": "c1119fe8dcfc3825ec74ec061b96ef0c8f281517", + "shasum": "" + }, + "require": { + "php": ">=8.4", + "psr/log": "^1|^2|^3", + "symfony/polyfill-php85": "^1.32", + "symfony/var-dumper": "^7.4|^8.0" + }, + "conflict": { + "symfony/deprecation-contracts": "<2.5" + }, + "require-dev": { + "symfony/console": "^7.4|^8.0", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/serializer": "^7.4|^8.0", + "symfony/webpack-encore-bundle": "^1.0|^2.0" + }, + "bin": [ + "Resources/bin/patch-type-declarations" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\ErrorHandler\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to manage errors and ease debugging PHP code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/error-handler/tree/v8.0.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-03-30T15:14:47+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v8.0.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "0c3c1a17604c4dbbec4b93fe162c538482096e1f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/0c3c1a17604c4dbbec4b93fe162c538482096e1f", + "reference": "0c3c1a17604c4dbbec4b93fe162c538482096e1f", + "shasum": "" + }, + "require": { + "php": ">=8.4", + "symfony/event-dispatcher-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/security-http": "<7.4", + "symfony/service-contracts": "<2.5" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/error-handler": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/framework-bundle": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v8.0.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-18T13:51:42+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v3.7.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "ccba7060602b7fed0b03c85bf025257f76d9ef32" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/ccba7060602b7fed0b03c85bf025257f76d9ef32", + "reference": "ccba7060602b7fed0b03c85bf025257f76d9ef32", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/event-dispatcher": "^1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.7-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.7.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-01-05T13:30:16+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v8.0.11", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "224db910898ce1317b892a9a1338f1f8f17eb7c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/224db910898ce1317b892a9a1338f1f8f17eb7c7", + "reference": "224db910898ce1317b892a9a1338f1f8f17eb7c7", + "shasum": "" + }, + "require": { + "php": ">=8.4", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "require-dev": { + "symfony/process": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v8.0.11" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-11T16:39:47+00:00" + }, + { + "name": "symfony/finder", + "version": "v8.0.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "8da41214757b87d97f181e3d14a4179286151007" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/8da41214757b87d97f181e3d14a4179286151007", + "reference": "8da41214757b87d97f181e3d14a4179286151007", + "shasum": "" + }, + "require": { + "php": ">=8.4" + }, + "require-dev": { + "symfony/filesystem": "^7.4|^8.0" + }, + "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/v8.0.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-03-30T15:14:47+00:00" + }, + { + "name": "symfony/http-foundation", + "version": "v8.0.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "02656f7ebeae5c155d659e946f6b3a33df24051b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/02656f7ebeae5c155d659e946f6b3a33df24051b", + "reference": "02656f7ebeae5c155d659e946f6b3a33df24051b", + "shasum": "" + }, + "require": { + "php": ">=8.4", + "symfony/polyfill-mbstring": "^1.1" + }, + "conflict": { + "doctrine/dbal": "<4.3" + }, + "require-dev": { + "doctrine/dbal": "^4.3", + "predis/predis": "^1.1|^2.0", + "symfony/cache": "^7.4|^8.0", + "symfony/clock": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/mime": "^7.4|^8.0", + "symfony/rate-limiter": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "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": "Defines an object-oriented layer for the HTTP specification", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-foundation/tree/v8.0.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-03-30T15:14:47+00:00" + }, + { + "name": "symfony/http-kernel", + "version": "v8.0.11", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-kernel.git", + "reference": "20d3680373f4b791903c09e74b45402b4aeda71c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/20d3680373f4b791903c09e74b45402b4aeda71c", + "reference": "20d3680373f4b791903c09e74b45402b4aeda71c", + "shasum": "" + }, + "require": { + "php": ">=8.4", + "psr/log": "^1|^2|^3", + "symfony/error-handler": "^7.4|^8.0", + "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/flex": "<2.10", + "symfony/http-client-contracts": "<2.5", + "symfony/translation-contracts": "<2.5", + "twig/twig": "<3.21" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/cache": "^1.0|^2.0|^3.0", + "symfony/browser-kit": "^7.4|^8.0", + "symfony/clock": "^7.4|^8.0", + "symfony/config": "^7.4|^8.0", + "symfony/console": "^7.4|^8.0", + "symfony/css-selector": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/dom-crawler": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/finder": "^7.4|^8.0", + "symfony/http-client-contracts": "^2.5|^3", + "symfony/process": "^7.4|^8.0", + "symfony/property-access": "^7.4|^8.0", + "symfony/routing": "^7.4|^8.0", + "symfony/serializer": "^7.4|^8.0", + "symfony/stopwatch": "^7.4|^8.0", + "symfony/translation": "^7.4|^8.0", + "symfony/translation-contracts": "^2.5|^3", + "symfony/uid": "^7.4|^8.0", + "symfony/validator": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0", + "symfony/var-exporter": "^7.4|^8.0", + "twig/twig": "^3.21" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpKernel\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a structured process for converting a Request into a Response", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-kernel/tree/v8.0.11" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-13T18:07:14+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.37.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "4864388bfbd3001ce88e234fab652acd91fdc57e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/4864388bfbd3001ce88e234fab652acd91fdc57e", + "reference": "4864388bfbd3001ce88e234fab652acd91fdc57e", + "shasum": "" + }, + "require": { + "php": ">=7.2" }, "suggest": { - "symfony/yaml": "To use the yaml reference dumper" + "ext-intl": "For best performance" }, "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Component\\Config\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -5681,18 +6866,26 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", + "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/config/tree/v5.4.31" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.37.0" }, "funding": [ { @@ -5703,66 +6896,53 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2023-11-09T08:22:43+00:00" + "time": "2026-04-26T13:13:48+00:00" }, { - "name": "symfony/console", - "version": "v4.4.49", + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.37.0", "source": { "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "33fa45ffc81fdcc1ca368d4946da859c8cdb58d9" + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "3833d7255cc303546435cb650316bff708a1c75c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/33fa45ffc81fdcc1ca368d4946da859c8cdb58d9", - "reference": "33fa45ffc81fdcc1ca368d4946da859c8cdb58d9", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", "shasum": "" }, "require": { - "php": ">=7.1.3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php73": "^1.8", - "symfony/polyfill-php80": "^1.16", - "symfony/service-contracts": "^1.1|^2" - }, - "conflict": { - "psr/log": ">=3", - "symfony/dependency-injection": "<3.4", - "symfony/event-dispatcher": "<4.3|>=5", - "symfony/lock": "<4.4", - "symfony/process": "<3.3" - }, - "provide": { - "psr/log-implementation": "1.0|2.0" - }, - "require-dev": { - "psr/log": "^1|^2", - "symfony/config": "^3.4|^4.0|^5.0", - "symfony/dependency-injection": "^3.4|^4.0|^5.0", - "symfony/event-dispatcher": "^4.3", - "symfony/lock": "^4.4|^5.0", - "symfony/process": "^3.4|^4.0|^5.0", - "symfony/var-dumper": "^4.3|^5.0" + "php": ">=7.2" }, "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" + "ext-intl": "For best performance" }, "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Component\\Console\\": "" + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", @@ -5771,18 +6951,26 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Eases the creation of beautiful and testable command line interfaces", + "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/console/tree/v4.4.49" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.37.0" }, "funding": [ { @@ -5793,40 +6981,55 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2022-11-05T17:10:16+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { - "name": "symfony/filesystem", - "version": "v5.4.25", + "name": "symfony/service-contracts", + "version": "v3.7.0", "source": { "type": "git", - "url": "https://github.com/symfony/filesystem.git", - "reference": "0ce3a62c9579a53358d3a7eb6b3dfb79789a6364" + "url": "https://github.com/symfony/service-contracts.git", + "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/0ce3a62c9579a53358d3a7eb6b3dfb79789a6364", - "reference": "0ce3a62c9579a53358d3a7eb6b3dfb79789a6364", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d25d82433a80eba6aa0e6c24b61d7370d99e444a", + "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.8", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" }, "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.7-dev" + } + }, "autoload": { "psr-4": { - "Symfony\\Component\\Filesystem\\": "" + "Symfony\\Contracts\\Service\\": "" }, "exclude-from-classmap": [ - "/Tests/" + "/Test/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -5835,18 +7038,26 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Provides basic utilities for the filesystem", + "description": "Generic abstractions related to writing services", "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], "support": { - "source": "https://github.com/symfony/filesystem/tree/v5.4.25" + "source": "https://github.com/symfony/service-contracts/tree/v3.7.0" }, "funding": [ { @@ -5857,49 +7068,42 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2023-05-31T13:04:02+00:00" + "time": "2026-03-28T09:44:51+00:00" }, { - "name": "symfony/polyfill-php73", - "version": "v1.28.0", + "name": "symfony/stopwatch", + "version": "v7.4.8", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "fe2f306d1d9d346a7fee353d0d5012e401e984b5" + "url": "https://github.com/symfony/stopwatch.git", + "reference": "70a852d72fec4d51efb1f48dcd968efcaf5ccb89" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fe2f306d1d9d346a7fee353d0d5012e401e984b5", - "reference": "fe2f306d1d9d346a7fee353d0d5012e401e984b5", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/70a852d72fec4d51efb1f48dcd968efcaf5ccb89", + "reference": "70a852d72fec4d51efb1f48dcd968efcaf5ccb89", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=8.2", + "symfony/service-contracts": "^2.5|^3" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, "autoload": { - "files": [ - "bootstrap.php" - ], "psr-4": { - "Symfony\\Polyfill\\Php73\\": "" + "Symfony\\Component\\Stopwatch\\": "" }, - "classmap": [ - "Resources/stubs" + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -5908,24 +7112,18 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "description": "Provides a way to profile code", "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.28.0" + "source": "https://github.com/symfony/stopwatch/tree/v7.4.8" }, "funding": [ { @@ -5936,49 +7134,58 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2023-01-26T09:26:14+00:00" + "time": "2026-03-24T13:12:05+00:00" }, { - "name": "symfony/polyfill-php81", - "version": "v1.28.0", + "name": "symfony/string", + "version": "v8.0.11", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php81.git", - "reference": "7581cd600fa9fd681b797d00b02f068e2f13263b" + "url": "https://github.com/symfony/string.git", + "reference": "39be2ad058a3c0bd558edca23e65f009865d75ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/7581cd600fa9fd681b797d00b02f068e2f13263b", - "reference": "7581cd600fa9fd681b797d00b02f068e2f13263b", + "url": "https://api.github.com/repos/symfony/string/zipball/39be2ad058a3c0bd558edca23e65f009865d75ff", + "reference": "39be2ad058a3c0bd558edca23e65f009865d75ff", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=8.4", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-intl-grapheme": "^1.33", + "symfony/polyfill-intl-normalizer": "^1.0", + "symfony/polyfill-mbstring": "^1.0" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/emoji": "^7.4|^8.0", + "symfony/http-client": "^7.4|^8.0", + "symfony/intl": "^7.4|^8.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^7.4|^8.0" }, + "type": "library", "autoload": { "files": [ - "bootstrap.php" + "Resources/functions.php" ], "psr-4": { - "Symfony\\Polyfill\\Php81\\": "" + "Symfony\\Component\\String\\": "" }, - "classmap": [ - "Resources/stubs" + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -5995,16 +7202,18 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "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": [ - "compatibility", - "polyfill", - "portable", - "shim" + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.28.0" + "source": "https://github.com/symfony/string/tree/v8.0.11" }, "funding": [ { @@ -6015,52 +7224,60 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2023-01-26T09:26:14+00:00" + "time": "2026-05-13T12:07:53+00:00" }, { - "name": "symfony/service-contracts", - "version": "v2.5.2", + "name": "symfony/var-dumper", + "version": "v8.0.8", "source": { "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c" + "url": "https://github.com/symfony/var-dumper.git", + "reference": "cfb7badd53bf4177f6e9416cfbbccc13c0e773a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/4b426aac47d6427cc1a1d0f7e2ac724627f5966c", - "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/cfb7badd53bf4177f6e9416cfbbccc13c0e773a1", + "reference": "cfb7badd53bf4177f6e9416cfbbccc13c0e773a1", "shasum": "" }, "require": { - "php": ">=7.2.5", - "psr/container": "^1.1", - "symfony/deprecation-contracts": "^2.1|^3" + "php": ">=8.4", + "symfony/polyfill-mbstring": "^1.0" }, "conflict": { - "ext-psr": "<1.1|>=2" + "symfony/console": "<7.4", + "symfony/error-handler": "<7.4" }, - "suggest": { - "symfony/service-implementation": "" + "require-dev": { + "symfony/console": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/process": "^7.4|^8.0", + "symfony/uid": "^7.4|^8.0", + "twig/twig": "^3.12" }, + "bin": [ + "Resources/bin/var-dump-server" + ], "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, "autoload": { + "files": [ + "Resources/functions/dump.php" + ], "psr-4": { - "Symfony\\Contracts\\Service\\": "" - } + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -6076,18 +7293,14 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Generic abstractions related to writing services", + "description": "Provides mechanisms for walking through any arbitrary PHP variable", "homepage": "https://symfony.com", "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" + "debug", + "dump" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v2.5.2" + "source": "https://github.com/symfony/var-dumper/tree/v8.0.8" }, "funding": [ { @@ -6098,35 +7311,43 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2022-05-30T19:17:29+00:00" + "time": "2026-03-31T07:15:36+00:00" }, { - "name": "symfony/stopwatch", - "version": "v5.4.21", + "name": "symfony/var-exporter", + "version": "v8.0.9", "source": { "type": "git", - "url": "https://github.com/symfony/stopwatch.git", - "reference": "f83692cd869a6f2391691d40a01e8acb89e76fee" + "url": "https://github.com/symfony/var-exporter.git", + "reference": "24cf67be4dd0926e4413635418682f4fff831412" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/f83692cd869a6f2391691d40a01e8acb89e76fee", - "reference": "f83692cd869a6f2391691d40a01e8acb89e76fee", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/24cf67be4dd0926e4413635418682f4fff831412", + "reference": "24cf67be4dd0926e4413635418682f4fff831412", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/service-contracts": "^1|^2|^3" + "php": ">=8.4" + }, + "require-dev": { + "symfony/property-access": "^7.4|^8.0", + "symfony/serializer": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0" }, "type": "library", "autoload": { "psr-4": { - "Symfony\\Component\\Stopwatch\\": "" + "Symfony\\Component\\VarExporter\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -6138,18 +7359,28 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Provides a way to profile code", + "description": "Allows exporting any serializable PHP data structure to plain PHP code", "homepage": "https://symfony.com", + "keywords": [ + "clone", + "construct", + "export", + "hydrate", + "instantiate", + "lazy-loading", + "proxy", + "serialize" + ], "support": { - "source": "https://github.com/symfony/stopwatch/tree/v5.4.21" + "source": "https://github.com/symfony/var-exporter/tree/v8.0.9" }, "funding": [ { @@ -6160,12 +7391,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2023-02-14T08:03:56+00:00" + "time": "2026-04-18T13:51:42+00:00" }, { "name": "symfony/yaml", @@ -6240,37 +7475,72 @@ }, { "name": "symplify/monorepo-builder", - "version": "10.3.3", + "version": "12.7.0", "source": { "type": "git", "url": "https://github.com/symplify/monorepo-builder.git", - "reference": "5f97613a2015b74aa86a7330b47a88c283877fd4" + "reference": "ab0f813088314f71a0ad68910afb6e8fadd227cb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symplify/monorepo-builder/zipball/5f97613a2015b74aa86a7330b47a88c283877fd4", - "reference": "5f97613a2015b74aa86a7330b47a88c283877fd4", + "url": "https://api.github.com/repos/symplify/monorepo-builder/zipball/ab0f813088314f71a0ad68910afb6e8fadd227cb", + "reference": "ab0f813088314f71a0ad68910afb6e8fadd227cb", "shasum": "" }, "require": { - "php": ">=7.2" + "nette/utils": "^4.0.5", + "phar-io/version": "^3.2", + "php": ">=8.2", + "sebastian/diff": "^6.0 || ^7.0 || ^8.0", + "symfony/config": "^7.0 || ^8.0", + "symfony/console": "^7.0 || ^8.0", + "symfony/dependency-injection": "^7.0 || ^8.0", + "symfony/filesystem": "^7.0 || ^8.0", + "symfony/finder": "^7.0 || ^8.0", + "symfony/http-kernel": "^7.0 || ^8.0", + "symfony/process": "^7.0 || ^8.0", + "webmozart/assert": "^1.11 || ^2.0" + }, + "require-dev": { + "php-parallel-lint/php-parallel-lint": "^1.3", + "phpstan/extension-installer": "1.4.3", + "phpstan/phpstan": "^2.1.22", + "phpunit/phpunit": "^11.0", + "rector/rector": "^2.1.3", + "symplify/easy-ci": "^11.3", + "symplify/easy-coding-standard": "^12.0", + "symplify/phpstan-extensions": "^12.0.1", + "symplify/phpstan-rules": "^14.6.12", + "tomasvotruba/class-leak": "^2.0.5", + "tomasvotruba/unused-public": "^2.0.1", + "tracy/tracy": "^2.9" }, "bin": [ - "bin/monorepo-builder" + "bin/monorepo-builder", + "src-deps/easy-testing/bin/easy-testing" ], "type": "library", - "extra": { - "branch-alias": { - "dev-main": "10.3-dev" + "autoload": { + "psr-4": { + "Symplify\\EasyTesting\\": "src-deps/easy-testing/src", + "Symplify\\PackageBuilder\\": "src-deps/package-builder/src", + "Symplify\\SymplifyKernel\\": "src-deps/symplify-kernel/src", + "Symplify\\MonorepoBuilder\\": [ + "src", + "packages" + ], + "Symplify\\SmartFileSystem\\": "src-deps/smart-file-system/src", + "Symplify\\AutowireArrayParameter\\": "src-deps/autowire-array-parameter/src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "Prefixed version of Not only Composer tools to build a Monorepo.", + "description": "Not only Composer tools to build a Monorepo.", "support": { - "source": "https://github.com/symplify/monorepo-builder/tree/10.3.3" + "issues": "https://github.com/symplify/monorepo-builder/issues", + "source": "https://github.com/symplify/monorepo-builder/tree/12.7.0" }, "funding": [ { @@ -6282,27 +7552,27 @@ "type": "github" } ], - "time": "2022-06-13T14:01:16+00:00" + "time": "2026-04-24T05:46:15+00:00" }, { "name": "theseer/tokenizer", - "version": "1.2.2", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96" + "reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b2ad5003ca10d4ee50a12da31de12a5774ba6b96", - "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/7989e43bf381af0eac72e4f0ca5bcbfa81658be4", + "reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4", "shasum": "" }, "require": { "ext-dom": "*", "ext-tokenizer": "*", "ext-xmlwriter": "*", - "php": "^7.2 || ^8.0" + "php": "^8.1" }, "type": "library", "autoload": { @@ -6324,7 +7594,7 @@ "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.2" + "source": "https://github.com/theseer/tokenizer/tree/2.0.1" }, "funding": [ { @@ -6332,34 +7602,42 @@ "type": "github" } ], - "time": "2023-11-20T00:12:19+00:00" + "time": "2025-12-08T11:19:18+00:00" }, { "name": "twig/twig", - "version": "v3.8.0", + "version": "v3.24.0", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d" + "reference": "a6769aefb305efef849dc25c9fd1653358c148f0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/9d15f0ac07f44dc4217883ec6ae02fd555c6f71d", - "reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/a6769aefb305efef849dc25c9fd1653358c148f0", + "reference": "a6769aefb305efef849dc25c9fd1653358c148f0", "shasum": "" }, "require": { - "php": ">=7.2.5", + "php": ">=8.1.0", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-mbstring": "^1.3", - "symfony/polyfill-php80": "^1.22" + "symfony/polyfill-mbstring": "^1.3" }, "require-dev": { + "php-cs-fixer/shim": "^3.0@stable", + "phpstan/phpstan": "^2.0@stable", "psr/container": "^1.0|^2.0", - "symfony/phpunit-bridge": "^5.4.9|^6.3|^7.0" + "symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0" }, "type": "library", "autoload": { + "files": [ + "src/Resources/core.php", + "src/Resources/debug.php", + "src/Resources/escaper.php", + "src/Resources/string_loader.php" + ], "psr-4": { "Twig\\": "src/" } @@ -6392,7 +7670,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.8.0" + "source": "https://github.com/twigphp/Twig/tree/v3.24.0" }, "funding": [ { @@ -6404,16 +7682,78 @@ "type": "tidelift" } ], - "time": "2023-11-21T18:54:41+00:00" + "time": "2026-03-17T21:31:11+00:00" + }, + { + "name": "webmozart/assert", + "version": "2.3.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "eb0d790f735ba6cff25c683a85a1da0eadeff9e4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/eb0d790f735ba6cff25c683a85a1da0eadeff9e4", + "reference": "eb0d790f735ba6cff25c683a85a1da0eadeff9e4", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-date": "*", + "ext-filter": "*", + "php": "^8.2" + }, + "suggest": { + "ext-intl": "", + "ext-simplexml": "", + "ext-spl": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-feature/2-0": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + }, + { + "name": "Woody Gilk", + "email": "woody.gilk@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/2.3.0" + }, + "time": "2026-04-11T10:33:05+00:00" } ], "aliases": [], "minimum-stability": "dev", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": "^7.4 || ^8.0", + "php": "^8.5", "ext-fileinfo": "*", "ext-json": "*", "ext-mbstring": "*", @@ -6421,6 +7761,6 @@ "ext-simplexml": "*", "ext-spl": "*" }, - "platform-dev": [], - "plugin-api-version": "2.6.0" + "platform-dev": {}, + "plugin-api-version": "2.9.0" } From 9a9d0a1bb9ee6bcb617dbceea6a682a60176def4 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Fri, 15 May 2026 14:11:31 -0400 Subject: [PATCH 05/94] chore(dependencies): downgrade PHP requirement to ^8.3 in composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index ec7521317..9e3e314ec 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,7 @@ } ], "require": { - "php": "^8.5", + "php": "^8.3", "ext-fileinfo": "*", "ext-json": "*", "ext-mbstring": "*", From b6d12642edc0d06202759fc6622e527392f31f97 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Fri, 15 May 2026 14:11:56 -0400 Subject: [PATCH 06/94] tests(phpcs): update script to use `./monorepo packages` and simplify package handling --- tests/script/phpcs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/tests/script/phpcs b/tests/script/phpcs index 3a3c53410..bca9cf35f 100755 --- a/tests/script/phpcs +++ b/tests/script/phpcs @@ -15,7 +15,7 @@ NC='\033[0m' # No Color ####### get_packages() { - vendor/bin/monorepo-builder packages-json + ./monorepo packages } ###### @@ -100,14 +100,7 @@ if [[ -z "$package" ]]; then # Testing all packages packages="$(get_packages)" echo "Testing all packages..." - # Cleaning get_packages output for array conversion - packages=${packages//,/ } - packages=${packages//[/} - packages=${packages//]/} - packages=${packages//\"/} - packages=($packages) - for i in "${packages[@]}"; do test_package $i done From dfd94ac978415c72d9054a75ba44954d4199227a Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Fri, 15 May 2026 15:20:02 -0400 Subject: [PATCH 07/94] feat(date-helper): replace `strftime` with `IntlDateFormatter` for improved localization Replaced the deprecated `strftime` with `IntlDateFormatter` for date formatting. Introduced locale support to the `DateHelper` class for better internationalization. --- .../Cms/Support/Helpers/DateHelper.php | 80 ++++++++++++++----- 1 file changed, 61 insertions(+), 19 deletions(-) diff --git a/packages/cms/src/Charcoal/Cms/Support/Helpers/DateHelper.php b/packages/cms/src/Charcoal/Cms/Support/Helpers/DateHelper.php index 35a0f6877..5428878d5 100644 --- a/packages/cms/src/Charcoal/Cms/Support/Helpers/DateHelper.php +++ b/packages/cms/src/Charcoal/Cms/Support/Helpers/DateHelper.php @@ -4,6 +4,7 @@ use DateTime; use Exception; +use IntlDateFormatter; // From 'charcoal-translator' use Charcoal\Translator\TranslatorAwareTrait; @@ -44,6 +45,11 @@ class DateHelper */ protected $timeFormat; + /** + * @var string $locale The current locale for date formatting + */ + protected $locale; + /** * DateHelper constructor. * @param array $data DateHelper data. @@ -64,6 +70,7 @@ public function __construct(array $data) $this->setTranslator($data['translator']); $this->dateFormats = $data['date_formats']; $this->timeFormats = $data['time_formats']; + $this->locale = $this->translator()->getLocale(); } /** @@ -202,16 +209,40 @@ private function formatDateFromCase($case) $formats['to'] = $this->crossPlatformFormat((string)$formats['to']); if (!$this->to || !$formats['to']) { + $formatter = new IntlDateFormatter( + $this->locale, + IntlDateFormatter::FULL, + IntlDateFormatter::FULL, + null, + null, + $formats['from'] + ); return sprintf( (string)$content, - strftime($formats['from'], $this->from->getTimestamp()) + $formatter->format($this->from) ); } + $formatterFrom = new IntlDateFormatter( + $this->locale, + IntlDateFormatter::FULL, + IntlDateFormatter::FULL, + null, + null, + $formats['from'] + ); + $formatterTo = new IntlDateFormatter( + $this->locale, + IntlDateFormatter::FULL, + IntlDateFormatter::FULL, + null, + null, + $formats['to'] + ); return sprintf( (string)$content, - strftime($formats['from'], $this->from->getTimestamp()), - strftime($formats['to'], $this->to->getTimestamp()) + $formatterFrom->format($this->from), + $formatterTo->format($this->to) ); } @@ -233,16 +264,40 @@ private function formatTimeFromCase($case) $formats['to'] = $this->translator()->translation($formats['to']); if (!$this->to || !$formats['to']) { + $formatter = new IntlDateFormatter( + $this->locale, + IntlDateFormatter::FULL, + IntlDateFormatter::FULL, + null, + null, + $formats['from'] + ); return sprintf( (string)$content, - strftime($formats['from'], $this->from->getTimestamp()) + $formatter->format($this->from) ); } + $formatterFrom = new IntlDateFormatter( + $this->locale, + IntlDateFormatter::FULL, + IntlDateFormatter::FULL, + null, + null, + $formats['from'] + ); + $formatterTo = new IntlDateFormatter( + $this->locale, + IntlDateFormatter::FULL, + IntlDateFormatter::FULL, + null, + null, + $formats['to'] + ); return sprintf( (string)$content, - strftime($formats['from'], $this->from->getTimestamp()), - strftime($formats['to'], $this->to->getTimestamp()) + $formatterFrom->format($this->from), + $formatterTo->format($this->to) ); } @@ -258,17 +313,4 @@ private function parseAsDate($date) return new DateTime($date); } - - /** - * @param mixed $format DateTime to be formatted. - * @return mixed - */ - private function crossPlatformFormat($format) - { - if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') { - $format = preg_replace('#(? Date: Tue, 19 May 2026 15:47:34 -0400 Subject: [PATCH 08/94] chore(ddev): add DDEV configuration for local development with PHP 8.5 and Xdebug --- .ddev/config.yaml | 16 ++++++++++++++++ .ddev/php/php-xdebug.ini | 2 ++ 2 files changed, 18 insertions(+) create mode 100644 .ddev/config.yaml create mode 100644 .ddev/php/php-xdebug.ini diff --git a/.ddev/config.yaml b/.ddev/config.yaml new file mode 100644 index 000000000..3d0e1970e --- /dev/null +++ b/.ddev/config.yaml @@ -0,0 +1,16 @@ +name: charcoal +type: php +docroot: "" +php_version: "8.5" +webserver_type: apache-fpm +xdebug_enabled: true +additional_hostnames: [] +additional_fqdns: [] +database: + type: mariadb + version: "10.11" +use_dns_when_possible: true +composer_version: "2" +web_environment: + - XDEBUG_MODE=coverage,debug,develop +corepack_enable: false diff --git a/.ddev/php/php-xdebug.ini b/.ddev/php/php-xdebug.ini new file mode 100644 index 000000000..c9a635b8c --- /dev/null +++ b/.ddev/php/php-xdebug.ini @@ -0,0 +1,2 @@ +[PHP] +xdebug.mode=coverage,debug,develop From b12447aa2b8e922e69696ff227f5e51105e4b98d Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Tue, 19 May 2026 15:48:27 -0400 Subject: [PATCH 09/94] tests(script): simplify `phpunit` script by using `./monorepo packages` and removing redundant parsing --- tests/script/phpunit | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/tests/script/phpunit b/tests/script/phpunit index 70f2f05c5..322d6f7b4 100755 --- a/tests/script/phpunit +++ b/tests/script/phpunit @@ -15,7 +15,7 @@ NC='\033[0m' # No Color ####### get_packages() { - vendor/bin/monorepo-builder packages-json + ./monorepo packages } ###### @@ -101,12 +101,6 @@ if [[ -z "$package" ]]; then # Testing all packages packages="$(get_packages)" echo "Testing all packages..." - # Cleaning get_packages output for array conversion - packages=${packages//,/ } - packages=${packages//[/} - packages=${packages//]/} - packages=${packages//\"/} - packages=($packages) for i in "${packages[@]}"; do From 1627f42d5dc6206291c8fe2adb4059d25db2af03 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Tue, 19 May 2026 15:48:40 -0400 Subject: [PATCH 10/94] tests(bootstrap): add `strict_types` declaration in test bootstrap file --- tests/bootstrap.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 8e2d2420b..83afb35da 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -1,12 +1,12 @@ Date: Tue, 19 May 2026 15:50:34 -0400 Subject: [PATCH 11/94] chore(dependencies): update composer.lock after dependencies upgrade --- composer.json | 8 +- composer.lock | 768 ++++++++++++++++++++++++-------------------------- 2 files changed, 367 insertions(+), 409 deletions(-) diff --git a/composer.json b/composer.json index 9e3e314ec..3a17ae6ec 100644 --- a/composer.json +++ b/composer.json @@ -71,12 +71,14 @@ "mockery/mockery": "^1.0", "mustache/mustache": "^2.11", "php-coveralls/php-coveralls": "^2.2", - "phpstan/phpstan": "^1.6", - "phpunit/phpunit": "^13.1", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^12.5", + "rector/rector": "^2.4", "squizlabs/php_codesniffer": "^3.5", "symfony/yaml": "^3.0", "symplify/monorepo-builder": "^12.7", - "twig/twig": "^3.4" + "twig/twig": "^3.4", + "yoast/phpunit-polyfills": "^4.0" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index c9cbe2955..13876cf73 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f1f21bd1a0b5f23ba077fbe1873c4e43", + "content-hash": "d08e2d03c511824149125bfc5c9644df", "packages": [ { "name": "assetic/framework", @@ -275,16 +275,16 @@ }, { "name": "guzzlehttp/guzzle", - "version": "7.10.0", + "version": "7.10.1", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4" + "reference": "b777df1776c667e287664dda75b0298ad8ae3a14" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", - "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b777df1776c667e287664dda75b0298ad8ae3a14", + "reference": "b777df1776c667e287664dda75b0298ad8ae3a14", "shasum": "" }, "require": { @@ -302,8 +302,9 @@ "bamarni/composer-bin-plugin": "^1.8.2", "ext-curl": "*", "guzzle/client-integration-tests": "3.0.2", + "guzzlehttp/test-server": "^0.3.2", "php-http/message-factory": "^1.1", - "phpunit/phpunit": "^8.5.39 || ^9.6.20", + "phpunit/phpunit": "^8.5.52 || ^9.6.34", "psr/log": "^1.1 || ^2.0 || ^3.0" }, "suggest": { @@ -381,7 +382,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.10.0" + "source": "https://github.com/guzzle/guzzle/tree/7.10.1" }, "funding": [ { @@ -397,20 +398,20 @@ "type": "tidelift" } ], - "time": "2025-08-23T22:36:01+00:00" + "time": "2026-05-19T18:01:31+00:00" }, { "name": "guzzlehttp/promises", - "version": "2.3.0", + "version": "2.3.1", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "481557b130ef3790cf82b713667b43030dc9c957" + "reference": "d2d8dfae4757f384d630fdffc2d8d6618d8f4c5e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/481557b130ef3790cf82b713667b43030dc9c957", - "reference": "481557b130ef3790cf82b713667b43030dc9c957", + "url": "https://api.github.com/repos/guzzle/promises/zipball/d2d8dfae4757f384d630fdffc2d8d6618d8f4c5e", + "reference": "d2d8dfae4757f384d630fdffc2d8d6618d8f4c5e", "shasum": "" }, "require": { @@ -418,7 +419,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.44 || ^9.6.25" + "phpunit/phpunit": "^8.5.52 || ^9.6.34" }, "type": "library", "extra": { @@ -464,7 +465,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.3.0" + "source": "https://github.com/guzzle/promises/tree/2.3.1" }, "funding": [ { @@ -480,20 +481,20 @@ "type": "tidelift" } ], - "time": "2025-08-22T14:34:08+00:00" + "time": "2026-05-19T18:30:48+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.9.0", + "version": "2.10.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "7d0ed42f28e42d61352a7a79de682e5e67fec884" + "reference": "d5ddaf5743c42a61cb6100f83dc9d5a2bafe75ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/7d0ed42f28e42d61352a7a79de682e5e67fec884", - "reference": "7d0ed42f28e42d61352a7a79de682e5e67fec884", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/d5ddaf5743c42a61cb6100f83dc9d5a2bafe75ca", + "reference": "d5ddaf5743c42a61cb6100f83dc9d5a2bafe75ca", "shasum": "" }, "require": { @@ -508,9 +509,9 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "http-interop/http-factory-tests": "0.9.0", + "http-interop/http-factory-tests": "1.1.0", "jshttp/mime-db": "1.54.0.1", - "phpunit/phpunit": "^8.5.44 || ^9.6.25" + "phpunit/phpunit": "^8.5.52 || ^9.6.34" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" @@ -581,7 +582,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.9.0" + "source": "https://github.com/guzzle/psr7/tree/2.10.0" }, "funding": [ { @@ -597,7 +598,7 @@ "type": "tidelift" } ], - "time": "2026-03-10T16:41:02+00:00" + "time": "2026-05-19T17:32:11+00:00" }, { "name": "intervention/image", @@ -2804,16 +2805,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.381.1", + "version": "3.381.4", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "405affc23ea14950c378d5ae79420302fcb467dc" + "reference": "846c38e4ebcbbc3ab30fe8dca343ce399b0b4ad4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/405affc23ea14950c378d5ae79420302fcb467dc", - "reference": "405affc23ea14950c378d5ae79420302fcb467dc", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/846c38e4ebcbbc3ab30fe8dca343ce399b0b4ad4", + "reference": "846c38e4ebcbbc3ab30fe8dca343ce399b0b4ad4", "shasum": "" }, "require": { @@ -2895,9 +2896,9 @@ "support": { "forum": "https://github.com/aws/aws-sdk-php/discussions", "issues": "https://github.com/aws/aws-sdk-php/issues", - "source": "https://github.com/aws/aws-sdk-php/tree/3.381.1" + "source": "https://github.com/aws/aws-sdk-php/tree/3.381.4" }, - "time": "2026-05-14T18:08:28+00:00" + "time": "2026-05-19T18:16:18+00:00" }, { "name": "cache/adapter-common", @@ -4204,15 +4205,15 @@ }, { "name": "phpstan/phpstan", - "version": "1.12.33", + "version": "2.1.55", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/37982d6fc7cbb746dda7773530cda557cdf119e1", - "reference": "37982d6fc7cbb746dda7773530cda557cdf119e1", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/9eaac3826ed5e9b8427350a43cac825eeca3f566", + "reference": "9eaac3826ed5e9b8427350a43cac825eeca3f566", "shasum": "" }, "require": { - "php": "^7.2|^8.0" + "php": "^7.4|^8.0" }, "conflict": { "phpstan/phpstan-shim": "*" @@ -4253,39 +4254,37 @@ "type": "github" } ], - "time": "2026-02-28T20:30:03+00:00" + "time": "2026-05-18T11:57:34+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "14.1.8", + "version": "12.5.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "031856c28c060e1c1d1fc94d256e3ffbe4230c91" + "reference": "876099a072646c7745f673d7aeab5382c4439691" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/031856c28c060e1c1d1fc94d256e3ffbe4230c91", - "reference": "031856c28c060e1c1d1fc94d256e3ffbe4230c91", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/876099a072646c7745f673d7aeab5382c4439691", + "reference": "876099a072646c7745f673d7aeab5382c4439691", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", - "ext-mbstring": "*", "ext-xmlwriter": "*", "nikic/php-parser": "^5.7.0", - "php": ">=8.4", - "phpunit/php-text-template": "^6.0", - "sebastian/complexity": "^6.0", - "sebastian/environment": "^9.2", - "sebastian/git-state": "^1.0", - "sebastian/lines-of-code": "^5.0", - "sebastian/version": "^7.0", + "php": ">=8.3", + "phpunit/php-text-template": "^5.0", + "sebastian/complexity": "^5.0", + "sebastian/environment": "^8.0.3", + "sebastian/lines-of-code": "^4.0", + "sebastian/version": "^6.0", "theseer/tokenizer": "^2.0.1" }, "require-dev": { - "phpunit/phpunit": "^13.1" + "phpunit/phpunit": "^12.5.1" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -4294,7 +4293,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "14.1.x-dev" + "dev-main": "12.5.x-dev" } }, "autoload": { @@ -4323,7 +4322,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/14.1.8" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.5.6" }, "funding": [ { @@ -4343,32 +4342,32 @@ "type": "tidelift" } ], - "time": "2026-05-09T12:06:52+00:00" + "time": "2026-04-15T08:23:17+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "7.0.0", + "version": "6.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "6e5aa1fb0a95b1703d83e721299ee18bb4e2de50" + "reference": "3d1cd096ef6bea4bf2762ba586e35dbd317cbfd5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/6e5aa1fb0a95b1703d83e721299ee18bb4e2de50", - "reference": "6e5aa1fb0a95b1703d83e721299ee18bb4e2de50", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3d1cd096ef6bea4bf2762ba586e35dbd317cbfd5", + "reference": "3d1cd096ef6bea4bf2762ba586e35dbd317cbfd5", "shasum": "" }, "require": { - "php": ">=8.4" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^13.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "7.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -4396,7 +4395,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/7.0.0" + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/6.0.1" }, "funding": [ { @@ -4416,28 +4415,28 @@ "type": "tidelift" } ], - "time": "2026-02-06T04:33:26+00:00" + "time": "2026-02-02T14:04:18+00:00" }, { "name": "phpunit/php-invoker", - "version": "7.0.0", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "42e5c5cae0c65df12d1b1a3ab52bf3f50f244d88" + "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/42e5c5cae0c65df12d1b1a3ab52bf3f50f244d88", - "reference": "42e5c5cae0c65df12d1b1a3ab52bf3f50f244d88", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/12b54e689b07a25a9b41e57736dfab6ec9ae5406", + "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406", "shasum": "" }, "require": { - "php": ">=8.4" + "php": ">=8.3" }, "require-dev": { "ext-pcntl": "*", - "phpunit/phpunit": "^13.0" + "phpunit/phpunit": "^12.0" }, "suggest": { "ext-pcntl": "*" @@ -4445,7 +4444,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "7.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -4472,52 +4471,40 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-invoker/issues", "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/7.0.0" + "source": "https://github.com/sebastianbergmann/php-invoker/tree/6.0.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpunit/php-invoker", - "type": "tidelift" } ], - "time": "2026-02-06T04:34:47+00:00" + "time": "2025-02-07T04:58:58+00:00" }, { "name": "phpunit/php-text-template", - "version": "6.0.0", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "a47af19f93f76aa3368303d752aa5272ca3299f4" + "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/a47af19f93f76aa3368303d752aa5272ca3299f4", - "reference": "a47af19f93f76aa3368303d752aa5272ca3299f4", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/e1367a453f0eda562eedb4f659e13aa900d66c53", + "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53", "shasum": "" }, "require": { - "php": ">=8.4" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^13.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "6.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -4544,52 +4531,40 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-text-template/issues", "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/6.0.0" + "source": "https://github.com/sebastianbergmann/php-text-template/tree/5.0.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpunit/php-text-template", - "type": "tidelift" } ], - "time": "2026-02-06T04:36:37+00:00" + "time": "2025-02-07T04:59:16+00:00" }, { "name": "phpunit/php-timer", - "version": "9.0.0", + "version": "8.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "a0e12065831f6ab0d83120dc61513eb8d9a966f6" + "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/a0e12065831f6ab0d83120dc61513eb8d9a966f6", - "reference": "a0e12065831f6ab0d83120dc61513eb8d9a966f6", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", + "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", "shasum": "" }, "require": { - "php": ">=8.4" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^13.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "9.0-dev" + "dev-main": "8.0-dev" } }, "autoload": { @@ -4616,40 +4591,28 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-timer/issues", "security": "https://github.com/sebastianbergmann/php-timer/security/policy", - "source": "https://github.com/sebastianbergmann/php-timer/tree/9.0.0" + "source": "https://github.com/sebastianbergmann/php-timer/tree/8.0.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpunit/php-timer", - "type": "tidelift" } ], - "time": "2026-02-06T04:37:53+00:00" + "time": "2025-02-07T04:59:38+00:00" }, { "name": "phpunit/phpunit", - "version": "13.1.10", + "version": "12.5.25", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "38959098d3c10660a189afaa35a94290c1de67bb" + "reference": "792c2980442dfce319226b88fa845b8b6de3b333" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/38959098d3c10660a189afaa35a94290c1de67bb", - "reference": "38959098d3c10660a189afaa35a94290c1de67bb", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/792c2980442dfce319226b88fa845b8b6de3b333", + "reference": "792c2980442dfce319226b88fa845b8b6de3b333", "shasum": "" }, "require": { @@ -4662,23 +4625,22 @@ "myclabs/deep-copy": "^1.13.4", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", - "php": ">=8.4.1", - "phpunit/php-code-coverage": "^14.1.8", - "phpunit/php-file-iterator": "^7.0.0", - "phpunit/php-invoker": "^7.0.0", - "phpunit/php-text-template": "^6.0.0", - "phpunit/php-timer": "^9.0.0", - "sebastian/cli-parser": "^5.0.0", - "sebastian/comparator": "^8.1.3", - "sebastian/diff": "^8.3.0", - "sebastian/environment": "^9.3.0", - "sebastian/exporter": "^8.0.2", - "sebastian/git-state": "^1.0", - "sebastian/global-state": "^9.0.0", - "sebastian/object-enumerator": "^8.0.0", - "sebastian/recursion-context": "^8.0.0", - "sebastian/type": "^7.0.0", - "sebastian/version": "^7.0.0", + "php": ">=8.3", + "phpunit/php-code-coverage": "^12.5.6", + "phpunit/php-file-iterator": "^6.0.1", + "phpunit/php-invoker": "^6.0.0", + "phpunit/php-text-template": "^5.0.0", + "phpunit/php-timer": "^8.0.0", + "sebastian/cli-parser": "^4.2.0", + "sebastian/comparator": "^7.1.6", + "sebastian/diff": "^7.0.0", + "sebastian/environment": "^8.1.0", + "sebastian/exporter": "^7.0.2", + "sebastian/global-state": "^8.0.2", + "sebastian/object-enumerator": "^7.0.0", + "sebastian/recursion-context": "^7.0.1", + "sebastian/type": "^6.0.3", + "sebastian/version": "^6.0.0", "staabm/side-effects-detector": "^1.0.5" }, "bin": [ @@ -4687,7 +4649,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "13.1-dev" + "dev-main": "12.5-dev" } }, "autoload": { @@ -4719,7 +4681,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/13.1.10" + "source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.25" }, "funding": [ { @@ -4727,7 +4689,7 @@ "type": "other" } ], - "time": "2026-05-15T08:03:56+00:00" + "time": "2026-05-13T03:56:57+00:00" }, { "name": "psr/event-dispatcher", @@ -4830,30 +4792,90 @@ }, "time": "2017-10-23T01:57:42+00:00" }, + { + "name": "rector/rector", + "version": "2.4.3", + "source": { + "type": "git", + "url": "https://github.com/rectorphp/rector.git", + "reference": "891824c6c59f02a56a5dd58ea8edc44e6c0ece29" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/rectorphp/rector/zipball/891824c6c59f02a56a5dd58ea8edc44e6c0ece29", + "reference": "891824c6c59f02a56a5dd58ea8edc44e6c0ece29", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0", + "phpstan/phpstan": "^2.1.48" + }, + "conflict": { + "rector/rector-doctrine": "*", + "rector/rector-downgrade-php": "*", + "rector/rector-phpunit": "*", + "rector/rector-symfony": "*" + }, + "suggest": { + "ext-dom": "To manipulate phpunit.xml via the custom-rule command" + }, + "bin": [ + "bin/rector" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Instant Upgrade and Automated Refactoring of any PHP code", + "homepage": "https://getrector.com/", + "keywords": [ + "automation", + "dev", + "migration", + "refactoring" + ], + "support": { + "issues": "https://github.com/rectorphp/rector/issues", + "source": "https://github.com/rectorphp/rector/tree/2.4.3" + }, + "funding": [ + { + "url": "https://github.com/tomasvotruba", + "type": "github" + } + ], + "time": "2026-05-12T11:17:24+00:00" + }, { "name": "sebastian/cli-parser", - "version": "5.0.0", + "version": "4.2.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "48a4654fa5e48c1c81214e9930048a572d4b23ca" + "reference": "7d05781b13f7dec9043a629a21d086ed74582a15" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/48a4654fa5e48c1c81214e9930048a572d4b23ca", - "reference": "48a4654fa5e48c1c81214e9930048a572d4b23ca", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/7d05781b13f7dec9043a629a21d086ed74582a15", + "reference": "7d05781b13f7dec9043a629a21d086ed74582a15", "shasum": "" }, "require": { - "php": ">=8.4" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^13.0" + "phpunit/phpunit": "^12.5.25" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "4.2-dev" } }, "autoload": { @@ -4877,7 +4899,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/cli-parser/issues", "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/5.0.0" + "source": "https://github.com/sebastianbergmann/cli-parser/tree/4.2.1" }, "funding": [ { @@ -4897,31 +4919,31 @@ "type": "tidelift" } ], - "time": "2026-02-06T04:39:44+00:00" + "time": "2026-05-17T05:29:34+00:00" }, { "name": "sebastian/comparator", - "version": "8.1.4", + "version": "7.1.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "1edd557042bf4ff9978ec125d8131b147d5c8224" + "reference": "c769009dee98f494e0edc3fd4f4087501688f11e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1edd557042bf4ff9978ec125d8131b147d5c8224", - "reference": "1edd557042bf4ff9978ec125d8131b147d5c8224", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/c769009dee98f494e0edc3fd4f4087501688f11e", + "reference": "c769009dee98f494e0edc3fd4f4087501688f11e", "shasum": "" }, "require": { "ext-dom": "*", "ext-mbstring": "*", - "php": ">=8.4", - "sebastian/diff": "^8.3", - "sebastian/exporter": "^8.0" + "php": ">=8.3", + "sebastian/diff": "^7.0", + "sebastian/exporter": "^7.0" }, "require-dev": { - "phpunit/phpunit": "^13.0" + "phpunit/phpunit": "^12.2" }, "suggest": { "ext-bcmath": "For comparing BcMath\\Number objects" @@ -4929,7 +4951,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "8.1-dev" + "dev-main": "7.1-dev" } }, "autoload": { @@ -4969,7 +4991,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/8.1.4" + "source": "https://github.com/sebastianbergmann/comparator/tree/7.1.6" }, "funding": [ { @@ -4989,33 +5011,33 @@ "type": "tidelift" } ], - "time": "2026-05-15T08:30:51+00:00" + "time": "2026-04-14T08:23:15+00:00" }, { "name": "sebastian/complexity", - "version": "6.0.0", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "c5651c795c98093480df79350cb050813fc7a2f3" + "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/c5651c795c98093480df79350cb050813fc7a2f3", - "reference": "c5651c795c98093480df79350cb050813fc7a2f3", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/bad4316aba5303d0221f43f8cee37eb58d384bbb", + "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb", "shasum": "" }, "require": { "nikic/php-parser": "^5.0", - "php": ">=8.4" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^13.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "6.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -5039,53 +5061,41 @@ "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", "security": "https://github.com/sebastianbergmann/complexity/security/policy", - "source": "https://github.com/sebastianbergmann/complexity/tree/6.0.0" + "source": "https://github.com/sebastianbergmann/complexity/tree/5.0.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/complexity", - "type": "tidelift" } ], - "time": "2026-02-06T04:41:32+00:00" + "time": "2025-02-07T04:55:25+00:00" }, { "name": "sebastian/diff", - "version": "8.3.0", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "b36d33b6e796513de7cb7df053afb3f55eefcd47" + "reference": "7ab1ea946c012266ca32390913653d844ecd085f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b36d33b6e796513de7cb7df053afb3f55eefcd47", - "reference": "b36d33b6e796513de7cb7df053afb3f55eefcd47", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7ab1ea946c012266ca32390913653d844ecd085f", + "reference": "7ab1ea946c012266ca32390913653d844ecd085f", "shasum": "" }, "require": { - "php": ">=8.4" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^13.0", + "phpunit/phpunit": "^12.0", "symfony/process": "^7.2" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "8.3-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -5118,47 +5128,35 @@ "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", "security": "https://github.com/sebastianbergmann/diff/security/policy", - "source": "https://github.com/sebastianbergmann/diff/tree/8.3.0" + "source": "https://github.com/sebastianbergmann/diff/tree/7.0.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/diff", - "type": "tidelift" } ], - "time": "2026-05-15T04:58:09+00:00" + "time": "2025-02-07T04:55:46+00:00" }, { "name": "sebastian/environment", - "version": "9.3.0", + "version": "8.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "6767059a30e4277ac95ee034809e793528464768" + "reference": "b121608b28a13f721e76ffbbd386d08eff58f3f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6767059a30e4277ac95ee034809e793528464768", - "reference": "6767059a30e4277ac95ee034809e793528464768", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/b121608b28a13f721e76ffbbd386d08eff58f3f6", + "reference": "b121608b28a13f721e76ffbbd386d08eff58f3f6", "shasum": "" }, "require": { - "php": ">=8.4" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^13.0" + "phpunit/phpunit": "^12.0" }, "suggest": { "ext-posix": "*" @@ -5166,7 +5164,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "9.3-dev" + "dev-main": "8.1-dev" } }, "autoload": { @@ -5194,7 +5192,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", "security": "https://github.com/sebastianbergmann/environment/security/policy", - "source": "https://github.com/sebastianbergmann/environment/tree/9.3.0" + "source": "https://github.com/sebastianbergmann/environment/tree/8.1.0" }, "funding": [ { @@ -5214,34 +5212,34 @@ "type": "tidelift" } ], - "time": "2026-04-15T12:14:03+00:00" + "time": "2026-04-15T12:13:01+00:00" }, { "name": "sebastian/exporter", - "version": "8.0.2", + "version": "7.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "9cee180ebe62259e3ed48df2212d1fc8cfd971bb" + "reference": "016951ae10980765e4e7aee491eb288c64e505b7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/9cee180ebe62259e3ed48df2212d1fc8cfd971bb", - "reference": "9cee180ebe62259e3ed48df2212d1fc8cfd971bb", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/016951ae10980765e4e7aee491eb288c64e505b7", + "reference": "016951ae10980765e4e7aee491eb288c64e505b7", "shasum": "" }, "require": { "ext-mbstring": "*", - "php": ">=8.4", - "sebastian/recursion-context": "^8.0" + "php": ">=8.3", + "sebastian/recursion-context": "^7.0" }, "require-dev": { - "phpunit/phpunit": "^13.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "8.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -5284,7 +5282,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", "security": "https://github.com/sebastianbergmann/exporter/security/policy", - "source": "https://github.com/sebastianbergmann/exporter/tree/8.0.2" + "source": "https://github.com/sebastianbergmann/exporter/tree/7.0.2" }, "funding": [ { @@ -5304,104 +5302,35 @@ "type": "tidelift" } ], - "time": "2026-04-15T12:38:05+00:00" - }, - { - "name": "sebastian/git-state", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/git-state.git", - "reference": "792a952e0eba55b6960a48aeceb9f371aad1f76b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/git-state/zipball/792a952e0eba55b6960a48aeceb9f371aad1f76b", - "reference": "792a952e0eba55b6960a48aeceb9f371aad1f76b", - "shasum": "" - }, - "require": { - "php": ">=8.4" - }, - "require-dev": { - "phpunit/phpunit": "^13.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for describing the state of a Git checkout", - "homepage": "https://github.com/sebastianbergmann/git-state", - "support": { - "issues": "https://github.com/sebastianbergmann/git-state/issues", - "security": "https://github.com/sebastianbergmann/git-state/security/policy", - "source": "https://github.com/sebastianbergmann/git-state/tree/1.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/git-state", - "type": "tidelift" - } - ], - "time": "2026-03-21T12:54:28+00:00" + "time": "2025-09-24T06:16:11+00:00" }, { "name": "sebastian/global-state", - "version": "9.0.0", + "version": "8.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "e52e3dc22441e6218c710afe72c3042f8fc41ea7" + "reference": "ef1377171613d09edd25b7816f05be8313f9115d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e52e3dc22441e6218c710afe72c3042f8fc41ea7", - "reference": "e52e3dc22441e6218c710afe72c3042f8fc41ea7", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/ef1377171613d09edd25b7816f05be8313f9115d", + "reference": "ef1377171613d09edd25b7816f05be8313f9115d", "shasum": "" }, "require": { - "php": ">=8.4", - "sebastian/object-reflector": "^6.0", - "sebastian/recursion-context": "^8.0" + "php": ">=8.3", + "sebastian/object-reflector": "^5.0", + "sebastian/recursion-context": "^7.0" }, "require-dev": { "ext-dom": "*", - "phpunit/phpunit": "^13.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "9.0-dev" + "dev-main": "8.0-dev" } }, "autoload": { @@ -5427,7 +5356,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", "security": "https://github.com/sebastianbergmann/global-state/security/policy", - "source": "https://github.com/sebastianbergmann/global-state/tree/9.0.0" + "source": "https://github.com/sebastianbergmann/global-state/tree/8.0.2" }, "funding": [ { @@ -5447,33 +5376,33 @@ "type": "tidelift" } ], - "time": "2026-02-06T04:45:13+00:00" + "time": "2025-08-29T11:29:25+00:00" }, { "name": "sebastian/lines-of-code", - "version": "5.0.0", + "version": "4.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "4f21bb7768e1c997722ccc7efb1d6b5c11bfd471" + "reference": "d543b8ef219dcd8da262cbb958639a96bedba10e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/4f21bb7768e1c997722ccc7efb1d6b5c11bfd471", - "reference": "4f21bb7768e1c997722ccc7efb1d6b5c11bfd471", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d543b8ef219dcd8da262cbb958639a96bedba10e", + "reference": "d543b8ef219dcd8da262cbb958639a96bedba10e", "shasum": "" }, "require": { - "nikic/php-parser": "^5.0", - "php": ">=8.4" + "nikic/php-parser": "^5.7.0", + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^13.0" + "phpunit/phpunit": "^12.5.25" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -5497,7 +5426,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/5.0.0" + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/4.0.1" }, "funding": [ { @@ -5517,34 +5446,34 @@ "type": "tidelift" } ], - "time": "2026-02-06T04:45:54+00:00" + "time": "2026-05-19T16:22:07+00:00" }, { "name": "sebastian/object-enumerator", - "version": "8.0.0", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "b39ab125fd9a7434b0ecbc4202eebce11a98cfc5" + "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/b39ab125fd9a7434b0ecbc4202eebce11a98cfc5", - "reference": "b39ab125fd9a7434b0ecbc4202eebce11a98cfc5", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1effe8e9b8e068e9ae228e542d5d11b5d16db894", + "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894", "shasum": "" }, "require": { - "php": ">=8.4", - "sebastian/object-reflector": "^6.0", - "sebastian/recursion-context": "^8.0" + "php": ">=8.3", + "sebastian/object-reflector": "^5.0", + "sebastian/recursion-context": "^7.0" }, "require-dev": { - "phpunit/phpunit": "^13.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "8.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -5567,52 +5496,40 @@ "support": { "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/8.0.0" + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/7.0.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/object-enumerator", - "type": "tidelift" } ], - "time": "2026-02-06T04:46:36+00:00" + "time": "2025-02-07T04:57:48+00:00" }, { "name": "sebastian/object-reflector", - "version": "6.0.0", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "3ca042c2c60b0eab094f8a1b6a7093f4d4c72200" + "reference": "4bfa827c969c98be1e527abd576533293c634f6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/3ca042c2c60b0eab094f8a1b6a7093f4d4c72200", - "reference": "3ca042c2c60b0eab094f8a1b6a7093f4d4c72200", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/4bfa827c969c98be1e527abd576533293c634f6a", + "reference": "4bfa827c969c98be1e527abd576533293c634f6a", "shasum": "" }, "require": { - "php": ">=8.4" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^13.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "6.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -5635,52 +5552,40 @@ "support": { "issues": "https://github.com/sebastianbergmann/object-reflector/issues", "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/6.0.0" + "source": "https://github.com/sebastianbergmann/object-reflector/tree/5.0.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/object-reflector", - "type": "tidelift" } ], - "time": "2026-02-06T04:47:13+00:00" + "time": "2025-02-07T04:58:17+00:00" }, { "name": "sebastian/recursion-context", - "version": "8.0.0", + "version": "7.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "74c5af21f6a5833e91767ca068c4d3dfec15317e" + "reference": "0b01998a7d5b1f122911a66bebcb8d46f0c82d8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/74c5af21f6a5833e91767ca068c4d3dfec15317e", - "reference": "74c5af21f6a5833e91767ca068c4d3dfec15317e", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/0b01998a7d5b1f122911a66bebcb8d46f0c82d8c", + "reference": "0b01998a7d5b1f122911a66bebcb8d46f0c82d8c", "shasum": "" }, "require": { - "php": ">=8.4" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^13.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "8.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -5711,7 +5616,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/8.0.0" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/7.0.1" }, "funding": [ { @@ -5731,32 +5636,32 @@ "type": "tidelift" } ], - "time": "2026-02-06T04:51:28+00:00" + "time": "2025-08-13T04:44:59+00:00" }, { "name": "sebastian/type", - "version": "7.0.0", + "version": "6.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "42412224607bd3931241bbd17f38e0f972f5a916" + "reference": "e549163b9760b8f71f191651d22acf32d56d6d4d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/42412224607bd3931241bbd17f38e0f972f5a916", - "reference": "42412224607bd3931241bbd17f38e0f972f5a916", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/e549163b9760b8f71f191651d22acf32d56d6d4d", + "reference": "e549163b9760b8f71f191651d22acf32d56d6d4d", "shasum": "" }, "require": { - "php": ">=8.4" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^13.0" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "7.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -5780,7 +5685,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/type/issues", "security": "https://github.com/sebastianbergmann/type/security/policy", - "source": "https://github.com/sebastianbergmann/type/tree/7.0.0" + "source": "https://github.com/sebastianbergmann/type/tree/6.0.3" }, "funding": [ { @@ -5800,29 +5705,29 @@ "type": "tidelift" } ], - "time": "2026-02-06T04:52:09+00:00" + "time": "2025-08-09T06:57:12+00:00" }, { "name": "sebastian/version", - "version": "7.0.0", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", - "reference": "ad37a5552c8e2b88572249fdc19b6da7792e021b" + "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/ad37a5552c8e2b88572249fdc19b6da7792e021b", - "reference": "ad37a5552c8e2b88572249fdc19b6da7792e021b", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/3e6ccf7657d4f0a59200564b08cead899313b53c", + "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c", "shasum": "" }, "require": { - "php": ">=8.4" + "php": ">=8.3" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "7.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -5846,27 +5751,15 @@ "support": { "issues": "https://github.com/sebastianbergmann/version/issues", "security": "https://github.com/sebastianbergmann/version/security/policy", - "source": "https://github.com/sebastianbergmann/version/tree/7.0.0" + "source": "https://github.com/sebastianbergmann/version/tree/6.0.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/version", - "type": "tidelift" } ], - "time": "2026-02-06T04:52:52+00:00" + "time": "2025-02-07T05:00:38+00:00" }, { "name": "squizlabs/php_codesniffer", @@ -7606,16 +7499,16 @@ }, { "name": "twig/twig", - "version": "v3.24.0", + "version": "v3.25.0", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "a6769aefb305efef849dc25c9fd1653358c148f0" + "reference": "0dade995be754556af4dcbf8721d45cb3271f9b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/a6769aefb305efef849dc25c9fd1653358c148f0", - "reference": "a6769aefb305efef849dc25c9fd1653358c148f0", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/0dade995be754556af4dcbf8721d45cb3271f9b4", + "reference": "0dade995be754556af4dcbf8721d45cb3271f9b4", "shasum": "" }, "require": { @@ -7670,7 +7563,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.24.0" + "source": "https://github.com/twigphp/Twig/tree/v3.25.0" }, "funding": [ { @@ -7682,7 +7575,7 @@ "type": "tidelift" } ], - "time": "2026-03-17T21:31:11+00:00" + "time": "2026-05-17T07:41:26+00:00" }, { "name": "webmozart/assert", @@ -7745,6 +7638,69 @@ "source": "https://github.com/webmozarts/assert/tree/2.3.0" }, "time": "2026-04-11T10:33:05+00:00" + }, + { + "name": "yoast/phpunit-polyfills", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/Yoast/PHPUnit-Polyfills.git", + "reference": "134921bfca9b02d8f374c48381451da1d98402f9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Yoast/PHPUnit-Polyfills/zipball/134921bfca9b02d8f374c48381451da1d98402f9", + "reference": "134921bfca9b02d8f374c48381451da1d98402f9", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "phpunit/phpunit": "^7.5 || ^8.0 || ^9.0 || ^11.0 || ^12.0" + }, + "require-dev": { + "php-parallel-lint/php-console-highlighter": "^1.0.0", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "yoast/yoastcs": "^3.1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.x-dev" + } + }, + "autoload": { + "files": [ + "phpunitpolyfills-autoload.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Team Yoast", + "email": "support@yoast.com", + "homepage": "https://yoast.com" + }, + { + "name": "Contributors", + "homepage": "https://github.com/Yoast/PHPUnit-Polyfills/graphs/contributors" + } + ], + "description": "Set of polyfills for changed PHPUnit functionality to allow for creating PHPUnit cross-version compatible tests", + "homepage": "https://github.com/Yoast/PHPUnit-Polyfills", + "keywords": [ + "phpunit", + "polyfill", + "testing" + ], + "support": { + "issues": "https://github.com/Yoast/PHPUnit-Polyfills/issues", + "security": "https://github.com/Yoast/PHPUnit-Polyfills/security/policy", + "source": "https://github.com/Yoast/PHPUnit-Polyfills" + }, + "time": "2025-02-09T18:58:54+00:00" } ], "aliases": [], @@ -7753,7 +7709,7 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": "^8.5", + "php": "^8.3", "ext-fileinfo": "*", "ext-json": "*", "ext-mbstring": "*", From 7adf89414236128ae215e35c5c0ec5db6c2e2968 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Tue, 19 May 2026 15:51:06 -0400 Subject: [PATCH 12/94] chore(dependencies): update `phpunit` to ^12.5 and fix namespace in `monorepo-builder` configuration --- monorepo-builder.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/monorepo-builder.php b/monorepo-builder.php index 86910891a..575f39d82 100644 --- a/monorepo-builder.php +++ b/monorepo-builder.php @@ -3,7 +3,7 @@ declare(strict_types=1); use Charcoal\MonorepoBuilder\Release\ReleaseWorker\UpdateBranchAliasReleaseWorker; -use Symplify\ComposerJsonManipulator\ValueObject\ComposerJsonSection; +use Symplify\MonorepoBuilder\ComposerJsonManipulator\ValueObject\ComposerJsonSection; use Symplify\MonorepoBuilder\Config\MBConfig; use Symplify\MonorepoBuilder\Release\ReleaseWorker\SetCurrentMutualDependenciesReleaseWorker; @@ -13,11 +13,10 @@ // default value __DIR__ . '/packages', ]); - // for "merge" command. $mbConfig->dataToAppend([ ComposerJsonSection::REQUIRE_DEV => [ - 'phpunit/phpunit' => '^9.5', + 'phpunit/phpunit' => '^12.5', ], ]); From a8ea26fe22833ff3bf6741a6a9d6f0eaba8b991e Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Tue, 19 May 2026 15:53:57 -0400 Subject: [PATCH 13/94] chore(tests): update PHPUnit configuration to match version 12.5 schema and cleanup deprecated settings --- phpunit.xml.dist | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 5fc4188f1..d6dea9138 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,29 +1,21 @@ - - - ./tests/Charcoal - - - - - - - - - - - + cacheDirectory=".phpunit.cache" + backupStaticProperties="false"> + + + ./tests/Charcoal + + + + + + + From dc3f349fc01b8b9306a488ef9cc8c8fad6874ad7 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Tue, 19 May 2026 15:54:54 -0400 Subject: [PATCH 14/94] chore(rector): add Rector configuration for PHP 8.5 and PHPUnit upgrades --- rector.php | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 rector.php diff --git a/rector.php b/rector.php new file mode 100644 index 000000000..ecd9a3f21 --- /dev/null +++ b/rector.php @@ -0,0 +1,32 @@ +withComposerBased(phpunit: true, symfony: true) + ->withPaths([ + __DIR__ . '/tests', + __DIR__ . '/packages/*/src', + __DIR__ . '/packages/*/tests', + ])->withSkip([ + __DIR__ . '/packages/*/tests/*/*/Fixture/*', + ]) + ->withPhpVersion(PhpVersion::PHP_85) + ->withSets([ + \Rector\Set\ValueObject\LevelSetList::UP_TO_PHP_85, + PHPUnitSetList::PHPUNIT_100, + PHPUnitSetList::PHPUNIT_110, + PHPUnitSetList::PHPUNIT_120 + ]) + ->withRules([ +// SerializableToSerializeRector::class +// \Rector\Php84\Rector\Param\ExplicitNullableParamTypeRector::class, +// \Rector\TypeDeclaration\Rector\ClassMethod\AddReturnTypeDeclarationBasedOnParentClassMethodRector::class, + ]) + ->withConfiguredRule(\Rector\Removing\Rector\Class_\RemoveInterfacesRector::class, [ + 'Serializable', + ]) + ->withPreparedSets(typeDeclarations: true, deadCode: true, codeQuality: true) + ->withPhpSets(php85: true); From 8f7a66bb7244958d65c3485d9ecad7014a979347 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Tue, 19 May 2026 16:03:54 -0400 Subject: [PATCH 15/94] chore(gitignore): update .gitignore to include PHPUnit and test-related cache and log files --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 119d860c5..30024c77a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ .phpunit.result.cache +phpunit.xml.dist.bak +.phpunit.cache charcoal_test node_modules/ packages/**/composer.lock @@ -7,3 +9,4 @@ phpstan.neon phpunit.xml psalm.xml vendor/ +tests/logs From 8c6dbcd71acb01ad9c3b10803b584a5c94328836 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Tue, 19 May 2026 16:08:40 -0400 Subject: [PATCH 16/94] chore(monorepo): enable strict typing, upgrade to PHP 8.3, and refactor for modern PHP syntax (rector utility) Applied strict typing declarations across packages. Updated composer dependencies to require PHP 8.3 and PHPUnit ^12.5. Refactored codebase with type hints, `static` return type, and improved null checks. --- packages/admin/composer.json | 48 ++- packages/admin/phpunit.xml.dist | 49 +-- .../Action/Account/LostPasswordAction.php | 38 +- .../Action/Account/ResetPasswordAction.php | 26 +- .../Admin/Action/ElfinderConnectorAction.php | 153 +++---- .../Admin/Action/Filesystem/LoadAction.php | 56 ++- .../src/Charcoal/Admin/Action/LoginAction.php | 11 +- .../Charcoal/Admin/Action/LogoutAction.php | 6 +- .../Action/Object/AbstractSaveAction.php | 9 +- .../Admin/Action/Object/DeleteAction.php | 10 +- .../Admin/Action/Object/ExportAction.php | 13 +- .../Admin/Action/Object/LoadAction.php | 64 +-- .../Admin/Action/Object/ReorderAction.php | 16 +- .../Action/Object/RevertRevisionAction.php | 7 +- .../Admin/Action/Object/SaveAction.php | 29 +- .../Admin/Action/Object/UpdateAction.php | 16 +- .../Admin/Action/Selectize/LoadAction.php | 45 +- .../Admin/Action/Selectize/SaveAction.php | 5 +- .../Selectize/SelectizeRendererAwareTrait.php | 9 +- .../Admin/Action/Selectize/UpdateAction.php | 5 +- .../Action/System/AbstractCacheAction.php | 4 +- .../Admin/Action/System/ClearCacheAction.php | 23 +- .../System/StaticWebsite/ActivateAction.php | 7 +- .../Action/System/StaticWebsite/AddAction.php | 15 +- .../System/StaticWebsite/DeactivateAction.php | 13 +- .../System/StaticWebsite/DeleteAction.php | 11 +- .../System/StaticWebsite/DeleteAllAction.php | 14 +- .../System/StaticWebsite/PreviewAction.php | 18 +- .../System/StaticWebsite/UpdateAction.php | 17 +- .../System/StaticWebsite/UpdateAllAction.php | 16 +- .../Action/Tinymce/UploadImageAction.php | 89 ++-- .../Admin/Action/Widget/LoadAction.php | 30 +- .../Action/Widget/Table/InlineAction.php | 18 +- .../Action/Widget/Table/InlineMultiAction.php | 25 +- .../admin/src/Charcoal/Admin/AdminAction.php | 19 +- .../admin/src/Charcoal/Admin/AdminModule.php | 29 +- .../admin/src/Charcoal/Admin/AdminScript.php | 15 +- .../src/Charcoal/Admin/AdminTemplate.php | 181 +++----- .../admin/src/Charcoal/Admin/AdminWidget.php | 130 ++---- .../admin/src/Charcoal/Admin/AssetsConfig.php | 5 +- packages/admin/src/Charcoal/Admin/Config.php | 33 +- .../Decorator/GridStackWidgetDecorator.php | 12 +- .../Docs/Template/Object/DocTemplate.php | 28 +- .../Docs/Widget/DocFormPropertyWidget.php | 53 +-- .../Docs/Widget/FormGroup/DocFormGroup.php | 19 +- .../Charcoal/Admin/Mustache/AssetsHelpers.php | 37 +- .../Charcoal/Admin/Object/Notification.php | 56 +-- .../Admin/Property/AbstractProperty.php | 38 +- .../Property/AbstractPropertyDisplay.php | 16 +- .../Admin/Property/AbstractPropertyInput.php | 41 +- .../Property/AbstractSelectableInput.php | 10 +- .../Admin/Property/AbstractTickableInput.php | 7 +- .../Admin/Property/Display/BooleanDisplay.php | 2 + .../Admin/Property/Display/ChoiceDisplay.php | 10 +- .../Property/Display/ColorSwatchDisplay.php | 2 + .../Property/Display/HierarchicalDisplay.php | 35 +- .../Admin/Property/Display/ImageDisplay.php | 6 +- .../Admin/Property/Display/LinkDisplay.php | 26 +- .../Admin/Property/Display/MessageDisplay.php | 12 +- .../Admin/Property/Display/ObjectDisplay.php | 2 + .../Admin/Property/Display/StatusDisplay.php | 17 +- .../Admin/Property/Display/TextDisplay.php | 2 + .../Property/HierarchicalObjectProperty.php | 11 +- .../Admin/Property/Input/AudioInput.php | 11 +- .../Admin/Property/Input/AudioWidgetInput.php | 179 +++----- .../Property/Input/CheckboxBtnGroupInput.php | 2 + .../Property/Input/CheckboxColorInput.php | 1 + .../Admin/Property/Input/CheckboxInput.php | 11 +- .../Admin/Property/Input/ColorPickerInput.php | 22 +- .../Property/Input/DateTimePickerInput.php | 27 +- .../Admin/Property/Input/DualSelectInput.php | 40 +- .../Admin/Property/Input/EmailInput.php | 7 +- .../Admin/Property/Input/FileInput.php | 109 ++--- .../Admin/Property/Input/HiddenInput.php | 7 +- .../Admin/Property/Input/ImageInput.php | 16 +- .../Admin/Property/Input/JsonEditorInput.php | 16 +- .../Admin/Property/Input/MapWidgetInput.php | 22 +- .../Property/Input/NestedWidgetInput.php | 37 +- .../Admin/Property/Input/NumberInput.php | 50 +-- .../Admin/Property/Input/PasswordInput.php | 7 +- .../Admin/Property/Input/PermalinkInput.php | 34 +- .../Admin/Property/Input/PhoneInput.php | 7 +- .../Property/Input/RadioBtnGroupInput.php | 2 + .../Admin/Property/Input/RadioColorInput.php | 1 + .../Admin/Property/Input/RadioInput.php | 11 +- .../Admin/Property/Input/RangeInput.php | 23 +- .../Admin/Property/Input/ReadonlyInput.php | 33 +- .../Property/Input/ReadonlySelectInput.php | 6 +- .../Admin/Property/Input/SelectInput.php | 20 +- .../Property/Input/Selectize/ListInput.php | 18 +- .../Property/Input/Selectize/TagsInput.php | 69 ++-- .../Selectize/Template/SpriteTemplate.php | 4 +- .../Admin/Property/Input/SelectizeInput.php | 213 ++++------ .../Property/Input/StructureWidgetInput.php | 8 +- .../Admin/Property/Input/SwitchInput.php | 18 +- .../Admin/Property/Input/TabulatorInput.php | 5 +- .../Admin/Property/Input/TextInput.php | 58 +-- .../Admin/Property/Input/TextareaInput.php | 48 +-- .../Property/Input/Tinymce/BasicInput.php | 8 +- .../Admin/Property/Input/TinymceInput.php | 48 +-- .../Admin/Property/Input/UrlInput.php | 7 +- .../Admin/Property/Input/VideoInput.php | 11 +- .../Property/PropertyDisplayInterface.php | 2 + .../Admin/Property/PropertyInputInterface.php | 2 + .../Admin/Property/PropertyInterface.php | 2 + .../Property/SelectableInputInterface.php | 2 + .../AbstractNotificationScript.php | 47 +-- .../Notification/ProcessDailyScript.php | 15 +- .../Notification/ProcessHourlyScript.php | 17 +- .../Notification/ProcessMinuteScript.php | 16 +- .../Notification/ProcessMonthlyScript.php | 15 +- .../Notification/ProcessWeeklyScript.php | 15 +- .../Admin/Script/Object/CreateScript.php | 13 +- .../Script/Object/ProcessSchedulesScript.php | 35 +- .../Object/Table/AlterPrimaryKeyScript.php | 169 +++----- .../Admin/Script/Object/Table/AlterScript.php | 15 +- .../Script/Object/Table/CreateScript.php | 15 +- .../Charcoal/Admin/Script/ObjectsScript.php | 15 +- .../Admin/Script/Tools/CheckLinksScript.php | 55 +-- .../Admin/Script/Tools/CopyAssetsScript.php | 19 +- .../Script/Tools/OptimizeImagesScript.php | 24 +- .../Admin/Script/Tools/ResizeImagesScript.php | 18 +- .../Tools/StaticWebsite/CrawlScript.php | 47 +-- .../Tools/StaticWebsite/UpdateScript.php | 26 +- .../Script/Translation/TranslateScript.php | 86 ++-- .../Script/User/AclRole/CreateScript.php | 8 +- .../Admin/Script/User/CreateScript.php | 37 +- .../Admin/Script/User/ResetPasswordScript.php | 19 +- .../Charcoal/Admin/Service/AssetsBuilder.php | 29 +- .../src/Charcoal/Admin/Service/Exporter.php | 82 ++-- .../Admin/Service/SelectizeRenderer.php | 11 +- .../ServiceProvider/AclServiceProvider.php | 9 +- .../ServiceProvider/AdminServiceProvider.php | 197 ++++----- .../AssetsManagerServiceProvider.php | 29 +- .../src/Charcoal/Admin/Support/AdminTrait.php | 16 +- .../Charcoal/Admin/Support/BaseUrlTrait.php | 29 +- .../Charcoal/Admin/Support/HttpAwareTrait.php | 16 +- .../Charcoal/Admin/Support/SecurityTrait.php | 4 +- .../src/Charcoal/Admin/Support/Sorter.php | 11 +- .../Template/Account/LostPasswordTemplate.php | 20 +- .../Account/ResetPasswordTemplate.php | 31 +- .../Admin/Template/AuthTemplateTrait.php | 2 +- .../Admin/Template/ElfinderTemplate.php | 116 ++---- .../Admin/Template/HandlerTemplate.php | 12 +- .../Charcoal/Admin/Template/HelpTemplate.php | 8 +- .../Charcoal/Admin/Template/HomeTemplate.php | 2 + .../Charcoal/Admin/Template/LoginTemplate.php | 24 +- .../Admin/Template/LogoutTemplate.php | 15 +- .../Template/Object/CollectionTemplate.php | 39 +- .../Admin/Template/Object/CreateTemplate.php | 27 +- .../Admin/Template/Object/EditTemplate.php | 27 +- .../Template/System/ClearCacheTemplate.php | 142 ++----- .../Template/System/Object/InfoTemplate.php | 67 +-- .../Template/System/StaticWebsiteTemplate.php | 19 +- .../System/UserPermissionsTemplate.php | 21 +- .../Template/System/UserRolesTemplate.php | 12 +- .../Admin/Template/System/UsersTemplate.php | 12 +- .../Admin/Ui/ActionContainerTrait.php | 112 ++--- .../Admin/Ui/CollectionContainerInterface.php | 2 + .../Admin/Ui/CollectionContainerTrait.php | 99 ++--- .../Admin/Ui/DashboardContainerInterface.php | 2 + .../Admin/Ui/DashboardContainerTrait.php | 8 +- .../Admin/Ui/FeedbackContainerTrait.php | 70 +--- .../Charcoal/Admin/Ui/FormGroupInterface.php | 2 + .../Admin/Ui/FormSidebarInterface.php | 2 + .../Admin/Ui/HasLanguageSwitcherInterface.php | 2 + .../Admin/Ui/HasLanguageSwitcherTrait.php | 11 +- .../Admin/Ui/ImageAttributesTrait.php | 12 +- .../Ui/LanguageSwitcherAwareInterface.php | 4 +- .../Ui/NestedWidgetContainerInterface.php | 2 + .../Admin/Ui/NestedWidgetContainerTrait.php | 28 +- .../Admin/Ui/ObjectContainerInterface.php | 2 + .../Admin/Ui/ObjectContainerTrait.php | 52 +-- .../Admin/Ui/ObjectRevisionsInterface.php | 2 + .../Admin/Ui/ObjectRevisionsTrait.php | 2 +- .../GenericSecondaryMenuGroup.php | 2 + .../SecondaryMenuGroupInterface.php | 2 + .../SecondaryMenu/SecondaryMenuGroupTrait.php | 47 +-- .../Admin/Ui/StructureContainerInterface.php | 2 + .../Admin/Ui/StructureContainerTrait.php | 12 +- packages/admin/src/Charcoal/Admin/User.php | 7 +- .../admin/src/Charcoal/Admin/User/AclRole.php | 2 + .../src/Charcoal/Admin/User/AuthToken.php | 2 + .../Charcoal/Admin/User/AuthTokenMetadata.php | 12 +- .../Charcoal/Admin/User/LostPasswordToken.php | 33 +- .../src/Charcoal/Admin/User/Permission.php | 2 + .../Admin/User/PermissionCategory.php | 2 + .../Admin/Widget/CardCollectionWidget.php | 32 +- .../Admin/Widget/CollectionMapWidget.php | 49 +-- .../Charcoal/Admin/Widget/DashboardWidget.php | 1 + .../Admin/Widget/Dialog/ImportlistWidget.php | 2 + .../src/Charcoal/Admin/Widget/DocWidget.php | 112 +++-- .../Admin/Widget/FormGroup/AclPermissions.php | 51 +-- .../Widget/FormGroup/GenericFormGroup.php | 2 + .../FormGroup/NestedWidgetFormGroup.php | 38 +- .../FormGroup/ObjectRevisionsFormGroup.php | 10 +- .../Widget/FormGroup/StructureFormGroup.php | 106 ++--- .../FormGroup/TemplateOptionsFormGroup.php | 46 +-- .../Charcoal/Admin/Widget/FormGroupWidget.php | 67 ++- .../Admin/Widget/FormPropertyWidget.php | 233 ++++------- .../Admin/Widget/FormSidebarWidget.php | 151 +++---- .../src/Charcoal/Admin/Widget/FormWidget.php | 137 ++----- .../Widget/Graph/AbstractGraphWidget.php | 17 +- .../Widget/Graph/AbstractTimeGraphWidget.php | 28 +- .../Widget/Graph/GraphWidgetInterface.php | 2 + .../Widget/Graph/TimeGraphWidgetInterface.php | 2 + .../src/Charcoal/Admin/Widget/GraphWidget.php | 28 +- .../Admin/Widget/GridStackDashboardWidget.php | 41 +- .../Admin/Widget/HierarchicalTableWidget.php | 7 +- .../Charcoal/Admin/Widget/LayoutWidget.php | 2 + .../src/Charcoal/Admin/Widget/MapWidget.php | 59 +-- .../Admin/Widget/ObjectFormWidget.php | 127 +++--- .../Admin/Widget/ObjectRevisionsWidget.php | 27 +- .../Admin/Widget/PaginationWidget.php | 75 +--- .../Charcoal/Admin/Widget/QuickFormWidget.php | 40 +- .../Charcoal/Admin/Widget/SearchWidget.php | 15 +- .../Admin/Widget/SecondaryMenuWidget.php | 171 +++----- .../Widget/SecondaryMenuWidgetInterface.php | 2 + .../Charcoal/Admin/Widget/TableGridWidget.php | 2 + .../Admin/Widget/TablePropertyWidget.php | 2 + .../src/Charcoal/Admin/Widget/TableWidget.php | 300 +++++--------- .../src/Charcoal/Admin/Widget/TextWidget.php | 62 +-- .../admin/tests/Charcoal/AbstractTestCase.php | 2 + .../Action/Account/LostPasswordActionTest.php | 36 +- .../Account/ResetPasswordActionTest.php | 47 +-- .../Charcoal/Admin/Action/LoginActionTest.php | 38 +- .../Admin/Action/LogoutActionTest.php | 31 +- .../Admin/Action/Object/DeleteActionTest.php | 43 +- .../Admin/Action/Object/ExportActionTest.php | 26 +- .../Admin/Action/Object/LoadActionTest.php | 31 +- .../Admin/Action/Object/ReorderActionTest.php | 56 +-- .../Admin/Action/Object/SaveActionTest.php | 26 +- .../Admin/Action/Object/UpdateActionTest.php | 26 +- .../Action/System/ClearCacheActionTest.php | 26 +- .../StaticWebsite/ActivateActionTest.php | 26 +- .../System/StaticWebsite/AddActionTest.php | 26 +- .../StaticWebsite/DeactivateActionTest.php | 26 +- .../System/StaticWebsite/DeleteActionTest.php | 26 +- .../StaticWebsite/DeleteAllActionTest.php | 26 +- .../StaticWebsite/PreviewActionTest.php | 26 +- .../System/StaticWebsite/UpdateActionTest.php | 26 +- .../StaticWebsite/UpdateAllActionTest.php | 26 +- .../tests/Charcoal/Admin/AdminActionTest.php | 35 +- .../Charcoal/Admin/AdminTemplateTest.php | 33 +- .../tests/Charcoal/Admin/AdminWidgetTest.php | 32 +- .../admin/tests/Charcoal/Admin/ConfigTest.php | 15 +- .../Charcoal/Admin/ContainerProvider.php | 317 ++++++-------- .../tests/Charcoal/Admin/Mock/AuthToken.php | 16 +- .../Charcoal/Admin/Mock/SortableModel.php | 4 +- .../Charcoal/Admin/Mock/UserProviderTrait.php | 4 +- .../Admin/Property/AbstractInputTest.php | 18 +- .../Admin/Property/Input/TextInputTest.php | 42 +- .../Property/Input/TextareaInputTest.php | 32 +- .../Notification/ProcessDailyScriptTest.php | 29 +- .../Notification/ProcessHourlyScriptTest.php | 29 +- .../Notification/ProcessMinuteScriptTest.php | 29 +- .../Notification/ProcessMonthlyScriptTest.php | 29 +- .../Notification/ProcessWeeklyScriptTest.php | 29 +- .../Script/Object/Table/CreateScriptTest.php | 21 +- .../Admin/Script/User/CreateScriptTest.php | 40 +- .../Script/User/ResetPasswordScriptTest.php | 25 +- .../Charcoal/Admin/Service/ExporterTest.php | 36 +- .../AclServiceProviderTest.php | 5 +- .../Account/LostPasswordTemplateTest.php | 10 +- .../Account/ResetPasswordTemplateTest.php | 10 +- .../Admin/Template/ElfinderTemplateTest.php | 23 +- .../Admin/Template/LoginTemplateTest.php | 11 +- .../Admin/Template/LogoutTemplateTest.php | 8 +- .../Object/CollectionTemplateTest.php | 31 +- .../Template/Object/CreateTemplateTest.php | 26 +- .../Template/Object/EditTemplateTest.php | 26 +- .../Admin/Widget/CollectionMapWidgetTest.php | 23 +- .../Admin/Widget/FormGroupWidgetTest.php | 11 +- .../Admin/Widget/FormPropertyWidgetTest.php | 19 +- .../Admin/Widget/FormSidebarWidgetTest.php | 9 +- .../Charcoal/Admin/Widget/FormWidgetTest.php | 22 +- .../Charcoal/Admin/Widget/GraphWidgetTest.php | 21 +- .../Admin/Widget/LayoutWidgetTest.php | 94 +---- .../Admin/Widget/ObjectFormWidgetTest.php | 8 +- .../Admin/Widget/PaginationWidgetTest.php | 13 +- .../Admin/Widget/SearchWidgetTest.php | 9 +- .../Admin/Widget/SecondaryMenuWidgetTest.php | 8 +- .../Charcoal/Admin/Widget/TableWidgetTest.php | 38 +- .../Charcoal/Admin/Widget/TextWidgetTest.php | 44 +- .../admin/tests/Charcoal/AssertionsTrait.php | 17 +- .../admin/tests/Charcoal/ReflectionsTrait.php | 19 +- packages/app/composer.json | 39 +- .../Charcoal/App/Action/AbstractAction.php | 34 +- .../Charcoal/App/Action/ActionInterface.php | 2 + packages/app/src/Charcoal/App/App.php | 44 +- .../src/Charcoal/App/AppAwareInterface.php | 2 + packages/app/src/Charcoal/App/AppConfig.php | 228 ++++------- .../app/src/Charcoal/App/AppContainer.php | 26 +- .../App/CallableResolverAwareTrait.php | 2 +- .../Charcoal/App/Config/DatabaseConfig.php | 70 ++-- .../Charcoal/App/Config/FilesystemConfig.php | 13 +- .../src/Charcoal/App/Config/LoggerConfig.php | 56 +-- .../app/src/Charcoal/App/DebugAwareTrait.php | 2 +- .../Charcoal/App/Handler/AbstractError.php | 22 +- .../Charcoal/App/Handler/AbstractHandler.php | 12 +- .../app/src/Charcoal/App/Handler/Error.php | 46 +-- .../Charcoal/App/Handler/HandlerConfig.php | 65 +-- .../Charcoal/App/Handler/HandlerInterface.php | 2 + .../src/Charcoal/App/Handler/Maintenance.php | 51 +-- .../src/Charcoal/App/Handler/NotAllowed.php | 59 +-- .../app/src/Charcoal/App/Handler/NotFound.php | 51 +-- .../app/src/Charcoal/App/Handler/PhpError.php | 46 +-- .../Charcoal/App/Helper/CallbackStream.php | 10 +- .../Charcoal/App/Middleware/IpMiddleware.php | 24 +- .../Charcoal/App/Module/AbstractModule.php | 4 +- .../Charcoal/App/Module/ModuleInterface.php | 2 + .../src/Charcoal/App/Route/ActionRoute.php | 3 +- .../Charcoal/App/Route/ActionRouteConfig.php | 13 +- .../src/Charcoal/App/Route/RouteConfig.php | 70 ++-- .../src/Charcoal/App/Route/RouteInterface.php | 2 + .../src/Charcoal/App/Route/RouteManager.php | 48 +-- .../src/Charcoal/App/Route/ScriptRoute.php | 3 +- .../Charcoal/App/Route/ScriptRouteConfig.php | 2 + .../src/Charcoal/App/Route/TemplateRoute.php | 3 +- .../App/Route/TemplateRouteConfig.php | 57 +-- .../Charcoal/App/Script/AbstractScript.php | 91 ++--- .../Charcoal/App/Script/ArgScriptTrait.php | 2 +- .../App/Script/CronScriptInterface.php | 2 + .../Charcoal/App/Script/CronScriptTrait.php | 15 +- .../Charcoal/App/Script/PathScriptTrait.php | 12 +- .../Charcoal/App/Script/ScriptInterface.php | 2 + .../ServiceProvider/AppServiceProvider.php | 206 ++++------ .../DatabaseServiceProvider.php | 23 +- .../FilesystemServiceProvider.php | 93 ++--- .../ServiceProvider/LoggerServiceProvider.php | 17 +- .../ServiceProvider/ScriptServiceProvider.php | 48 +-- .../App/Template/AbstractTemplate.php | 4 +- .../Charcoal/App/Template/AbstractWidget.php | 7 +- .../Charcoal/App/Template/GenericTemplate.php | 2 + .../App/Template/TemplateInterface.php | 2 + .../Charcoal/App/Template/WidgetBuilder.php | 15 +- .../Charcoal/App/Template/WidgetInterface.php | 2 + .../app/tests/Charcoal/AbstractTestCase.php | 2 + .../App/Action/AbstractActionTest.php | 50 +-- .../app/tests/Charcoal/App/AppConfigTest.php | 4 +- packages/app/tests/Charcoal/App/AppTest.php | 19 +- .../App/Config/DatabaseConfigTest.php | 14 +- .../App/Config/FilesystemConfigTest.php | 4 +- .../Charcoal/App/Config/LoggerConfigTest.php | 10 +- .../tests/Charcoal/App/ContainerProvider.php | 295 ++++++------- .../App/Route/ActionRouteConfigTest.php | 2 +- .../Charcoal/App/Route/ActionRouteTest.php | 16 +- .../Charcoal/App/Route/RouteConfigTest.php | 20 +- .../Charcoal/App/Route/RouteManagerTest.php | 13 +- .../Charcoal/App/Route/ScriptRouteTest.php | 26 +- .../App/Route/TemplateRouteConfigTest.php | 12 +- .../App/Script/AbstractScriptTest.php | 26 +- .../tests/Charcoal/App/ServerTestTrait.php | 29 +- .../AppServiceProviderTest.php | 2 +- .../DatabaseServiceProviderTest.php | 2 +- .../FilesystemServiceProviderTest.php | 28 +- .../LoggerServiceProviderTest.php | 2 +- .../ScriptServiceProviderTest.php | 2 +- .../App/Template/AbstractTemplateTest.php | 18 +- .../App/Template/AbstractWidgetTest.php | 16 +- .../app/tests/Charcoal/AssertionsTrait.php | 8 +- packages/app/tests/bootstrap.php | 2 + packages/attachment/composer.json | 26 +- .../Charcoal/Admin/Action/AddJoinAction.php | 7 +- .../src/Charcoal/Admin/Action/JoinAction.php | 7 +- .../Admin/Action/RemoveJoinAction.php | 7 +- .../Admin/Widget/AddAttachmentWidget.php | 2 + .../Admin/Widget/AttachmentWidget.php | 139 +++---- .../Widget/FormGroup/AttachmentFormGroup.php | 44 +- .../Charcoal/Attachment/AttachmentsConfig.php | 31 +- .../Interfaces/AttachableInterface.php | 2 + .../Interfaces/AttachmentAwareInterface.php | 2 + .../AttachmentContainerInterface.php | 2 + .../AttachmentRelationInterface.php | 2 + .../Attachment/Interfaces/JoinInterface.php | 2 + .../Charcoal/Attachment/Object/Accordion.php | 2 + .../Charcoal/Attachment/Object/Attachment.php | 190 +++------ .../Attachment/Object/Category/Generic.php | 9 +- .../Charcoal/Attachment/Object/Container.php | 15 +- .../src/Charcoal/Attachment/Object/Embed.php | 2 + .../src/Charcoal/Attachment/Object/File.php | 14 +- .../Charcoal/Attachment/Object/Gallery.php | 10 +- .../src/Charcoal/Attachment/Object/Image.php | 5 +- .../src/Charcoal/Attachment/Object/Join.php | 51 +-- .../src/Charcoal/Attachment/Object/Link.php | 2 + .../src/Charcoal/Attachment/Object/Text.php | 2 + .../src/Charcoal/Attachment/Object/Video.php | 2 + .../Attachment/Script/CleanupScript.php | 57 +-- .../Attachment/Script/MigrateScript.php | 17 +- .../Traits/AttachmentAwareTrait.php | 71 ++-- .../Traits/AttachmentContainerTrait.php | 24 +- .../Traits/ConfigurableAttachmentsTrait.php | 2 +- .../Charcoal/ContainerIntegrationTrait.php | 3 +- .../tests/Charcoal/ContainerProvider.php | 126 +++--- packages/cache/composer.json | 27 +- .../cache/src/Charcoal/Cache/CacheBuilder.php | 57 +-- .../cache/src/Charcoal/Cache/CacheConfig.php | 53 +-- .../Charcoal/Cache/CachePoolAwareTrait.php | 2 +- .../Charcoal/Cache/Facade/CachePoolFacade.php | 5 +- .../Cache/Middleware/CacheMiddleware.php | 60 +-- .../ServiceProvider/CacheServiceProvider.php | 56 +-- .../cache/tests/Charcoal/AbstractTestCase.php | 2 + .../tests/Charcoal/Cache/CacheConfigTest.php | 86 ++-- .../Charcoal/Cache/CachePoolAwareTest.php | 14 +- .../tests/Charcoal/Cache/CachePoolTrait.php | 4 +- .../Cache/Facade/CachePoolFacadeTest.php | 68 +-- .../Factory/AbstractCacheBuilderTest.php | 8 +- .../Cache/Factory/CacheBuilderClassTest.php | 89 ++-- .../Cache/Factory/CacheBuilderDriverTest.php | 75 +--- .../Cache/Factory/CacheBuilderPoolTest.php | 46 +-- .../AbstractCacheMiddlewareTest.php | 23 +- .../Middleware/CacheMiddlewareRequestTest.php | 33 +- .../CacheMiddlewareResponseTest.php | 18 +- .../CacheServiceProviderTest.php | 50 +-- .../tests/Charcoal/Mocks/CachePoolAware.php | 2 + .../tests/Charcoal/Mocks/DefaultAwarePool.php | 4 +- .../Mocks/DefaultsAwareCacheMiddlewares.php | 2 + .../tests/Charcoal/Mocks/LoggerAwarePool.php | 3 - packages/cms/composer.json | 31 +- .../Cms/HierarchicalSectionTableWidget.php | 8 +- .../Admin/Widget/Cms/SectionTableTrait.php | 46 +-- .../Admin/Widget/Cms/SectionTableWidget.php | 1 + .../FormGroup/GroupAttachmentFormGroup.php | 3 + .../Widget/FormGroup/MultiGroupFormGroup.php | 32 +- .../Admin/Widget/GroupAttachmentWidget.php | 44 +- .../Admin/Widget/MultiGroupWidget.php | 54 +-- .../cms/src/Charcoal/Cms/AbstractEvent.php | 51 +-- packages/cms/src/Charcoal/Cms/AbstractFaq.php | 2 + .../cms/src/Charcoal/Cms/AbstractNews.php | 27 +- .../cms/src/Charcoal/Cms/AbstractSection.php | 33 +- packages/cms/src/Charcoal/Cms/AbstractTag.php | 2 +- .../src/Charcoal/Cms/AbstractWebTemplate.php | 33 +- packages/cms/src/Charcoal/Cms/Config.php | 23 +- .../cms/src/Charcoal/Cms/Config/CmsConfig.php | 35 +- .../src/Charcoal/Cms/Config/EventConfig.php | 25 +- .../src/Charcoal/Cms/Config/NewsConfig.php | 36 +- .../src/Charcoal/Cms/Config/SectionConfig.php | 11 +- .../cms/src/Charcoal/Cms/ConfigInterface.php | 2 + packages/cms/src/Charcoal/Cms/Event.php | 5 +- .../cms/src/Charcoal/Cms/EventCategory.php | 17 +- .../cms/src/Charcoal/Cms/EventInterface.php | 2 + packages/cms/src/Charcoal/Cms/Faq.php | 6 +- packages/cms/src/Charcoal/Cms/FaqCategory.php | 8 +- .../cms/src/Charcoal/Cms/FaqInterface.php | 2 + .../cms/src/Charcoal/Cms/MetatagInterface.php | 2 + .../cms/src/Charcoal/Cms/MetatagTrait.php | 2 +- .../Cms/Mixin/HasContentBlocksInterface.php | 2 + .../Mixin/Traits/HasContentBlocksTrait.php | 18 +- packages/cms/src/Charcoal/Cms/News.php | 6 +- .../cms/src/Charcoal/Cms/NewsCategory.php | 19 +- .../cms/src/Charcoal/Cms/NewsInterface.php | 2 + .../cms/src/Charcoal/Cms/Route/EventRoute.php | 32 +- .../src/Charcoal/Cms/Route/GenericRoute.php | 90 ++-- .../cms/src/Charcoal/Cms/Route/NewsRoute.php | 32 +- .../src/Charcoal/Cms/Route/SectionRoute.php | 32 +- .../src/Charcoal/Cms/SearchableInterface.php | 2 + packages/cms/src/Charcoal/Cms/Section.php | 2 + .../Charcoal/Cms/Section/BlocksSection.php | 9 +- .../Charcoal/Cms/Section/ContentSection.php | 8 +- .../src/Charcoal/Cms/Section/EmptySection.php | 2 + .../Charcoal/Cms/Section/ExternalSection.php | 7 +- .../cms/src/Charcoal/Cms/SectionInterface.php | 2 + .../Cms/Service/Loader/AbstractLoader.php | 18 +- .../Cms/Service/Loader/EventLoader.php | 6 +- .../Cms/Service/Loader/NewsLoader.php | 15 +- .../Cms/Service/Loader/SectionLoader.php | 23 +- .../Cms/Service/Manager/AbstractManager.php | 21 +- .../Cms/Service/Manager/EventManager.php | 166 +++----- .../Cms/Service/Manager/NewsManager.php | 95 ++--- .../ServiceProvider/CmsServiceProvider.php | 98 ++--- .../Cms/Support/ContextualTemplateTrait.php | 6 +- .../Charcoal/Cms/Support/DocumentTrait.php | 19 +- .../Cms/Support/Helpers/DateHelper.php | 30 +- .../Interfaces/EventManagerAwareInterface.php | 2 + .../Interfaces/NewsManagerAwareInterface.php | 2 + .../SectionLoaderAwareInterface.php | 2 + .../Charcoal/Cms/Support/LocaleAwareTrait.php | 17 +- .../Support/Traits/DateHelperAwareTrait.php | 2 +- .../Support/Traits/EventManagerAwareTrait.php | 16 +- .../Support/Traits/NewsManagerAwareTrait.php | 16 +- .../Traits/SectionLoaderAwareTrait.php | 10 +- .../Traits/SocialNetworksAwareTrait.php | 6 +- packages/cms/src/Charcoal/Cms/Tag.php | 2 + .../cms/src/Charcoal/Cms/TagInterface.php | 2 + .../src/Charcoal/Cms/TaggableInterface.php | 2 + .../Charcoal/Cms/TemplateableInterface.php | 2 + .../src/Charcoal/Cms/TemplateableTrait.php | 26 +- .../Property/TemplateOptionsProperty.php | 11 +- .../Charcoal/Property/TemplateProperty.php | 41 +- .../cms/tests/Charcoal/AbstractTestCase.php | 2 + .../Cms/ContainerIntegrationTrait.php | 11 +- .../tests/Charcoal/Cms/ContainerProvider.php | 142 +++---- .../tests/Charcoal/Cms/EventCategoryTest.php | 16 +- packages/cms/tests/Charcoal/Cms/EventTest.php | 84 +--- .../tests/Charcoal/Cms/FaqCategoryTest.php | 11 +- packages/cms/tests/Charcoal/Cms/FaqTest.php | 26 +- .../tests/Charcoal/Cms/MetatagTraitTest.php | 19 +- .../Charcoal/Cms/Mock/BrokenTemplate.php | 32 +- .../tests/Charcoal/Cms/Mock/EventTemplate.php | 12 +- .../Charcoal/Cms/Mock/GenericTemplate.php | 12 +- .../tests/Charcoal/Cms/Mock/HomeTemplate.php | 12 +- .../tests/Charcoal/Cms/Mock/NewsTemplate.php | 12 +- .../Charcoal/Cms/Mock/TemplateableModel.php | 6 +- .../cms/tests/Charcoal/Cms/Mock/WebPage.php | 11 +- .../tests/Charcoal/Cms/NewsCategoryTest.php | 11 +- packages/cms/tests/Charcoal/Cms/NewsTest.php | 78 +--- .../Cms/Route/AbstractRouteTestCase.php | 6 +- .../Charcoal/Cms/Route/EventRouteTest.php | 25 +- .../Charcoal/Cms/Route/GenericRouteTest.php | 14 +- .../Charcoal/Cms/Route/NewsRouteTest.php | 25 +- .../Charcoal/Cms/Route/SectionRouteTest.php | 23 +- .../cms/tests/Charcoal/Cms/SectionTest.php | 61 +-- packages/cms/tests/Charcoal/Cms/TagTest.php | 25 +- .../Charcoal/Cms/TemplateableTraitTest.php | 47 +-- .../Property/TemplateOptionsPropertyTest.php | 17 +- .../Property/TemplatePropertyTest.php | 63 +-- packages/config/composer.json | 36 +- .../src/Charcoal/Config/AbstractConfig.php | 24 +- .../src/Charcoal/Config/AbstractEntity.php | 41 +- .../src/Charcoal/Config/ConfigInterface.php | 2 + .../Charcoal/Config/ConfigurableInterface.php | 2 + .../src/Charcoal/Config/ConfigurableTrait.php | 2 +- .../Config/DelegatesAwareInterface.php | 2 + .../Charcoal/Config/DelegatesAwareTrait.php | 2 +- .../src/Charcoal/Config/EntityInterface.php | 7 +- .../Charcoal/Config/FileAwareInterface.php | 2 + .../src/Charcoal/Config/FileAwareTrait.php | 17 +- .../src/Charcoal/Config/GenericConfig.php | 2 + .../Config/SeparatorAwareInterface.php | 2 + .../Charcoal/Config/SeparatorAwareTrait.php | 2 +- .../tests/Charcoal/AbstractTestCase.php | 2 + .../config/tests/Charcoal/AssertionsTrait.php | 12 +- .../Config/Config/AbstractConfigTestCase.php | 2 +- .../Config/Config/ConfigArrayAccessTest.php | 77 +--- .../Config/Config/ConfigArrayMergeTest.php | 65 +-- .../Config/ConfigDelegatesAwareTest.php | 90 ++-- .../Config/Config/ConfigFileAwareTest.php | 105 ++--- .../Config/ConfigSeparatorAwareTest.php | 92 ++--- .../Charcoal/Config/Config/ConfigTest.php | 65 +-- .../Config/Entity/AbstractEntityTestCase.php | 4 +- .../Config/Entity/EntityArrayAccessTest.php | 103 ++--- .../Charcoal/Config/Entity/EntityTest.php | 65 +-- .../Config/Fixture/fail/exception.php | 2 + .../Charcoal/Config/Fixture/fail/invalid1.php | 2 + .../Charcoal/Config/Fixture/pass/invalid2.php | 2 + .../Charcoal/Config/Fixture/pass/valid1.php | 2 + .../Config/Mixin/ArrayAccessTestTrait.php | 50 +-- .../Config/Mixin/ConfigurableTest.php | 106 ++--- .../Config/Mixin/DelegatesAwareTest.php | 66 +-- .../Charcoal/Config/Mixin/FileAwareTest.php | 34 +- .../FileLoader/AbstractFileLoaderTestCase.php | 2 - .../Mixin/FileLoader/IniFileLoaderTest.php | 34 +- .../Mixin/FileLoader/JsonFileLoaderTest.php | 25 +- .../Mixin/FileLoader/PhpFileLoaderTest.php | 39 +- .../Mixin/FileLoader/YamlFileLoaderTest.php | 66 +-- .../Config/Mixin/SeparatorAwareTest.php | 239 +++-------- .../Config/Mock/ConfigurableObject.php | 2 + .../Charcoal/Config/Mock/DelegateEntity.php | 14 +- .../tests/Charcoal/Config/Mock/Entity.php | 6 +- .../tests/Charcoal/Config/Mock/FileLoader.php | 2 + .../Charcoal/Config/Mock/MacroConfig.php | 8 +- .../Charcoal/Config/Mock/MacroEntity.php | 2 + .../tests/Charcoal/Config/Mock/MacroTrait.php | 4 +- .../tests/Charcoal/Config/Mock/TreeEntity.php | 24 +- .../config/tests/Charcoal/FixturesTrait.php | 2 +- packages/config/tests/bootstrap.php | 4 +- packages/core/composer.json | 27 +- .../src/Charcoal/Loader/CollectionLoader.php | 120 ++---- .../Loader/CollectionLoaderAwareTrait.php | 2 +- .../Loader/CollectionLoaderFactoryTrait.php | 4 +- .../Charcoal/Loader/LazyCollectionLoader.php | 5 +- .../core/src/Charcoal/Model/AbstractModel.php | 46 +-- .../core/src/Charcoal/Model/Collection.php | 72 ++-- .../Charcoal/Model/CollectionInterface.php | 2 + .../Charcoal/Model/DescribableInterface.php | 2 + .../src/Charcoal/Model/DescribableTrait.php | 11 +- .../src/Charcoal/Model/MetadataInterface.php | 2 + .../Model/MetadataLoaderAwareTrait.php | 5 +- packages/core/src/Charcoal/Model/Model.php | 3 +- .../src/Charcoal/Model/ModelFactoryTrait.php | 2 +- .../src/Charcoal/Model/ModelInterface.php | 4 +- .../Model/ModelLoaderBuilderTrait.php | 6 +- .../core/src/Charcoal/Model/ModelMetadata.php | 28 +- .../Charcoal/Model/Service/MetadataConfig.php | 41 +- .../Charcoal/Model/Service/MetadataLoader.php | 141 +++---- .../Charcoal/Model/Service/ModelBuilder.php | 32 +- .../Charcoal/Model/Service/ModelLoader.php | 53 +-- .../Model/Service/ModelLoaderBuilder.php | 22 +- .../ServiceProvider/ModelServiceProvider.php | 141 +++---- .../Charcoal/Source/AbstractExpression.php | 17 +- .../src/Charcoal/Source/AbstractSource.php | 37 +- .../Source/Database/DatabaseExpression.php | 2 + .../Database/DatabaseExpressionInterface.php | 2 + .../Source/Database/DatabaseFilter.php | 27 +- .../Source/Database/DatabaseOrder.php | 29 +- .../Source/Database/DatabasePagination.php | 12 +- .../src/Charcoal/Source/DatabaseSource.php | 202 ++++----- .../Charcoal/Source/DatabaseSourceConfig.php | 72 ++-- .../Source/DatabaseSourceInterface.php | 2 + .../core/src/Charcoal/Source/Expression.php | 15 +- .../Source/ExpressionFieldInterface.php | 2 + .../Charcoal/Source/ExpressionFieldTrait.php | 14 +- .../Charcoal/Source/ExpressionInterface.php | 5 +- packages/core/src/Charcoal/Source/Filter.php | 37 +- .../Source/FilterCollectionInterface.php | 2 + .../Charcoal/Source/FilterCollectionTrait.php | 8 +- .../src/Charcoal/Source/FilterInterface.php | 2 + .../Charcoal/Source/ModelAwareInterface.php | 2 + .../src/Charcoal/Source/ModelAwareTrait.php | 4 +- packages/core/src/Charcoal/Source/Order.php | 39 +- .../Source/OrderCollectionInterface.php | 2 + .../Charcoal/Source/OrderCollectionTrait.php | 8 +- .../src/Charcoal/Source/OrderInterface.php | 2 + .../core/src/Charcoal/Source/Pagination.php | 20 +- .../Charcoal/Source/PaginationInterface.php | 2 + .../Source/RawExpressionInterface.php | 2 + .../core/src/Charcoal/Source/SourceConfig.php | 18 +- .../src/Charcoal/Source/SourceInterface.php | 10 +- .../src/Charcoal/Source/StorableInterface.php | 4 +- .../src/Charcoal/Source/StorableTrait.php | 46 +-- .../Charcoal/Validator/AbstractValidator.php | 18 +- .../Validator/ValidatableInterface.php | 4 +- .../Charcoal/Validator/ValidatableTrait.php | 4 +- .../Charcoal/Validator/ValidatorInterface.php | 2 + .../Charcoal/Validator/ValidatorResult.php | 41 +- .../core/tests/Charcoal/AbstractTestCase.php | 2 + .../core/tests/Charcoal/AssertionsTrait.php | 12 +- .../CoreContainerIntegrationTrait.php | 3 +- .../tests/Charcoal/CoreContainerProvider.php | 182 ++++----- .../Charcoal/Loader/CollectionLoaderTest.php | 67 +-- .../tests/Charcoal/Mock/BadStorableMock.php | 28 +- .../Charcoal/Mock/FilterCollectionClass.php | 2 +- .../core/tests/Charcoal/Mock/GenericModel.php | 12 +- .../core/tests/Charcoal/Mock/MockModule.php | 2 + .../Charcoal/Mock/OrderCollectionClass.php | 2 +- .../core/tests/Charcoal/Mock/OrderTree.php | 15 +- .../core/tests/Charcoal/Mock/SourceMock.php | 12 +- .../core/tests/Charcoal/Mock/StorableMock.php | 23 +- .../tests/Charcoal/Mock/ValidatableClass.php | 3 +- .../tests/Charcoal/Mock/ValidatorClass.php | 7 +- .../Charcoal/Model/AbstractMetadataTest.php | 13 +- .../tests/Charcoal/Model/CollectionTest.php | 189 ++------- .../Charcoal/Model/ModelMetadataTest.php | 44 +- .../core/tests/Charcoal/Model/ModelTest.php | 65 +-- .../Charcoal/Model/ModelValidatorTest.php | 17 +- .../Model/Service/MetadataLoaderTest.php | 10 +- .../Model/Service/ModelLoaderBuilderTest.php | 13 +- .../Model/Service/ModelLoaderTest.php | 9 +- .../ModelServiceProviderTest.php | 35 +- .../core/tests/Charcoal/ReflectionsTrait.php | 19 +- .../Source/AbstractExpressionTest.php | 66 +-- .../Charcoal/Source/AbstractSourceTest.php | 168 +++----- .../Database/DatabaseExpressionTest.php | 16 +- .../Source/Database/DatabaseFilterTest.php | 117 ++---- .../Source/Database/DatabaseOrderTest.php | 64 +-- .../Database/DatabasePaginationTest.php | 24 +- .../Source/DatabaseExpressionTestTrait.php | 4 +- .../Source/DatabaseSourceConfigTest.php | 35 +- .../Charcoal/Source/DatabaseSourceTest.php | 7 +- .../Charcoal/Source/DatabaseTestModel.php | 2 + .../Source/ExpressionCollectionTestTrait.php | 4 +- .../Source/ExpressionFieldTraitTest.php | 58 +-- .../tests/Charcoal/Source/ExpressionTest.php | 25 +- .../Source/ExpressionTestFieldTrait.php | 16 +- .../Charcoal/Source/ExpressionTestTrait.php | 36 +- .../Source/FilterCollectionTraitTest.php | 71 +--- .../core/tests/Charcoal/Source/FilterTest.php | 120 ++---- .../Source/OrderCollectionTraitTest.php | 71 +--- .../core/tests/Charcoal/Source/OrderTest.php | 74 +--- .../tests/Charcoal/Source/PaginationTest.php | 47 +-- .../Charcoal/Source/SourceConfigTest.php | 10 +- .../Charcoal/Source/StorableTraitTest.php | 96 ++--- .../Validator/ValidatableTraitTest.php | 8 +- .../Validator/ValidatorResultTest.php | 30 +- .../Charcoal/Validator/ValidatorTest.php | 57 +-- packages/email/composer.json | 29 +- .../src/Charcoal/Email/Api/V1/LinkAction.php | 21 +- .../src/Charcoal/Email/Api/V1/OpenAction.php | 17 +- .../email/src/Charcoal/Email/ApiModule.php | 17 +- packages/email/src/Charcoal/Email/Email.php | 202 +++------ .../src/Charcoal/Email/EmailAwareTrait.php | 7 - .../email/src/Charcoal/Email/EmailConfig.php | 125 ++---- .../src/Charcoal/Email/EmailInterface.php | 10 - .../email/src/Charcoal/Email/EmailLog.php | 2 + .../src/Charcoal/Email/EmailQueueItem.php | 69 ++-- .../src/Charcoal/Email/EmailQueueManager.php | 4 +- .../Email/Exception/EmailNotSentException.php | 2 + .../Email/Exception/ExceptionInterface.php | 2 + .../Charcoal/Email/GenericEmailTemplate.php | 2 + .../src/Charcoal/Email/Objects/EmailLog.php | 70 ++-- .../email/src/Charcoal/Email/Objects/Link.php | 12 +- .../src/Charcoal/Email/Objects/LinkLog.php | 34 +- .../src/Charcoal/Email/Objects/OpenLog.php | 37 +- .../Email/Script/ProcessQueueScript.php | 26 +- .../ServiceProvider/EmailServiceProvider.php | 60 ++- .../src/Charcoal/Email/Services/Parser.php | 3 - .../src/Charcoal/Email/Services/Tracker.php | 19 +- .../email/tests/Charcoal/AbstractTestCase.php | 2 + .../Charcoal/Email/EmailAwareTraitTest.php | 16 +- .../tests/Charcoal/Email/EmailConfigTest.php | 24 +- .../Charcoal/Email/EmailQueueItemTest.php | 11 +- .../Charcoal/Email/EmailQueueManagerTest.php | 2 +- .../email/tests/Charcoal/Email/EmailTest.php | 38 +- .../Charcoal/Email/Objects/EmailLogTest.php | 4 +- .../Charcoal/Email/Services/TrackerTest.php | 13 +- packages/email/tests/bootstrap.php | 2 + packages/factory/composer.json | 24 +- .../src/Charcoal/Factory/AbstractFactory.php | 48 +-- .../src/Charcoal/Factory/FactoryInterface.php | 4 +- .../src/Charcoal/Factory/GenericFactory.php | 2 + .../src/Charcoal/Factory/GenericResolver.php | 12 +- .../src/Charcoal/Factory/MapFactory.php | 2 + .../src/Charcoal/Factory/ResolverFactory.php | 59 +-- .../tests/Charcoal/AbstractTestCase.php | 2 + .../Charcoal/Factory/AbstractFactoryTest.php | 66 +-- .../Charcoal/Factory/GenericFactoryTest.php | 43 +- .../Charcoal/Factory/ResolverFactoryTest.php | 52 +-- packages/image/composer.json | 36 +- .../src/Charcoal/Image/AbstractEffect.php | 13 +- .../src/Charcoal/Image/AbstractImage.php | 17 +- .../Effect/AbstractAutoorientationEffect.php | 2 + .../Image/Effect/AbstractBlurEffect.php | 35 +- .../Effect/AbstractCompressionEffect.php | 7 +- .../Image/Effect/AbstractCropEffect.php | 34 +- .../Image/Effect/AbstractDitherEffect.php | 12 +- .../Image/Effect/AbstractGrayscaleEffect.php | 2 + .../Image/Effect/AbstractMaskEffect.php | 5 +- .../Image/Effect/AbstractMirrorEffect.php | 5 +- .../Image/Effect/AbstractModulateEffect.php | 2 + .../Image/Effect/AbstractResizeEffect.php | 64 +-- .../Image/Effect/AbstractRotateEffect.php | 5 +- .../Image/Effect/AbstractSepiaEffect.php | 2 + .../Image/Effect/AbstractSharpenEffect.php | 26 +- .../Image/Effect/AbstractThresholdEffect.php | 7 +- .../Image/Effect/AbstractTintEffect.php | 16 +- .../Image/Effect/AbstractWatermarkEffect.php | 4 +- .../Image/Effect/LayerEffectInterface.php | 2 + .../src/Charcoal/Image/EffectFactory.php | 2 + .../src/Charcoal/Image/EffectInterface.php | 4 +- .../image/src/Charcoal/Image/ImageFactory.php | 17 +- .../src/Charcoal/Image/ImageInterface.php | 4 +- .../ImagemagickAutoorientationEffect.php | 2 +- .../Effect/ImagemagickBlurEffect.php | 30 +- .../Effect/ImagemagickCompressionEffect.php | 3 +- .../Effect/ImagemagickDitherEffect.php | 5 +- .../Effect/ImagemagickFormatEffect.php | 5 +- .../Effect/ImagemagickGrayscaleEffect.php | 2 +- .../Effect/ImagemagickMaskEffect.php | 5 +- .../Effect/ImagemagickMirrorEffect.php | 9 +- .../Effect/ImagemagickModulateEffect.php | 3 +- .../Effect/ImagemagickResizeEffect.php | 6 +- .../Effect/ImagemagickRevertEffect.php | 3 +- .../Effect/ImagemagickRotateEffect.php | 3 +- .../Effect/ImagemagickSepiaEffect.php | 3 +- .../Effect/ImagemagickSharpenEffect.php | 15 +- .../Effect/ImagemagickThresholdEffect.php | 3 +- .../Effect/ImagemagickTintEffect.php | 9 +- .../Effect/ImagemagickWatermarkEffect.php | 5 +- .../Image/Imagemagick/ImagemagickImage.php | 79 ++-- .../Effect/ImagickAutoorientationEffect.php | 2 +- .../Imagick/Effect/ImagickBlurEffect.php | 13 +- .../Effect/ImagickCompressionEffect.php | 7 +- .../Imagick/Effect/ImagickDitherEffect.php | 2 +- .../Imagick/Effect/ImagickFormatEffect.php | 2 +- .../Imagick/Effect/ImagickGrayscaleEffect.php | 2 +- .../Imagick/Effect/ImagickMaskEffect.php | 5 +- .../Imagick/Effect/ImagickMirrorEffect.php | 3 +- .../Imagick/Effect/ImagickModulateEffect.php | 2 +- .../Imagick/Effect/ImagickRevertEffect.php | 2 +- .../Imagick/Effect/ImagickRotateEffect.php | 2 +- .../Imagick/Effect/ImagickSepiaEffect.php | 2 +- .../Imagick/Effect/ImagickSharpenEffect.php | 6 +- .../Imagick/Effect/ImagickThresholdEffect.php | 3 +- .../Imagick/Effect/ImagickTintEffect.php | 3 +- .../Imagick/Effect/ImagickWatermarkEffect.php | 5 +- .../Charcoal/Image/Imagick/ImagickImage.php | 42 +- .../Charcoal/Image/AbstractImageTest.php | 12 +- .../Image/Effect/AbstractBlurEffectTest.php | 22 +- .../Image/Effect/AbstractDitherEffectTest.php | 12 +- .../Effect/AbstractGrayscaleEffectTest.php | 6 +- .../Image/Effect/AbstractMaskEffectTest.php | 18 +- .../Image/Effect/AbstractMirrorEffectTest.php | 10 +- .../Effect/AbstractModulateEffectTest.php | 26 +- .../Image/Effect/AbstractResizeEffectTest.php | 46 +-- .../Image/Effect/AbstractRevertEffectTest.php | 10 +- .../Image/Effect/AbstractRotateEffectTest.php | 12 +- .../Image/Effect/AbstractSepiaEffectTest.php | 14 +- .../Effect/AbstractSharpenEffectTest.php | 26 +- .../Effect/AbstractThresholdEffectTest.php | 14 +- .../Image/Effect/AbstractTintEffectTest.php | 14 +- .../Effect/AbstractWatermarkEffectTest.php | 18 +- .../Imagemagick/ImagemagickImageTest.php | 36 +- .../Image/Imagick/ImagickImageTest.php | 48 +-- packages/image/tests/bootstrap.php | 2 + packages/object/composer.json | 20 +- .../Charcoal/Object/ArchivableInterface.php | 2 + .../src/Charcoal/Object/ArchivableTrait.php | 2 + .../Charcoal/Object/AuthorableInterface.php | 2 + .../Object/CategorizableInterface.php | 2 + .../Object/CategorizableMultipleInterface.php | 2 + .../src/Charcoal/Object/CategoryInterface.php | 2 + .../src/Charcoal/Object/CategoryTrait.php | 10 +- .../object/src/Charcoal/Object/Content.php | 47 +-- .../src/Charcoal/Object/ContentInterface.php | 2 + .../Object/HierarchicalCollection.php | 36 +- .../Charcoal/Object/HierarchicalInterface.php | 2 + .../src/Charcoal/Object/HierarchicalTrait.php | 107 ++--- .../src/Charcoal/Object/ObjectRevision.php | 66 ++- .../Object/ObjectRevisionInterface.php | 2 + .../src/Charcoal/Object/ObjectRoute.php | 43 +- .../Charcoal/Object/ObjectRouteInterface.php | 2 + .../src/Charcoal/Object/ObjectSchedule.php | 77 ++-- .../Object/ObjectScheduleInterface.php | 8 +- .../Charcoal/Object/PublishableInterface.php | 2 + .../src/Charcoal/Object/PublishableTrait.php | 18 +- .../Charcoal/Object/RevisionableInterface.php | 2 + .../src/Charcoal/Object/RevisionableTrait.php | 28 +- .../src/Charcoal/Object/RoutableInterface.php | 2 + .../src/Charcoal/Object/RoutableTrait.php | 98 ++--- .../Object/TimestampableInterface.php | 2 + .../object/src/Charcoal/Object/UserData.php | 55 +-- .../src/Charcoal/Object/UserDataInterface.php | 2 + .../tests/Charcoal/AbstractTestCase.php | 2 + .../Charcoal/Object/ArchivableTraitTest.php | 17 +- .../Object/CategorizableTraitTest.php | 11 +- .../Charcoal/Object/CategoryTraitTest.php | 20 +- .../Charcoal/Object/ContainerProvider.php | 166 ++++---- .../tests/Charcoal/Object/ContentTest.php | 72 +--- .../Charcoal/Object/HierarchicalTraitTest.php | 99 ++--- .../Charcoal/Object/Mocks/AbstractModel.php | 7 +- .../Object/Mocks/HierarchicalClass.php | 12 +- .../Object/Mocks/PublishableClass.php | 7 +- .../Charcoal/Object/Mocks/RoutableClass.php | 12 +- .../Charcoal/Object/ObjectRevisionTest.php | 61 +-- .../tests/Charcoal/Object/ObjectRouteTest.php | 51 +-- .../Charcoal/Object/ObjectScheduleTest.php | 61 +-- .../Charcoal/Object/PublishableTraitTest.php | 62 +-- .../Charcoal/Object/RevisionableTraitTest.php | 2 + .../Charcoal/Object/RoutableTraitTest.php | 87 +--- .../tests/Charcoal/Object/UserDataTest.php | 44 +- packages/property/composer.json | 24 +- .../Charcoal/Property/AbstractProperty.php | 139 +++---- .../src/Charcoal/Property/AudioProperty.php | 62 +-- .../src/Charcoal/Property/BooleanProperty.php | 59 ++- .../src/Charcoal/Property/ColorProperty.php | 50 +-- .../Charcoal/Property/DateTimeProperty.php | 104 ++--- .../Property/DescribablePropertyInterface.php | 4 +- .../Property/DescribablePropertyTrait.php | 25 +- .../src/Charcoal/Property/EmailProperty.php | 27 +- .../src/Charcoal/Property/FileProperty.php | 292 +++++-------- .../src/Charcoal/Property/GenericProperty.php | 13 +- .../src/Charcoal/Property/HtmlProperty.php | 20 +- .../src/Charcoal/Property/IdProperty.php | 51 ++- .../src/Charcoal/Property/ImageProperty.php | 136 +++--- .../src/Charcoal/Property/IpProperty.php | 54 ++- .../src/Charcoal/Property/LangProperty.php | 37 +- .../Property/MapStructureProperty.php | 7 +- .../Property/ModelStructureProperty.php | 115 ++---- .../Charcoal/Property/MultiObjectProperty.php | 55 +-- .../src/Charcoal/Property/NumberProperty.php | 25 +- .../src/Charcoal/Property/ObjectProperty.php | 152 +++---- .../Charcoal/Property/PasswordProperty.php | 27 +- .../src/Charcoal/Property/PhoneProperty.php | 26 +- .../src/Charcoal/Property/PropertyFactory.php | 30 +- .../src/Charcoal/Property/PropertyField.php | 68 ++- .../Charcoal/Property/PropertyInterface.php | 2 + .../Charcoal/Property/PropertyMetadata.php | 15 +- .../Property/SelectablePropertyInterface.php | 2 + .../Property/SelectablePropertyTrait.php | 11 +- .../src/Charcoal/Property/SpriteProperty.php | 42 +- .../Property/StorablePropertyInterface.php | 2 + .../Property/StorablePropertyTrait.php | 38 +- .../src/Charcoal/Property/StringProperty.php | 118 ++---- .../Property/Structure/StructureMetadata.php | 36 +- .../Property/Structure/StructureModel.php | 2 + .../Charcoal/Property/StructureProperty.php | 58 +-- .../src/Charcoal/Property/TextProperty.php | 20 +- .../src/Charcoal/Property/UrlProperty.php | 13 +- .../src/Charcoal/Property/VideoProperty.php | 37 +- .../src/Charcoal/Property/data/colors.php | 2 + .../tests/Charcoal/AbstractTestCase.php | 2 + .../Property/AbstractFilePropertyTestCase.php | 78 ++-- .../Property/AbstractPropertyTest.php | 100 +---- .../Charcoal/Property/AudioPropertyTest.php | 67 +-- .../Charcoal/Property/BooleanPropertyTest.php | 60 +-- .../Charcoal/Property/ColorPropertyTest.php | 65 +-- .../Property/ContainerIntegrationTrait.php | 3 +- .../Charcoal/Property/ContainerProvider.php | 203 ++++----- .../Property/DateTimePropertyTest.php | 86 +--- .../Charcoal/Property/EmailPropertyTest.php | 30 +- .../Charcoal/Property/FilePropertyTest.php | 386 ++++++++---------- .../tests/Charcoal/Property/FixturesTrait.php | 4 +- .../Charcoal/Property/GenericPropertyTest.php | 16 +- .../Charcoal/Property/HtmlPropertyTest.php | 22 +- .../Charcoal/Property/IdPropertyTest.php | 76 +--- .../Charcoal/Property/ImagePropertyTest.php | 60 +-- .../Charcoal/Property/IpPropertyTest.php | 56 +-- .../Charcoal/Property/LangPropertyTest.php | 32 +- .../Property/MapStructurePropertyTest.php | 10 +- .../Charcoal/Property/Mocks/GenericModel.php | 14 +- .../Property/ModelStructurePropertyTest.php | 12 +- .../Charcoal/Property/NumberPropertyTest.php | 14 +- .../Charcoal/Property/ObjectPropertyTest.php | 167 ++------ .../Property/PasswordPropertyTest.php | 17 +- .../Charcoal/Property/PhonePropertyTest.php | 24 +- .../Charcoal/Property/PropertyFieldTest.php | 45 +- .../Property/PropertyMetadataTest.php | 8 +- .../Property/SelectablePropertyTraitTest.php | 44 +- .../Charcoal/Property/SpritePropertyTest.php | 20 +- .../Property/StorablePropertyTraitTest.php | 31 +- .../Charcoal/Property/StringPropertyTest.php | 138 ++----- .../Property/StructurePropertyTest.php | 24 +- .../Charcoal/Property/TextPropertyTest.php | 19 +- .../Charcoal/Property/UrlPropertyTest.php | 11 +- .../tests/Charcoal/ReflectionsTrait.php | 19 +- packages/queue/composer.json | 25 +- .../Charcoal/Queue/AbstractQueueManager.php | 39 +- .../src/Charcoal/Queue/QueueItemInterface.php | 8 +- .../src/Charcoal/Queue/QueueItemTrait.php | 26 +- .../Charcoal/Queue/QueueManagerInterface.php | 2 + .../src/Charcoal/Queue/QueueableInterface.php | 2 + packages/translator/composer.json | 35 +- .../src/Charcoal/Translator/LocalesConfig.php | 46 +-- .../Charcoal/Translator/LocalesManager.php | 43 +- .../Middleware/LanguageMiddleware.php | 93 ++--- .../Script/TranslationParserScript.php | 63 ++- .../TranslatorServiceProvider.php | 107 ++--- .../Translator/TranslatableInterface.php | 2 + .../Charcoal/Translator/TranslatableValue.php | 31 +- .../src/Charcoal/Translator/Translation.php | 42 +- .../src/Charcoal/Translator/Translator.php | 63 +-- .../Charcoal/Translator/TranslatorConfig.php | 51 +-- .../Charcoal/Translator/AbstractTestCase.php | 2 + .../Charcoal/Translator/ContainerProvider.php | 245 +++++------ .../Charcoal/Translator/LocalesConfigTest.php | 26 +- .../Translator/LocalesManagerTest.php | 69 +--- .../Middleware/LanguageMiddlewareTest.php | 84 +--- .../Charcoal/Translator/Mock/StringClass.php | 17 +- .../Charcoal/Translator/ReflectionsTrait.php | 19 +- .../Script/TranslationParserScriptTest.php | 20 +- .../TranslatorServiceProviderTest.php | 75 +--- .../Charcoal/Translator/TranslationTest.php | 116 ++---- .../Translator/TranslatorAwareTraitTest.php | 16 +- .../Translator/TranslatorConfigTest.php | 51 +-- .../Charcoal/Translator/TranslatorTest.php | 131 ++---- packages/ui/composer.json | 36 +- .../ui/src/Charcoal/Ui/AbstractUiItem.php | 2 +- .../Ui/ConditionalizableInterface.php | 2 + .../Charcoal/Ui/ConditionalizableTrait.php | 27 +- .../Ui/Dashboard/AbstractDashboard.php | 2 +- .../Ui/Dashboard/DashboardBuilder.php | 20 +- .../Charcoal/Ui/Dashboard/DashboardConfig.php | 2 + .../Ui/Dashboard/DashboardInterface.php | 2 + .../Charcoal/Ui/Dashboard/DashboardTrait.php | 16 +- .../Ui/Dashboard/GenericDashboard.php | 7 +- .../ui/src/Charcoal/Ui/Form/AbstractForm.php | 2 +- .../ui/src/Charcoal/Ui/Form/FormBuilder.php | 12 +- .../ui/src/Charcoal/Ui/Form/FormConfig.php | 2 + .../ui/src/Charcoal/Ui/Form/FormInterface.php | 2 + .../ui/src/Charcoal/Ui/Form/FormTrait.php | 102 ++--- .../ui/src/Charcoal/Ui/Form/GenericForm.php | 7 +- .../Ui/FormGroup/AbstractFormGroup.php | 3 +- .../Charcoal/Ui/FormGroup/FormGroupConfig.php | 2 + .../Ui/FormGroup/FormGroupInterface.php | 4 +- .../Charcoal/Ui/FormGroup/FormGroupTrait.php | 12 +- .../Ui/FormGroup/GenericFormGroup.php | 7 +- .../Ui/FormInput/AbstractFormInput.php | 2 + .../Ui/FormInput/FormInputBuilder.php | 20 +- .../Charcoal/Ui/FormInput/FormInputConfig.php | 2 + .../Ui/FormInput/FormInputInterface.php | 2 + .../Ui/FormInput/GenericFormInput.php | 7 +- .../src/Charcoal/Ui/Layout/AbstractLayout.php | 2 + .../src/Charcoal/Ui/Layout/GenericLayout.php | 6 +- .../Ui/Layout/LayoutAwareInterface.php | 2 + .../src/Charcoal/Ui/Layout/LayoutBuilder.php | 20 +- .../src/Charcoal/Ui/Layout/LayoutConfig.php | 2 + .../src/Charcoal/Ui/Layout/LayoutFactory.php | 20 +- .../Charcoal/Ui/Layout/LayoutInterface.php | 2 + .../ui/src/Charcoal/Ui/Layout/LayoutTrait.php | 45 +- .../ui/src/Charcoal/Ui/Menu/AbstractMenu.php | 12 +- .../ui/src/Charcoal/Ui/Menu/GenericMenu.php | 7 +- .../ui/src/Charcoal/Ui/Menu/MenuBuilder.php | 21 +- .../ui/src/Charcoal/Ui/Menu/MenuConfig.php | 2 + .../ui/src/Charcoal/Ui/Menu/MenuFactory.php | 20 +- .../ui/src/Charcoal/Ui/Menu/MenuInterface.php | 2 + .../Charcoal/Ui/MenuItem/AbstractMenuItem.php | 12 +- .../Charcoal/Ui/MenuItem/GenericMenuItem.php | 7 +- .../Charcoal/Ui/MenuItem/MenuItemBuilder.php | 21 +- .../Charcoal/Ui/MenuItem/MenuItemConfig.php | 2 + .../Charcoal/Ui/MenuItem/MenuItemFactory.php | 20 +- .../Ui/MenuItem/MenuItemInterface.php | 2 + .../Charcoal/Ui/PrioritizableInterface.php | 2 + .../DashboardServiceProvider.php | 43 +- .../ServiceProvider/FormServiceProvider.php | 96 ++--- .../ServiceProvider/LayoutServiceProvider.php | 17 +- .../ServiceProvider/MenuServiceProvider.php | 18 +- .../Ui/ServiceProvider/UiServiceProvider.php | 3 +- .../src/Charcoal/Ui/UiGroupingInterface.php | 2 + packages/ui/src/Charcoal/Ui/UiItemConfig.php | 26 +- .../ui/src/Charcoal/Ui/UiItemInterface.php | 2 + packages/ui/src/Charcoal/Ui/UiItemTrait.php | 38 +- .../ui/tests/Charcoal/AbstractTestCase.php | 2 + .../tests/Charcoal/Ui/AbstractUiItemTest.php | 74 +--- .../Charcoal/Ui/ContainerIntegrationTrait.php | 3 +- .../tests/Charcoal/Ui/ContainerProvider.php | 39 +- .../Ui/Dashboard/AbstractDashboardTest.php | 64 +-- .../Ui/Dashboard/GenericDashboardTest.php | 8 +- .../Charcoal/Ui/Form/AbstractFormTest.php | 61 +-- .../Charcoal/Ui/Form/GenericFormTest.php | 8 +- .../Ui/FormGroup/AbstractFormGroupTest.php | 41 +- .../Ui/FormGroup/GenericFormGroupTest.php | 8 +- .../Ui/FormInput/GenericFormInputTest.php | 8 +- .../Charcoal/Ui/Layout/AbstractLayoutTest.php | 95 +---- .../Charcoal/Ui/Layout/GenericLayoutTest.php | 8 +- .../Charcoal/Ui/Menu/AbstractMenuTest.php | 31 +- .../Charcoal/Ui/Menu/GenericMenuTest.php | 8 +- .../Ui/MenuItem/AbstractMenuItemTest.php | 17 +- .../Ui/MenuItem/GenericMenuItemTest.php | 8 +- .../DashboardServiceProviderTest.php | 37 +- .../ServiceProvider/UiServiceProviderTest.php | 7 +- packages/user/composer.json | 20 +- .../src/Charcoal/User/AbstractAuthToken.php | 25 +- .../Charcoal/User/AbstractAuthenticator.php | 55 +-- .../src/Charcoal/User/AbstractAuthorizer.php | 7 +- .../user/src/Charcoal/User/AbstractUser.php | 39 +- .../User/Access/AuthenticatableInterface.php | 2 + .../User/Access/AuthenticatableTrait.php | 3 +- .../user/src/Charcoal/User/Acl/Manager.php | 17 +- .../user/src/Charcoal/User/Acl/Permission.php | 26 +- .../Charcoal/User/Acl/PermissionCategory.php | 10 +- packages/user/src/Charcoal/User/Acl/Role.php | 51 +-- .../user/src/Charcoal/User/AclAwareTrait.php | 2 +- .../src/Charcoal/User/AuthAwareInterface.php | 2 + .../user/src/Charcoal/User/AuthAwareTrait.php | 9 +- packages/user/src/Charcoal/User/AuthToken.php | 2 + .../User/AuthTokenCookieInterface.php | 2 + .../Charcoal/User/AuthTokenCookieTrait.php | 8 +- .../src/Charcoal/User/AuthTokenInterface.php | 2 + .../src/Charcoal/User/AuthTokenMetadata.php | 73 ++-- .../user/src/Charcoal/User/Authenticator.php | 20 +- .../Charcoal/User/AuthenticatorInterface.php | 2 + .../user/src/Charcoal/User/Authorizer.php | 21 +- .../src/Charcoal/User/AuthorizerInterface.php | 2 + .../user/src/Charcoal/User/GenericUser.php | 6 +- .../ServiceProvider/AuthServiceProvider.php | 35 +- .../user/src/Charcoal/User/UserInterface.php | 2 + .../user/tests/Charcoal/AbstractTestCase.php | 2 + .../user/tests/Charcoal/ReflectionsTrait.php | 19 +- .../tests/Charcoal/User/AbstractUserTest.php | 60 +-- .../tests/Charcoal/User/Acl/ManagerTest.php | 26 +- .../User/Acl/PermissionCategoryTest.php | 21 +- .../Charcoal/User/Acl/PermissionTest.php | 40 +- .../user/tests/Charcoal/User/Acl/RoleTest.php | 40 +- .../Charcoal/User/AuthTokenMetadataTest.php | 26 +- .../tests/Charcoal/User/AuthTokenTest.php | 51 +-- .../tests/Charcoal/User/AuthenticatorTest.php | 64 +-- .../tests/Charcoal/User/AuthorizerTest.php | 138 ++----- .../tests/Charcoal/User/ContainerProvider.php | 166 ++++---- .../tests/Charcoal/User/GenericUserTest.php | 21 +- packages/view/composer.json | 38 +- .../view/src/Charcoal/View/AbstractEngine.php | 14 +- .../view/src/Charcoal/View/AbstractLoader.php | 26 +- .../view/src/Charcoal/View/AbstractView.php | 14 +- .../src/Charcoal/View/EngineInterface.php | 2 - .../src/Charcoal/View/LoaderInterface.php | 2 - .../Charcoal/View/Mustache/AssetsHelpers.php | 74 +--- .../View/Mustache/HelpersInterface.php | 2 - .../View/Mustache/MarkdownHelpers.php | 12 +- .../Charcoal/View/Mustache/MustacheEngine.php | 39 +- .../Charcoal/View/Mustache/MustacheLoader.php | 8 +- .../View/Mustache/TranslatorHelpers.php | 30 +- .../view/src/Charcoal/View/Php/PhpEngine.php | 8 +- .../view/src/Charcoal/View/Php/PhpLoader.php | 8 +- packages/view/src/Charcoal/View/Renderer.php | 7 +- .../src/Charcoal/View/Twig/DebugHelpers.php | 10 +- .../Charcoal/View/Twig/HelpersInterface.php | 2 - .../Charcoal/View/Twig/TranslatorHelpers.php | 18 +- .../src/Charcoal/View/Twig/TwigEngine.php | 31 +- .../src/Charcoal/View/Twig/TwigLoader.php | 15 +- .../src/Charcoal/View/Twig/UrlHelpers.php | 29 +- .../view/src/Charcoal/View/ViewConfig.php | 47 +-- .../view/src/Charcoal/View/ViewInterface.php | 3 - .../src/Charcoal/View/ViewServiceProvider.php | 148 +++---- .../src/Charcoal/View/ViewableInterface.php | 3 - .../view/src/Charcoal/View/ViewableTrait.php | 5 - .../view/tests/Charcoal/AbstractTestCase.php | 2 + .../view/tests/Charcoal/AssertionsTrait.php | 35 +- .../Charcoal/View/AbstractEngineTest.php | 13 +- .../Charcoal/View/AbstractLoaderTest.php | 18 +- .../tests/Charcoal/View/AbstractViewTest.php | 32 +- .../tests/Charcoal/View/Mock/MockModule.php | 2 + .../View/Mustache/AssetsHelpersTest.php | 92 +---- .../View/Mustache/MarkdownHelpersTest.php | 22 +- .../View/Mustache/Mock/MockHelpers.php | 4 +- .../View/Mustache/MustacheEngineTest.php | 52 +-- .../View/Mustache/MustacheLoaderTest.php | 30 +- .../View/Mustache/TranslatorHelpersTest.php | 53 +-- .../tests/Charcoal/View/Php/PhpEngineTest.php | 22 +- .../tests/Charcoal/View/Php/PhpLoaderTest.php | 22 +- .../view/tests/Charcoal/View/RendererTest.php | 8 +- .../Charcoal/View/Twig/DebugHelpersTest.php | 20 +- .../Charcoal/View/Twig/Mock/MockHelpers.php | 4 +- .../View/Twig/TranslatorHelpersTest.php | 23 +- .../Charcoal/View/Twig/TwigEngineTest.php | 52 +-- .../Charcoal/View/Twig/TwigLoaderTest.php | 40 +- .../Charcoal/View/Twig/UrlHelpersTest.php | 20 +- .../tests/Charcoal/View/ViewConfigTest.php | 42 +- .../Charcoal/View/ViewServiceProviderTest.php | 33 +- .../tests/Charcoal/View/ViewableTraitTest.php | 50 +-- 1109 files changed, 11536 insertions(+), 21796 deletions(-) diff --git a/packages/admin/composer.json b/packages/admin/composer.json index baef4cfc2..3ce9f9afc 100644 --- a/packages/admin/composer.json +++ b/packages/admin/composer.json @@ -1,8 +1,11 @@ { - "type": "library", "name": "charcoal/admin", "description": "The Charcoal Administration Dashboard", - "keywords": ["charcoal", "admin", "cms"], + "keywords": [ + "charcoal", + "admin", + "cms" + ], "homepage": "https://charcoal.locomotive.ca", "license": "MIT", "authors": [ @@ -15,20 +18,10 @@ "homepage": "https://locomotive.ca" } ], - "config": { - "sort-packages": true - }, - "extra": { - "branch-alias": { - "dev-main": "5.x-dev" - } - }, + "type": "library", "require": { - "php": "^7.4 || ^8.0", + "php": "^8.3", "barryvdh/elfinder-flysystem-driver": "^0.3", - "guzzlehttp/guzzle": "^6.0 || ^7.0", - "kriswallsmith/assetic": "^1.4", - "laminas/laminas-permissions-acl": "^2.8", "charcoal/app": "^5.1", "charcoal/cache": "^5.1", "charcoal/cms": "^5.1", @@ -38,26 +31,25 @@ "charcoal/translator": "^5.1", "charcoal/ui": "^5.1", "charcoal/user": "^5.1", + "guzzlehttp/guzzle": "^6.0 || ^7.0", + "kriswallsmith/assetic": "^1.4", + "laminas/laminas-permissions-acl": "^2.8", "mcaskill/php-html-build-attributes": "^1.0", "psr/cache": "^1.0", "psr/http-message": "^1.0", "psr/log": "^1.0", - "studio-42/elfinder": "2.1.64" + "studio-42/elfinder": "^2.1.64" }, "require-dev": { "league/csv": "^9.5", "mockery/mockery": "^1.0", "mustache/mustache": "^2.11", "php-coveralls/php-coveralls": "^2.2", - "phpunit/phpunit": "^9.5", + "phpunit/phpunit": "^12.5", "seld/jsonlint": "^1.9", "squizlabs/php_codesniffer": "^3.5", "tedivm/stash": "~0.16" }, - "suggest": { - "league/csv": "To use the exporter (to CSV).", - "fabpot/goutte": "To use the various crawler-based tools." - }, "autoload": { "psr-4": { "Charcoal\\Admin\\": "src/Charcoal/Admin/" @@ -68,8 +60,10 @@ "Charcoal\\Tests\\": "tests/Charcoal/" } }, - "replace": { - "locomotivemtl/charcoal-admin": "*" + "extra": { + "branch-alias": { + "dev-main": "5.x-dev" + } }, "scripts": { "test": [ @@ -87,6 +81,16 @@ "phpcbf": "php vendor/bin/phpcbf -ps --colors src/ tests/", "phpunit": "php vendor/bin/phpunit --coverage-text" }, + "suggest": { + "league/csv": "To use the exporter (to CSV).", + "fabpot/goutte": "To use the various crawler-based tools." + }, + "config": { + "sort-packages": true + }, + "replace": { + "locomotivemtl/charcoal-admin": "*" + }, "minimum-stability": "dev", "prefer-stable": true } diff --git a/packages/admin/phpunit.xml.dist b/packages/admin/phpunit.xml.dist index 5c94aaf08..e8377b8c5 100644 --- a/packages/admin/phpunit.xml.dist +++ b/packages/admin/phpunit.xml.dist @@ -1,32 +1,21 @@ - - - - ./tests/Charcoal - - - - - - ./src/Charcoal - - - - - - - - - + + + + ./tests/Charcoal + + + + + + + + + + + + + ./src/Charcoal + + diff --git a/packages/admin/src/Charcoal/Admin/Action/Account/LostPasswordAction.php b/packages/admin/src/Charcoal/Admin/Action/Account/LostPasswordAction.php index 2b4d5dd38..684b9d13b 100644 --- a/packages/admin/src/Charcoal/Admin/Action/Account/LostPasswordAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/Account/LostPasswordAction.php @@ -40,15 +40,11 @@ class LostPasswordAction extends AdminAction { /** * Store the factory instance for the current class. - * - * @var FactoryInterface */ - private $emailFactory; + private ?\Charcoal\Factory\FactoryInterface $emailFactory = null; - /** - * @return boolean - */ - public function authRequired() + #[\Override] + public function authRequired(): bool { return false; } @@ -65,7 +61,7 @@ public function run(RequestInterface $request, ResponseInterface $response) { $translator = $this->translator(); - $ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null; + $ip = $_SERVER['REMOTE_ADDR'] ?? null; $email = $request->getParam('email'); if (!$email) { @@ -160,23 +156,20 @@ public function run(RequestInterface $request, ResponseInterface $response) } } - /** - * @return array - */ - public function results() + #[\Override] + public function results(): array { - $ret = [ + return [ 'success' => $this->success(), 'feedbacks' => $this->feedbacks(), ]; - - return $ret; } /** * @param Container $container Pimple DI Container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -187,13 +180,12 @@ protected function setDependencies(Container $container) * Retrieve the email model factory. * * @throws RuntimeException If the model factory was not previously set. - * @return FactoryInterface */ - protected function emailFactory() + protected function emailFactory(): \Charcoal\Factory\FactoryInterface { - if (!isset($this->emailFactory)) { + if (!$this->emailFactory instanceof \Charcoal\Factory\FactoryInterface) { throw new RuntimeException( - sprintf('Email Factory is not defined for "%s"', get_class($this)) + sprintf('Email Factory is not defined for "%s"', static::class) ); } @@ -204,9 +196,8 @@ protected function emailFactory() * Set an email model factory. * * @param FactoryInterface $factory The email factory, to create emails. - * @return self */ - private function setEmailFactory(FactoryInterface $factory) + private function setEmailFactory(FactoryInterface $factory): static { $this->emailFactory = $factory; @@ -231,9 +222,8 @@ private function generateLostPasswordToken(User $user) * @todo Implement `$container['admin/config']['user.lost_password_email']` * @param User $user The user to send the lost-password email to. * @param string $token The lost-password token, as string. - * @return void */ - private function sendLostPasswordEmail(User $user, $token) + private function sendLostPasswordEmail(User $user, $token): void { $translator = $this->translator(); $userEmail = $user['email']; @@ -268,7 +258,7 @@ private function sendLostPasswordEmail(User $user, $token) 'adminUrl' => $this->adminUrl(), 'urlResetPassword' => $this->adminUrl() . 'account/reset-password/' . $token->id(), 'expiry' => $token->expiry()->format('Y-m-d H:i:s'), - 'ipAddress' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '', + 'ipAddress' => $_SERVER['REMOTE_ADDR'] ?? '', ], ]); $emailObj->send(); diff --git a/packages/admin/src/Charcoal/Admin/Action/Account/ResetPasswordAction.php b/packages/admin/src/Charcoal/Admin/Action/Account/ResetPasswordAction.php index 37dd376ad..7ac70de4c 100644 --- a/packages/admin/src/Charcoal/Admin/Action/Account/ResetPasswordAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/Account/ResetPasswordAction.php @@ -34,10 +34,8 @@ */ class ResetPasswordAction extends AdminAction { - /** - * @return boolean - */ - public function authRequired() + #[\Override] + public function authRequired(): bool { return false; } @@ -55,7 +53,7 @@ public function run(RequestInterface $request, ResponseInterface $response) { $translator = $this->translator(); - $ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null; + $ip = $_SERVER['REMOTE_ADDR'] ?? null; $token = $request->getParam('token'); $email = $request->getParam('email'); @@ -191,17 +189,13 @@ public function run(RequestInterface $request, ResponseInterface $response) return $response; } - /** - * @return array - */ - public function results() + #[\Override] + public function results(): array { - $ret = [ + return [ 'success' => $this->success(), 'feedbacks' => $this->feedbacks(), ]; - - return $ret; } /** @@ -216,9 +210,8 @@ public function results() * @see \Charcoal\Admin\Template\Account::validateToken() * @param string $token The token to validate. * @param string $userId The user ID that should match the token. - * @return boolean */ - private function validateToken($token, $userId) + private function validateToken($token, $userId): bool { $obj = $this->modelFactory()->create(LostPasswordToken::class); $sql = strtr('SELECT * FROM `%table` WHERE `token` = :token AND `user` = :userId AND `expiry` > NOW()', [ @@ -229,16 +222,15 @@ private function validateToken($token, $userId) 'userId' => $userId, ]); - return !!$obj->token(); + return (bool) $obj->token(); } /** * Delete the given password reset token. * * @param string $token The token to delete. - * @return void */ - private function deleteToken($token) + private function deleteToken($token): void { $obj = $this->modelFactory()->create(LostPasswordToken::class); $obj->setToken($token); diff --git a/packages/admin/src/Charcoal/Admin/Action/ElfinderConnectorAction.php b/packages/admin/src/Charcoal/Admin/Action/ElfinderConnectorAction.php index a1b37ca7d..1c027206d 100644 --- a/packages/admin/src/Charcoal/Admin/Action/ElfinderConnectorAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/ElfinderConnectorAction.php @@ -32,6 +32,10 @@ */ class ElfinderConnectorAction extends AdminAction { + /** + * @var \elFinderConnector + */ + public $connector; use CallableResolverAwareTrait; /** @@ -123,9 +127,9 @@ class ElfinderConnectorAction extends AdminAction * Sets the action data from a PSR Request object. * * @param RequestInterface $request A PSR-7 compatible Request instance. - * @return self */ - protected function setDataFromRequest(RequestInterface $request) + #[\Override] + protected function setDataFromRequest(RequestInterface $request): static { $keys = $this->validDataFromRequest(); $data = $request->getParams($keys); @@ -150,7 +154,8 @@ protected function setDataFromRequest(RequestInterface $request) * * @return string[] */ - protected function validDataFromRequest() + #[\Override] + protected function validDataFromRequest(): array { return [ 'obj_type', @@ -162,9 +167,8 @@ protected function validDataFromRequest() /** * @param RequestInterface $request A PSR-7 compatible Request instance. * @param ResponseInterface $response A PSR-7 compatible Response instance. - * @return ResponseInterface */ - public function run(RequestInterface $request, ResponseInterface $response) + public function run(RequestInterface $request, ResponseInterface $response): ResponseInterface { unset($request); @@ -177,9 +181,8 @@ public function run(RequestInterface $request, ResponseInterface $response) * Setup the elFinder connector. * * @param array|null $extraOptions Additional settings to pass to elFinder. - * @return elFinderConnector */ - public function setupElfinder(array $extraOptions = []) + public function setupElfinder(array $extraOptions = []): \elFinderConnector { if (!defined('ELFINDER_IMG_PARENT_URL')) { // Ensure images injected by elFinder are relative to its assets directory @@ -219,9 +222,8 @@ public function getConnectorOptions() * your application's admin configuration. * * @param array $extraOptions Additional settings to pass to elFinder. - * @return array */ - public function buildConnectorOptions(array $extraOptions = []) + public function buildConnectorOptions(array $extraOptions = []): array { $options = [ 'debug' => false, @@ -251,7 +253,7 @@ public function buildConnectorOptions(array $extraOptions = []) * @param array $options2 The settings from which data is extracted. * @return array The merged settings. */ - protected function mergeConnectorOptions(array $options1, array $options2) + protected function mergeConnectorOptions(array $options1, array $options2): array { return array_replace_recursive($options1, $options2); } @@ -262,7 +264,7 @@ protected function mergeConnectorOptions(array $options1, array $options2) * @param array $options The admin settings to parse. * @return array The parsed settings. */ - protected function parseAdminOptionsForConnectorBuild(array $options) + protected function parseAdminOptionsForConnectorBuild(array $options): array { // Root settings are already merged when retrieving available roots. unset($options['roots']); @@ -276,7 +278,7 @@ protected function parseAdminOptionsForConnectorBuild(array $options) * @param array $options The extra settings to parse. * @return array The parsed settings. */ - protected function parseExtraOptionsForConnectorBuild(array $options) + protected function parseExtraOptionsForConnectorBuild(array $options): array { // Resolve callbacks on extra options if (isset($options['roots'])) { @@ -290,10 +292,8 @@ protected function parseExtraOptionsForConnectorBuild(array $options) * Retrieve the admin's elFinder Connector options. * * Path: `config.admin.elfinder.connector` - * - * @return array */ - public function getAdminConnectorOptions() + public function getAdminConnectorOptions(): array { $config = $this->elfinderConfig('connector'); if (!is_array($config)) { @@ -305,10 +305,8 @@ public function getAdminConnectorOptions() /** * Retrieve the default elFinder Connector options. - * - * @return array */ - protected function getDefaultElfinderRootSettings() + protected function getDefaultElfinderRootSettings(): array { return [ 'driver' => 'LocalFileSystem', @@ -322,17 +320,15 @@ protected function getDefaultElfinderRootSettings() 'uploadDeny' => $this->defaultUploadDeny(), 'uploadAllow' => $this->defaultUploadAllow(), 'uploadOrder' => [ 'deny', 'allow' ], - 'accessControl' => [ $this, 'checkAccess' ], + 'accessControl' => $this->checkAccess(...), 'duplicateSuffix' => '_%s_', ]; } /** * Retrieve the default Flysystem / elFinder options. - * - * @return array */ - protected function getDefaultFlysystemRootSettings() + protected function getDefaultFlysystemRootSettings(): array { return [ 'driver' => 'Flysystem', @@ -348,18 +344,13 @@ protected function getDefaultFlysystemRootSettings() * Retrieve the default Flysystem / elFinder options. * * @param string $ident The disk identifier. - * @return array */ - protected function resolveFallbackRootSettings($ident) + protected function resolveFallbackRootSettings($ident): array { $fsConfig = $this->getFilesystemConfig($ident); $uploadPath = $this->defaultUploadPath(); - if (isset($fsConfig['base_url'])) { - $baseUrl = rtrim($fsConfig['base_url'], '/') . '/'; - } else { - $baseUrl = $this->baseUrl(); - } + $baseUrl = isset($fsConfig['base_url']) ? rtrim($fsConfig['base_url'], '/') . '/' : $this->baseUrl(); return [ 'URL' => $baseUrl . '/' . $uploadPath, @@ -378,14 +369,14 @@ protected function resolveFallbackRootSettings($ident) * @param string $ident The disk identifier. * @return array|null Returns an elFinder root structure or NULL. */ - public function getNamedRoot($ident) + public function getNamedRoot($ident): ?array { if ($this->hasFilesystem($ident) === false) { return null; } - $filesystem = $this->getFilesystem($ident); - $fsConfig = $this->getFilesystemConfig($ident); + $this->getFilesystem($ident); + $this->getFilesystemConfig($ident); $elfConfig = $this->getFilesystemAdminConfig($ident); $immutableSettings = [ @@ -410,10 +401,8 @@ public function getNamedRoot($ident) /** * Retrieve only the public elFinder root volumes. - * - * @return array */ - public function getPublicRoots() + public function getPublicRoots(): array { $roots = []; foreach ($this->filesystems->keys() as $ident) { @@ -430,10 +419,8 @@ public function getPublicRoots() /** * Retrieve all elFinder root volumes. - * - * @return array */ - public function getAllRoots() + public function getAllRoots(): array { $roots = []; foreach ($this->filesystems->keys() as $ident) { @@ -452,7 +439,7 @@ public function getAllRoots() * @return array Returns all public root volumes * or a subset if the context has a related form property. */ - public function getCurrentRoots() + public function getCurrentRoots(): array { $formProperty = $this->formProperty(); $targetFilesystem = $formProperty ? $formProperty['filesystem'] : null; @@ -475,9 +462,7 @@ public function getCurrentRoots() if ($acceptedMimetypes) { $disk['uploadAllow'] = array_merge( - isset($disk['uploadAllow']) - ? $disk['uploadAllow'] - : [], + $disk['uploadAllow'] ?? [], $acceptedMimetypes ); } @@ -493,7 +478,7 @@ public function getCurrentRoots() * @param array $roots One or many roots with possible unresolved callables. * @return array Returns the root(s) with resolved callables. */ - protected function resolveCallbacksForRoots(array $roots) + protected function resolveCallbacksForRoots(array $roots): array { foreach ($roots as $i => $root) { $roots[$i] = $this->resolveCallbacksForRoot($root); @@ -508,7 +493,7 @@ protected function resolveCallbacksForRoots(array $roots) * @param array $root A root structure with possible unresolved callables. * @return array Returns the root with resolved callables. */ - protected function resolveCallbacksForRoot(array $root) + protected function resolveCallbacksForRoot(array $root): array { if (isset($root['accessControl'])) { $callable = $root['accessControl']; @@ -526,14 +511,14 @@ protected function resolveCallbacksForRoot(array $root) * @param array $toResolve One or many pairs of callbacks. * @return array Returns the parsed event listeners. */ - protected function resolveCallbacksForBindOption(array $toResolve) + protected function resolveCallbacksForBindOption(array $toResolve): array { $resolved = $toResolve; foreach ($toResolve as $actions => $callables) { foreach ($callables as $i => $callable) { if (!is_callable($callable) && is_string($callable)) { - if (0 === strpos($callable, 'Plugin.')) { + if (str_starts_with($callable, 'Plugin.')) { continue; } @@ -557,9 +542,8 @@ protected function resolveCallbacksForBindOption(array $toResolve) * @param array $args The command arguments from the client. * @param object $elfinder The elFinder instance. * @param object $volume The current volume instance. - * @return void|bool|array */ - public function translateDirectoriesOnAnyCommand($cmd, &$result, $args, $elfinder, $volume) + public function translateDirectoriesOnAnyCommand($cmd, array &$result, $args, object $elfinder, $volume): void { // To please PHPCS unset($cmd, $args, $volume); @@ -587,7 +571,6 @@ public function translateDirectoriesOnAnyCommand($cmd, &$result, $args, $elfinde * @param array $stat The directory reference. * @param object $elfinder The elFinder instance or volume instance. * @throws UnexpectedValueException If the related volume is not found. - * @return array */ protected function translateDirectoryStat(array $stat, object $elfinder): array { @@ -608,7 +591,6 @@ protected function translateDirectoryStat(array $stat, object $elfinder): array * @param object $elfinder The elFinder instance or volume instance. * @throws InvalidArgumentException If the elFinder client or volume is not provided. * @throws UnexpectedValueException If the related volume is not found. - * @return ?string */ protected function getVolumeNameFromHash(string $hash, object $elfinder): ?string { @@ -635,7 +617,6 @@ protected function getVolumeNameFromHash(string $hash, object $elfinder): ?strin * Attempts to retrieve the filesystem name. * * @param string $ident The filesystem identifier. - * @return ?string */ protected function getFilesystemName(string $ident): ?string { @@ -655,7 +636,6 @@ protected function getFilesystemName(string $ident): ?string * Attempts to localize the filesystem identifier. * * @param string $ident The filesystem identifier. - * @return ?string */ protected function translateFilesystemName(string $ident): ?string { @@ -684,7 +664,7 @@ protected function translateFilesystemName(string $ident): ?string * @param object $volume The current volume instance. * @return void|bool|array */ - public function sanitizeOnUploadPreSave(&$path, &$name, $src, $elfinder, $volume) + public function sanitizeOnUploadPreSave(&$path, &$name, $src, $elfinder, $volume): bool { // To please PHPCS unset($path, $src, $elfinder, $volume); @@ -712,11 +692,7 @@ public function sanitizeOnUploadPreSave(&$path, &$name, $src, $elfinder, $volume */ protected function sanitizeFileName(string $name, array $options): string { - if (is_array($options['replace'])) { - $mask = implode($options['replace']); - } else { - $mask = $options['replace']; - } + $mask = is_array($options['replace']) ? implode('', $options['replace']) : $options['replace']; $ext = '.' . pathinfo($name, PATHINFO_EXTENSION); @@ -725,8 +701,8 @@ protected function sanitizeFileName(string $name, array $options): string // Squeeze multiple delimiters and whitespace with a single separator $name = preg_replace( - '![' . preg_quote($mask, '!') . '\.\s]{2,}!', - $options['replace'], + '![' . preg_quote((string) $mask, '!') . '\.\s]{2,}!', + (string) $options['replace'], $name ); @@ -751,7 +727,7 @@ protected function sanitizeFileName(string $name, array $options): string * started with directory separator. * @return boolean|null TRUE to allow, FALSE to deny, NULL to let elFinder decide. **/ - public function checkAccess($attr, $path, $data, elFinderVolumeDriver $volume, $isDir, $relPath) + public function checkAccess($attr, $path, $data, elFinderVolumeDriver $volume, $isDir, $relPath): ?bool { unset($data, $volume, $isDir); @@ -763,7 +739,7 @@ public function checkAccess($attr, $path, $data, elFinderVolumeDriver $volume, $ * set to NULL to let elFinder decide itself. */ return ($basename[0] === '.' && strlen($relPath) !== 1) - ? !($attr === 'read' || $attr === 'write') + ? $attr !== 'read' && $attr !== 'write' : null; } @@ -825,20 +801,16 @@ public function formProperty() /** * Retrieve the default root path. - * - * @return string */ - public function defaultUploadPath() + public function defaultUploadPath(): string { return self::DEFAULT_STORAGE_PATH; } /** * Allow upload for a subset MIME types. - * - * @return array */ - protected function defaultUploadAllow() + protected function defaultUploadAllow(): array { // By default, all images, PDF, and plain-text files are allowed. return [ @@ -850,10 +822,8 @@ protected function defaultUploadAllow() /** * Deny upload for all MIME types. - * - * @return array */ - protected function defaultUploadDeny() + protected function defaultUploadDeny(): array { // By default, all files are rejected. return [ @@ -863,10 +833,8 @@ protected function defaultUploadDeny() /** * Default attributes for files and directories. - * - * @return array */ - protected function attributesForHiddenFiles() + protected function attributesForHiddenFiles(): array { return [ // Block access to all hidden files and directories (anything starting with ".") @@ -882,9 +850,9 @@ protected function attributesForHiddenFiles() * Inject dependencies from a DI Container. * * @param Container $container A dependencies container instance. - * @return void */ - public function setDependencies(Container $container) + #[\Override] + public function setDependencies(Container $container): void { parent::setDependencies($container); @@ -908,11 +876,7 @@ public function setDependencies(Container $container) */ protected function getFilesystem($ident) { - if (isset($this->filesystems[$ident])) { - return $this->filesystems[$ident]; - } - - return null; + return $this->filesystems[$ident] ?? null; } /** @@ -921,7 +885,7 @@ protected function getFilesystem($ident) * @param string $ident The filesystem identifier. * @return boolean TRUE if the filesystem instance exists, otherwise FALSE. */ - protected function hasFilesystem($ident) + protected function hasFilesystem($ident): bool { return ($this->getFilesystem($ident) !== null); } @@ -939,11 +903,7 @@ protected function getFilesystemConfig($ident) return null; } - if (isset($this->filesystemConfig['connections'][$ident])) { - return $this->filesystemConfig['connections'][$ident]; - } - - return []; + return $this->filesystemConfig['connections'][$ident] ?? []; } /** @@ -959,11 +919,7 @@ protected function isFilesystemPublic($ident) } $config = $this->getFilesystemConfig($ident); - if (isset($config['public']) && $config['public'] === false) { - return false; - } - - return true; + return !(isset($config['public']) && $config['public'] === false); } /** @@ -980,11 +936,8 @@ protected function getFilesystemAdminConfig($ident) } $elfConfig = $this->getAdminConnectorOptions(); - if (isset($elfConfig['roots'][$ident])) { - return $elfConfig['roots'][$ident]; - } - return []; + return $elfConfig['roots'][$ident] ?? []; } /** @@ -1010,12 +963,10 @@ protected function elfinderConfig($key = null, $default = null) if ($key) { if (isset($this->elfinderConfig[$key])) { return $this->elfinderConfig[$key]; + } elseif (!is_string($default) && is_callable($default)) { + return $default(); } else { - if (!is_string($default) && is_callable($default)) { - return $default(); - } else { - return $default; - } + return $default; } } diff --git a/packages/admin/src/Charcoal/Admin/Action/Filesystem/LoadAction.php b/packages/admin/src/Charcoal/Admin/Action/Filesystem/LoadAction.php index dbaebc91f..0c15dcdde 100644 --- a/packages/admin/src/Charcoal/Admin/Action/Filesystem/LoadAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/Filesystem/LoadAction.php @@ -72,10 +72,9 @@ class LoadAction extends AdminAction /** * Determine if user authentication is required. - * - * @return boolean */ - protected function authRequired() + #[\Override] + protected function authRequired(): bool { return true; } @@ -84,9 +83,9 @@ protected function authRequired() * Sets the action data. * * @param array $data The action data. - * @return self */ - public function setData(array $data) + #[\Override] + public function setData(array $data): static { $keys = $this->validDataFromRequest(); $data = array_intersect_key($data, array_flip($keys)); @@ -99,9 +98,9 @@ public function setData(array $data) * Sets the action data from a PSR Request object. * * @param RequestInterface $request A PSR-7 compatible Request instance. - * @return self */ - protected function setDataFromRequest(RequestInterface $request) + #[\Override] + protected function setDataFromRequest(RequestInterface $request): static { $keys = $this->validDataFromRequest(); $data = $request->getParams($keys); @@ -114,9 +113,8 @@ protected function setDataFromRequest(RequestInterface $request) * Add data to action, replacing existing items with the same data key. * * @param array $data The action data. - * @return self */ - public function mergeData(array $data) + public function mergeData(array $data): static { $this->params = array_replace($this->params, $data); @@ -128,7 +126,8 @@ public function mergeData(array $data) * * @return string[] */ - protected function validDataFromRequest() + #[\Override] + protected function validDataFromRequest(): array { return [ 'disk', 'disposition', 'path', 'name' ]; } @@ -139,14 +138,14 @@ protected function validDataFromRequest() * @param array|null $keys Subset of keys to retrieve. * @return array|null */ - public function getParams(array $keys = null) + public function getParams(?array $keys = null) { $params = $this->params; if ($keys) { $subset = []; foreach ($keys as $key) { - if (array_key_exists($key, $params)) { + if (array_key_exists((string) $key, $params)) { $subset[$key] = $params[$key]; } } @@ -168,12 +167,10 @@ public function getParam($key, $default = null) $params = $this->params; if (is_array($params) && isset($params[$key])) { $result = $params[$key]; + } elseif (!is_string($default) && is_callable($default)) { + $result = $default(); } else { - if (!is_string($default) && is_callable($default)) { - $result = $default(); - } else { - $result = $default; - } + $result = $default; } return $result; @@ -189,7 +186,7 @@ public function run(RequestInterface $request, ResponseInterface $response) { unset($request); - $translator = $this->translator(); + $this->translator(); try { $disk = $this->getParam('disk', $this->filesystemConfig['default_connection']); @@ -225,7 +222,7 @@ public function run(RequestInterface $request, ResponseInterface $response) } try { - $filename = isset($name) ? $name : basename($path); + $filename = $name ?? basename((string) $path); $disposition = $this->generateHttpDisposition($disp, $filename); $resource = $handler->readStream(); $stream = new Stream($resource); @@ -276,7 +273,7 @@ public function run(RequestInterface $request, ResponseInterface $response) * @throws InvalidArgumentException If the parameters are invalid. * @return string A string suitable for use as a Content-Disposition field-value. */ - public function generateHttpDisposition($disposition, $filename, $filenameFallback = '') + public function generateHttpDisposition($disposition, $filename, $filenameFallback = ''): string { if (!in_array($disposition, [ self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE ])) { throw new InvalidArgumentException(sprintf( @@ -296,16 +293,16 @@ public function generateHttpDisposition($disposition, $filename, $filenameFallba // } // percent characters aren't safe in fallback. - if (strpos($filenameFallback, '%') !== false) { + if (str_contains($filenameFallback, '%')) { throw new InvalidArgumentException('The filename fallback cannot contain the "%" character.'); } // path separators aren't allowed in either. if ( - strpos($filename, '/') !== false || - strpos($filename, '\\') !== false || - strpos($filenameFallback, '/') !== false || - strpos($filenameFallback, '\\') !== false + str_contains($filename, '/') || + str_contains($filename, '\\') || + str_contains($filenameFallback, '/') || + str_contains($filenameFallback, '\\') ) { throw new InvalidArgumentException( 'The filename and the fallback cannot contain the "/" and "\\" characters.' @@ -344,7 +341,7 @@ protected function assertValidDisk($disk) } if (!is_string($disk)) { - $actualType = is_object($disk) ? get_class($disk) : gettype($disk); + $actualType = get_debug_type($disk); $message = $translator->translate( '{{ parameter }} must be a {{ expectedType }}, received {{ actualType }}', [ @@ -388,7 +385,7 @@ protected function assertValidPath($path) throw new InvalidArgumentException($message, 400); } elseif (!is_string($path)) { - $actualType = is_object($path) ? get_class($path) : gettype($path); + $actualType = get_debug_type($path); $message = $translator->translate( '{{ parameter }} must be a {{ expectedType }}, received {{ actualType }}', [ @@ -412,7 +409,7 @@ protected function assertValidPath($path) protected function assertValidName($name) { if (!is_string($name) && $name !== null) { - $actualType = is_object($name) ? get_class($name) : gettype($name); + $actualType = get_debug_type($name); $message = $this->translator()->translate( '{{ parameter }} must be a {{ expectedType }}, received {{ actualType }}', [ @@ -446,7 +443,7 @@ protected function assertValidDisposition($disposition) } if (!is_string($disposition) && $disposition !== null) { - $actualType = is_object($disposition) ? get_class($disposition) : gettype($disposition); + $actualType = get_debug_type($disposition); $message = $translator->translate( '{{ parameter }} must be a {{ expectedType }}, received {{ actualType }}', [ @@ -466,6 +463,7 @@ protected function assertValidDisposition($disposition) * @param Container $container A service locator. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); diff --git a/packages/admin/src/Charcoal/Admin/Action/LoginAction.php b/packages/admin/src/Charcoal/Admin/Action/LoginAction.php index af3adbee4..feb42a76b 100644 --- a/packages/admin/src/Charcoal/Admin/Action/LoginAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/LoginAction.php @@ -41,10 +41,9 @@ class LoginAction extends AdminAction * Authentication is required by default. * * Change to false in the login action controller; this is meant to be called before login. - * - * @return boolean */ - public function authRequired() + #[\Override] + public function authRequired(): bool { return false; } @@ -65,7 +64,7 @@ public function run(RequestInterface $request, ResponseInterface $response) '{{ errorMessage }}' => $failMessage ]); - $ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null; + $ip = $_SERVER['REMOTE_ADDR'] ?? null; $email = $request->getParam('email'); $password = $request->getParam('password'); @@ -125,8 +124,8 @@ public function run(RequestInterface $request, ResponseInterface $response) $this->addFeedback('success', $doneMessage); $this->setSuccess(true); - if (is_string($nextUrl) && !empty($nextUrl)) { - $this->setSuccessUrl((string)$nextUrl); + if (is_string($nextUrl) && ($nextUrl !== '' && $nextUrl !== '0')) { + $this->setSuccessUrl($nextUrl); } else { $this->setSuccessUrl((string)$this->adminUrl()); } diff --git a/packages/admin/src/Charcoal/Admin/Action/LogoutAction.php b/packages/admin/src/Charcoal/Admin/Action/LogoutAction.php index e521ed2b6..1a2c8e7ac 100644 --- a/packages/admin/src/Charcoal/Admin/Action/LogoutAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/LogoutAction.php @@ -62,7 +62,7 @@ public function run(RequestInterface $request, ResponseInterface $response) } /** Fail silently — Never confirm or deny the existence of an account. */ - $ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null; + $ip = $_SERVER['REMOTE_ADDR'] ?? null; if ($ip) { $logMessage = sprintf('[Admin] Logout attempt for unauthenticated user from %s', $ip); } else { @@ -78,9 +78,9 @@ public function run(RequestInterface $request, ResponseInterface $response) /** * @todo Provide feedback and redirection? - * @return array */ - public function results() + #[\Override] + public function results(): array { return [ 'success' => $this->success(), diff --git a/packages/admin/src/Charcoal/Admin/Action/Object/AbstractSaveAction.php b/packages/admin/src/Charcoal/Admin/Action/Object/AbstractSaveAction.php index 48a232e56..078664a3a 100644 --- a/packages/admin/src/Charcoal/Admin/Action/Object/AbstractSaveAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/Object/AbstractSaveAction.php @@ -43,11 +43,7 @@ public function setObj($obj) { $this->obj = $obj; - if ($obj instanceof ModelInterface) { - $this->objId = $obj->id(); - } else { - $this->objId = null; - } + $this->objId = $obj instanceof ModelInterface ? $obj->id() : null; return $this; } @@ -145,7 +141,7 @@ public function addFeedbackFromModelValidatorResult(ValidatorResult $result, Mod $propertyLabel = (string)$obj->property($prop)['label']; $resultMessage = $result->message(); - if (strpos($resultMessage, $propertyLabel) === false) { + if (!str_contains((string) $resultMessage, $propertyLabel)) { $resultMessage = strtr($this->translator()->translation('{{ errorMessage }}: {{ errorThrown }}'), [ '{{ errorMessage }}' => $propertyLabel, '{{ errorThrown }}' => $resultMessage, @@ -161,6 +157,7 @@ public function addFeedbackFromModelValidatorResult(ValidatorResult $result, Mod /** * @return array */ + #[\Override] public function results() { $results = [ diff --git a/packages/admin/src/Charcoal/Admin/Action/Object/DeleteAction.php b/packages/admin/src/Charcoal/Admin/Action/Object/DeleteAction.php index da13446d5..bb342f003 100644 --- a/packages/admin/src/Charcoal/Admin/Action/Object/DeleteAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/Object/DeleteAction.php @@ -58,7 +58,7 @@ public function run(RequestInterface $request, ResponseInterface $response) $objId = $request->getParam('obj_id'); if (!$objType) { - $actualType = is_object($objType) ? get_class($objType) : gettype($objType); + $actualType = get_debug_type($objType); $this->addFeedback('error', strtr($reqMessage, [ '{{ parameter }}' => '"obj_type"', '{{ expectedType }}' => 'string', @@ -70,7 +70,7 @@ public function run(RequestInterface $request, ResponseInterface $response) } if (!$objId) { - $actualType = is_object($objId) ? get_class($objId) : gettype($objId); + $actualType = get_debug_type($objId); $this->addFeedback('error', strtr($reqMessage, [ '{{ parameter }}' => '"obj_id"', '{{ expectedType }}' => 'string or numeric', @@ -124,10 +124,8 @@ public function run(RequestInterface $request, ResponseInterface $response) } } - /** - * @return array - */ - public function results() + #[\Override] + public function results(): array { return [ 'success' => $this->success(), diff --git a/packages/admin/src/Charcoal/Admin/Action/Object/ExportAction.php b/packages/admin/src/Charcoal/Admin/Action/Object/ExportAction.php index 89fcaba8a..a6bee833d 100644 --- a/packages/admin/src/Charcoal/Admin/Action/Object/ExportAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/Object/ExportAction.php @@ -53,7 +53,7 @@ public function run(RequestInterface $request, ResponseInterface $response) set_time_limit(0); $failMessage = $this->translator()->translation('Failed to export object(s)'); - $errorThrown = strtr($this->translator()->translation('{{ errorMessage }}: {{ errorThrown }}'), [ + strtr($this->translator()->translation('{{ errorMessage }}: {{ errorThrown }}'), [ '{{ errorMessage }}' => $failMessage ]); $reqMessage = $this->translator()->translation( @@ -67,7 +67,7 @@ public function run(RequestInterface $request, ResponseInterface $response) $exportIdent = $request->getParam('ident'); if (!$objType) { - $actualType = is_object($objType) ? get_class($objType) : gettype($objType); + $actualType = get_debug_type($objType); $this->addFeedback('error', strtr($reqMessage, [ '{{ parameter }}' => '"obj_type"', '{{ expectedType }}' => 'string', @@ -91,7 +91,7 @@ public function run(RequestInterface $request, ResponseInterface $response) if (isset($exportIdent)) { if (!is_string($exportIdent)) { - $actualType = is_object($exportIdent) ? get_class($exportIdent) : gettype($exportIdent); + $actualType = get_debug_type($exportIdent); $this->addFeedback('error', strtr($typeMessage, [ '{{ parameter }}' => 'Export "ident"', '{{ expectedType }}' => 'string', @@ -113,10 +113,8 @@ public function run(RequestInterface $request, ResponseInterface $response) return $response; } - /** - * @return array - */ - public function results() + #[\Override] + public function results(): array { return [ 'success' => $this->success(), @@ -130,6 +128,7 @@ public function results() * @param Container $container A dependencies container instance. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); diff --git a/packages/admin/src/Charcoal/Admin/Action/Object/LoadAction.php b/packages/admin/src/Charcoal/Admin/Action/Object/LoadAction.php index 92c7d42fb..05f2326f0 100644 --- a/packages/admin/src/Charcoal/Admin/Action/Object/LoadAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/Object/LoadAction.php @@ -43,17 +43,8 @@ class LoadAction extends AdminAction { /** * Store the collection loader for the current class. - * - * @var CollectionLoader */ - private $collectionLoader; - - /** - * Store the factory instance for the current class. - * - * @var \Charcoal\Factory\FactoryInterface - */ - private $modelFactory; + private ?\Charcoal\Loader\CollectionLoader $collectionLoader = null; /** * @var string @@ -70,7 +61,8 @@ class LoadAction extends AdminAction * * @return string[] */ - protected function validDataFromRequest() + #[\Override] + protected function validDataFromRequest(): array { return array_merge([ 'obj_type', 'obj_id' @@ -107,7 +99,7 @@ public function run(RequestInterface $request, ResponseInterface $response) } if (!$objType) { - $actualType = is_object($objType) ? get_class($objType) : gettype($objType); + $actualType = get_debug_type($objType); $this->addFeedback('error', strtr($reqMessage, [ '{{ parameter }}' => '"obj_type"', '{{ expectedType }}' => 'string', @@ -125,21 +117,13 @@ public function run(RequestInterface $request, ResponseInterface $response) $this->loadObjectCollection($objType); $count = count($this->objCollection); - switch ($count) { - case 0: - $doneMessage = $this->translator()->translation('No objects found.'); - break; - - case 1: - $doneMessage = $this->translator()->translation('One object found.'); - break; - - default: - $doneMessage = strtr($this->translator()->translation('{{ count }} objects found.'), [ - '{{ count }}' => $count - ]); - break; - } + $doneMessage = match ($count) { + 0 => $this->translator()->translation('No objects found.'), + 1 => $this->translator()->translation('One object found.'), + default => strtr($this->translator()->translation('{{ count }} objects found.'), [ + '{{ count }}' => $count + ]), + }; $this->addFeedback('success', $doneMessage); $this->setSuccess(true); @@ -157,7 +141,7 @@ public function run(RequestInterface $request, ResponseInterface $response) /** * @return array The object collection parsed as array */ - public function objCollection() + public function objCollection(): array { if (!$this->objCollection) { return []; @@ -170,14 +154,13 @@ public function objCollection() * Retrieve the model collection loader. * * @throws RuntimeException If the collection loader was not previously set. - * @return CollectionLoader */ - public function collectionLoader() + public function collectionLoader(): \Charcoal\Loader\CollectionLoader { - if (!isset($this->collectionLoader)) { + if (!$this->collectionLoader instanceof \Charcoal\Loader\CollectionLoader) { throw new RuntimeException(sprintf( 'Collection Loader is not defined for "%s"', - get_class($this) + static::class )); } @@ -195,14 +178,13 @@ public function objType() /** * @param string $objType The object type as string. * @throws InvalidArgumentException If the object type is not a string. - * @return self */ - public function setObjType($objType) + public function setObjType($objType): static { if (!is_string($objType)) { throw new InvalidArgumentException(sprintf( 'Object type must be a string, received %s', - is_object($objType) ? get_class($objType) : gettype($objType) + get_debug_type($objType) )); } @@ -211,10 +193,8 @@ public function setObjType($objType) return $this; } - /** - * @return array - */ - public function results() + #[\Override] + public function results(): array { return [ 'success' => $this->success(), @@ -227,6 +207,7 @@ public function results() * @param Container $container DI Container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -240,7 +221,7 @@ protected function setDependencies(Container $container) * @param string $objType The object type as string. * @return \Charcoal\Model\Collection */ - protected function loadObjectCollection($objType) + protected function loadObjectCollection($objType): \ArrayAccess|array { $proto = $this->modelFactory()->get($objType); $loader = $this->collectionLoader(); @@ -256,9 +237,8 @@ protected function loadObjectCollection($objType) * Set a model collection loader. * * @param CollectionLoader $loader The collection loader. - * @return self */ - protected function setCollectionLoader(CollectionLoader $loader) + protected function setCollectionLoader(CollectionLoader $loader): static { $this->collectionLoader = $loader; diff --git a/packages/admin/src/Charcoal/Admin/Action/Object/ReorderAction.php b/packages/admin/src/Charcoal/Admin/Action/Object/ReorderAction.php index 1b0a4b6c7..b9718e773 100644 --- a/packages/admin/src/Charcoal/Admin/Action/Object/ReorderAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/Object/ReorderAction.php @@ -45,7 +45,8 @@ class ReorderAction extends AdminAction implements ObjectContainerInterface * * @return string[] */ - protected function validDataFromRequest() + #[\Override] + protected function validDataFromRequest(): array { return array_merge([ 'obj_type', 'obj_id' @@ -79,7 +80,7 @@ public function run(RequestInterface $request, ResponseInterface $response) try { if (!$objType) { - $actualType = is_object($objType) ? get_class($objType) : gettype($objType); + $actualType = get_debug_type($objType); $this->addFeedback('error', strtr($reqMessage, [ '{{ parameter }}' => '"obj_type"', '{{ expectedType }}' => 'string', @@ -95,7 +96,7 @@ public function run(RequestInterface $request, ResponseInterface $response) $this->setObjType($objType); if (!$objOrders || !is_array($objOrders)) { - $actualType = is_object($objOrders) ? get_class($objOrders) : gettype($objOrders); + $actualType = get_debug_type($objOrders); $this->addFeedback('error', strtr($reqMessage, [ '{{ parameter }}' => '"obj_orders"', '{{ expectedType }}' => 'array of object IDs', @@ -107,7 +108,7 @@ public function run(RequestInterface $request, ResponseInterface $response) } if (!is_string($orderProperty)) { - $actualType = is_object($orderProperty) ? get_class($orderProperty) : gettype($orderProperty); + $actualType = get_debug_type($orderProperty); $this->addFeedback('error', strtr($typeMessage, [ '{{ parameter }}' => '"obj_property"', '{{ expectedType }}' => 'string', @@ -166,10 +167,8 @@ public function run(RequestInterface $request, ResponseInterface $response) } } - /** - * @return array - */ - public function results() + #[\Override] + public function results(): array { return [ 'success' => $this->success(), @@ -181,6 +180,7 @@ public function results() * @param Container $container A DI Container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); diff --git a/packages/admin/src/Charcoal/Admin/Action/Object/RevertRevisionAction.php b/packages/admin/src/Charcoal/Admin/Action/Object/RevertRevisionAction.php index 3b320b310..92d2b7119 100644 --- a/packages/admin/src/Charcoal/Admin/Action/Object/RevertRevisionAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/Object/RevertRevisionAction.php @@ -50,7 +50,8 @@ class RevertRevisionAction extends AdminAction implements ObjectContainerInterfa * * @return string[] */ - protected function validDataFromRequest() + #[\Override] + protected function validDataFromRequest(): array { return array_merge([ 'obj_type', @@ -66,12 +67,12 @@ protected function validDataFromRequest() * @throws InvalidArgumentException If the given revision is invalid. * @return ObjectContainerInterface Chainable */ - protected function setRevNum($revNum) + protected function setRevNum($revNum): static { if (!is_numeric($revNum)) { throw new InvalidArgumentException(sprintf( 'Revision must be an integer, received %s.', - (is_object($revNum) ? get_class($revNum) : gettype($revNum)) + (get_debug_type($revNum)) )); } diff --git a/packages/admin/src/Charcoal/Admin/Action/Object/SaveAction.php b/packages/admin/src/Charcoal/Admin/Action/Object/SaveAction.php index f87f55736..18ce8b44e 100644 --- a/packages/admin/src/Charcoal/Admin/Action/Object/SaveAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/Object/SaveAction.php @@ -51,9 +51,9 @@ class SaveAction extends AbstractSaveAction * This {@see self::$saveData subset} is merged onto the target model. * * @param RequestInterface $request A PSR-7 compatible Request instance. - * @return self */ - protected function setDataFromRequest(RequestInterface $request) + #[\Override] + protected function setDataFromRequest(RequestInterface $request): static { parent::setDataFromRequest($request); @@ -69,7 +69,8 @@ protected function setDataFromRequest(RequestInterface $request) * * @return string[] */ - protected function validDataFromRequest() + #[\Override] + protected function validDataFromRequest(): array { return array_merge([ 'obj_type' @@ -80,9 +81,8 @@ protected function validDataFromRequest() * Filter the dataset used to create the target model. * * @param array $data The save data to filter. - * @return array */ - public function filterSaveData(array $data) + public function filterSaveData(array $data): array { unset( $data['widget_id'], @@ -102,7 +102,7 @@ public function filterSaveData(array $data) * @param array $data The save data. * @return SaveAction Chainable */ - public function setSaveData(array $data) + public function setSaveData(array $data): static { $this->saveData = $data; @@ -123,23 +123,22 @@ public function getSaveData() * @param ModelInterface $obj The object to validate. * @return boolean */ + #[\Override] public function validate(ModelInterface $obj) { $this->parsePrimaryKey($obj); - $result = parent::validate($obj); - return $result; + return parent::validate($obj); } /** * Prepare the primary key for the object. * * @param ModelInterface $obj The object to validate. - * @return void */ - public function parsePrimaryKey(ModelInterface $obj) + public function parsePrimaryKey(ModelInterface $obj): void { - if (($obj instanceof StorableInterface) && ($obj instanceof DescribablePropertyInterface)) { + if ($obj instanceof DescribablePropertyInterface) { $pk = $obj->key(); $id = $obj[$pk]; @@ -168,7 +167,7 @@ public function run(RequestInterface $request, ResponseInterface $response) $objType = $request->getParam('obj_type'); if (!$objType) { - $actualType = is_object($objType) ? get_class($objType) : gettype($objType); + $actualType = get_debug_type($objType); $this->addFeedback('error', strtr($reqMessage, [ '{{ parameter }}' => '"obj_type"', '{{ expectedType }}' => 'string', @@ -232,11 +231,7 @@ public function run(RequestInterface $request, ResponseInterface $response) } catch (PDOException $e) { $this->setObj(null); - if (isset($e->errorInfo[2])) { - $message = $e->errorInfo[2]; - } else { - $message = $e->getMessage(); - } + $message = $e->errorInfo[2] ?? $e->getMessage(); $this->addFeedback('error', strtr($errorThrown, [ '{{ errorThrown }}' => $message diff --git a/packages/admin/src/Charcoal/Admin/Action/Object/UpdateAction.php b/packages/admin/src/Charcoal/Admin/Action/Object/UpdateAction.php index 1136dd539..283800deb 100644 --- a/packages/admin/src/Charcoal/Admin/Action/Object/UpdateAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/Object/UpdateAction.php @@ -45,9 +45,9 @@ class UpdateAction extends AbstractSaveAction * This {@see self::$updateData subset} is merged onto the target model. * * @param RequestInterface $request A PSR-7 compatible Request instance. - * @return self */ - protected function setDataFromRequest(RequestInterface $request) + #[\Override] + protected function setDataFromRequest(RequestInterface $request): static { parent::setDataFromRequest($request); @@ -63,7 +63,8 @@ protected function setDataFromRequest(RequestInterface $request) * * @return string[] */ - protected function validDataFromRequest() + #[\Override] + protected function validDataFromRequest(): array { return array_merge([ 'obj_type', 'obj_id' @@ -74,9 +75,8 @@ protected function validDataFromRequest() * Filter the dataset used to update the target model. * * @param array $data The update data to filter. - * @return array */ - public function filterUpdateData(array $data) + public function filterUpdateData(array $data): array { unset( $data['widget_id'], @@ -98,7 +98,7 @@ public function filterUpdateData(array $data) * @param array $data The update data. * @return UpdateAction Chainable */ - public function setUpdateData(array $data) + public function setUpdateData(array $data): static { $this->updateData = $data; @@ -138,7 +138,7 @@ public function run(RequestInterface $request, ResponseInterface $response) $objId = $request->getParam('obj_id'); if (!$objType) { - $actualType = is_object($objType) ? get_class($objType) : gettype($objType); + $actualType = get_debug_type($objType); $this->addFeedback('error', strtr($reqMessage, [ '{{ parameter }}' => '"obj_type"', '{{ expectedType }}' => 'string', @@ -150,7 +150,7 @@ public function run(RequestInterface $request, ResponseInterface $response) } if (!$objId) { - $actualType = is_object($objId) ? get_class($objId) : gettype($objId); + $actualType = get_debug_type($objId); $this->addFeedback('error', strtr($reqMessage, [ '{{ parameter }}' => '"obj_id"', '{{ expectedType }}' => 'ID', diff --git a/packages/admin/src/Charcoal/Admin/Action/Selectize/LoadAction.php b/packages/admin/src/Charcoal/Admin/Action/Selectize/LoadAction.php index 8ae9d176a..f8892afc2 100644 --- a/packages/admin/src/Charcoal/Admin/Action/Selectize/LoadAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/Selectize/LoadAction.php @@ -36,7 +36,8 @@ class LoadAction extends BaseLoadAction * * @return string[] */ - protected function validDataFromRequest() + #[\Override] + protected function validDataFromRequest(): array { return array_merge([ 'selectize_prop_ident', 'selectize_property' @@ -50,6 +51,7 @@ protected function validDataFromRequest() * @throws UnexpectedValueException If "obj_id" is passed as $request option. * @todo Implement obj_id support for load object action */ + #[\Override] public function run(RequestInterface $request, ResponseInterface $response) { unset($request); @@ -75,14 +77,14 @@ public function run(RequestInterface $request, ResponseInterface $response) $searchProperties = (array)$options['searchProperties']; } elseif ( !empty($choiceMap['label']) && - strpos($choiceMap['label'], '{{') === false + !str_contains((string) $choiceMap['label'], '{{') ) { $searchProperties = [ $choiceMap['label'] ]; } else { $searchProperties = []; } - if ($searchProperties) { + if ($searchProperties !== []) { $search = [ 'conjunction' => 'OR', 'conditions' => [], @@ -97,7 +99,7 @@ public function run(RequestInterface $request, ResponseInterface $response) $filters = $property->filters(); if (is_array($filters)) { - array_push($filters, $search); + $filters[] = $search; } else { $filters = [ $search ]; } @@ -111,21 +113,13 @@ public function run(RequestInterface $request, ResponseInterface $response) $this->setSelectizeCollection($this->selectizeVal($choices)); $count = count($choices); - switch ($count) { - case 0: - $doneMessage = $this->translator()->translation('No objects found.'); - break; - - case 1: - $doneMessage = $this->translator()->translation('One object found.'); - break; - - default: - $doneMessage = strtr($this->translator()->translation('{{ count }} objects found.'), [ - '{{ count }}' => $count - ]); - break; - } + $doneMessage = match ($count) { + 0 => $this->translator()->translation('No objects found.'), + 1 => $this->translator()->translation('One object found.'), + default => strtr($this->translator()->translation('{{ count }} objects found.'), [ + '{{ count }}' => $count + ]), + }; $this->addFeedback('success', $doneMessage); $this->setSuccess(true); @@ -150,9 +144,8 @@ public function query() /** * @param string $query Query for LoadAction. - * @return self */ - public function setQuery($query) + public function setQuery($query): static { $this->query = $query; @@ -169,19 +162,16 @@ public function selectizeCollection() /** * @param array|mixed $selectizeCollection The collection to return. - * @return self */ - public function setSelectizeCollection($selectizeCollection) + public function setSelectizeCollection($selectizeCollection): static { $this->selectizeCollection = $selectizeCollection; return $this; } - /** - * @return array - */ - public function results() + #[\Override] + public function results(): array { return [ 'success' => $this->success(), @@ -195,6 +185,7 @@ public function results() * @param Container $container DI Container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); diff --git a/packages/admin/src/Charcoal/Admin/Action/Selectize/SaveAction.php b/packages/admin/src/Charcoal/Admin/Action/Selectize/SaveAction.php index 6d6f4a7c5..04b08e5b2 100644 --- a/packages/admin/src/Charcoal/Admin/Action/Selectize/SaveAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/Selectize/SaveAction.php @@ -20,7 +20,8 @@ class SaveAction extends BaseSaveAction * * @return string[] */ - protected function validDataFromRequest() + #[\Override] + protected function validDataFromRequest(): array { return array_merge([ 'selectize_obj_type', 'selectize_prop_ident', 'selectize_property' @@ -30,6 +31,7 @@ protected function validDataFromRequest() /** * @return array */ + #[\Override] public function results() { $results = parent::results(); @@ -46,6 +48,7 @@ public function results() * @param Container $container DI Container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); diff --git a/packages/admin/src/Charcoal/Admin/Action/Selectize/SelectizeRendererAwareTrait.php b/packages/admin/src/Charcoal/Admin/Action/Selectize/SelectizeRendererAwareTrait.php index 3c1d01851..f947f6622 100644 --- a/packages/admin/src/Charcoal/Admin/Action/Selectize/SelectizeRendererAwareTrait.php +++ b/packages/admin/src/Charcoal/Admin/Action/Selectize/SelectizeRendererAwareTrait.php @@ -77,7 +77,7 @@ public function setSelectizeProperty($struct) protected function selectizeInput() { $prop = $this->selectizeProperty(); - $type = isset($prop['input_type']) ? $prop['input_type'] : SelectizeInput::class; + $type = $prop['input_type'] ?? SelectizeInput::class; $input = $this->propertyInputFactory()->create($type); $input->setInputType($type); @@ -93,9 +93,8 @@ protected function selectizeInput() * Retrieves the output from SelectizeInput::selectizeVal. * * @param mixed $val The value(s) to parse as selectize choices. - * @return array */ - protected function selectizeVal($val) + protected function selectizeVal($val): array { if ($val === null) { return []; @@ -139,7 +138,7 @@ public function propertyInputFactory() if (!isset($this->propertyInputFactory)) { throw new RuntimeException(sprintf( 'Property Input Factory is not defined for [%s]', - get_class($this) + $this::class )); } @@ -170,7 +169,7 @@ public function propertyFactory() if (!isset($this->propertyFactory)) { throw new RuntimeException(sprintf( 'Property Control Factory is not defined for [%s]', - get_class($this) + $this::class )); } diff --git a/packages/admin/src/Charcoal/Admin/Action/Selectize/UpdateAction.php b/packages/admin/src/Charcoal/Admin/Action/Selectize/UpdateAction.php index e0b7430d9..9cf32a020 100644 --- a/packages/admin/src/Charcoal/Admin/Action/Selectize/UpdateAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/Selectize/UpdateAction.php @@ -20,7 +20,8 @@ class UpdateAction extends BaseUpdateAction * * @return string[] */ - protected function validDataFromRequest() + #[\Override] + protected function validDataFromRequest(): array { return array_merge([ 'selectize_obj_type', 'selectize_prop_ident', 'selectize_property' @@ -30,6 +31,7 @@ protected function validDataFromRequest() /** * @return array */ + #[\Override] public function results() { $results = parent::results(); @@ -46,6 +48,7 @@ public function results() * @param Container $container DI Container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); diff --git a/packages/admin/src/Charcoal/Admin/Action/System/AbstractCacheAction.php b/packages/admin/src/Charcoal/Admin/Action/System/AbstractCacheAction.php index efbc5c979..602becadc 100644 --- a/packages/admin/src/Charcoal/Admin/Action/System/AbstractCacheAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/System/AbstractCacheAction.php @@ -67,6 +67,7 @@ public function getTwigEngine(): ?TwigEngine /** * @return array */ + #[\Override] public function results() { return [ @@ -81,6 +82,7 @@ public function results() * @param Container $container A service locator. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -95,7 +97,7 @@ protected function setDependencies(Container $container) return null; }; $this->twigEngine = function () use ($container) { - if (class_exists('\Twig\Environment')) { + if (class_exists(\Twig\Environment::class)) { return $container['view/engine/twig']; } diff --git a/packages/admin/src/Charcoal/Admin/Action/System/ClearCacheAction.php b/packages/admin/src/Charcoal/Admin/Action/System/ClearCacheAction.php index 2573ebab4..721e600d1 100644 --- a/packages/admin/src/Charcoal/Admin/Action/System/ClearCacheAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/System/ClearCacheAction.php @@ -24,7 +24,7 @@ public function run(RequestInterface $request, ResponseInterface $response) $translator = $this->translator(); $cacheType = $request->getParam('cache_type'); - if (!is_string($cacheType) || empty($cacheType)) { + if (!is_string($cacheType) || ($cacheType === '' || $cacheType === '0')) { $this->addFeedback('error', $translator->translate('Cache type not defined.')); $this->setSuccess(false); return $response->withStatus(400); @@ -100,8 +100,7 @@ public function run(RequestInterface $request, ResponseInterface $response) private function clearGlobalCache() { $cache = $this->cachePool(); - $result = $cache->clear(); - return $result; + return $cache->clear(); } /** @@ -112,8 +111,7 @@ private function clearGlobalCache() private function clearPagesCache() { $cache = $this->cachePool(); - $result = $cache->deleteItems([ 'request', 'template' ]); - return $result; + return $cache->deleteItems([ 'request', 'template' ]); } /** @@ -124,21 +122,18 @@ private function clearPagesCache() private function clearObjectsCache() { $cache = $this->cachePool(); - $result = $cache->deleteItems([ 'object', 'metadata' ]); - return $result; + return $cache->deleteItems([ 'object', 'metadata' ]); } private function clearTwigCache(): bool { $engine = $this->getTwigEngine(); - if (!$engine) { + if (!$engine instanceof \Charcoal\View\Twig\TwigEngine) { return true; } $defaultCachePath = realpath($engine->cache()); - $cachePath = $defaultCachePath - ? $defaultCachePath - : realpath($this->appConfig['publicPath'] . DIRECTORY_SEPARATOR . $engine->cache()); + $cachePath = $defaultCachePath ?: realpath($this->appConfig['publicPath'] . DIRECTORY_SEPARATOR . $engine->cache()); if (!is_dir($cachePath)) { return false; } @@ -149,14 +144,12 @@ private function clearTwigCache(): bool private function clearMustacheCache(): bool { $engine = $this->getMustacheEngine(); - if (!$engine) { + if (!$engine instanceof \Charcoal\View\Mustache\MustacheEngine) { return true; } $defaultCachePath = realpath($engine->cache()); - $cachePath = $defaultCachePath - ? $defaultCachePath - : realpath($this->appConfig['publicPath'] . DIRECTORY_SEPARATOR . $engine->cache()); + $cachePath = $defaultCachePath ?: realpath($this->appConfig['publicPath'] . DIRECTORY_SEPARATOR . $engine->cache()); if (!is_dir($cachePath)) { return false; } diff --git a/packages/admin/src/Charcoal/Admin/Action/System/StaticWebsite/ActivateAction.php b/packages/admin/src/Charcoal/Admin/Action/System/StaticWebsite/ActivateAction.php index e196d5d54..0d0a7a909 100644 --- a/packages/admin/src/Charcoal/Admin/Action/System/StaticWebsite/ActivateAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/System/StaticWebsite/ActivateAction.php @@ -1,5 +1,7 @@ request('GET', $url, [ 'http_errors' => false ]); - } catch (Exception $e) { + } catch (Exception) { $this->setSuccess(false); return $response->withStatus(404); } @@ -84,7 +84,7 @@ public function run(RequestInterface $request, ResponseInterface $response) return $response->withStatus(404); } - if (strstr($headers['Content-Type'][0], 'text/html') !== false) { + if (str_contains($headers['Content-Type'][0], 'text/html')) { $outputFile = $outputDir . '/index.html'; $prefix = ''; } else { @@ -113,23 +113,20 @@ public function run(RequestInterface $request, ResponseInterface $response) } } - /** - * @return array - */ - public function results() + #[\Override] + public function results(): array { - $ret = [ + return [ 'success' => $this->success(), 'feedbacks' => $this->feedbacks() ]; - - return $ret; } /** * @param Container $container Pimple DI Container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); diff --git a/packages/admin/src/Charcoal/Admin/Action/System/StaticWebsite/DeactivateAction.php b/packages/admin/src/Charcoal/Admin/Action/System/StaticWebsite/DeactivateAction.php index c4c4a2644..a612ace58 100644 --- a/packages/admin/src/Charcoal/Admin/Action/System/StaticWebsite/DeactivateAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/System/StaticWebsite/DeactivateAction.php @@ -1,5 +1,7 @@ $this->success(), 'feedbacks' => $this->feedbacks() ]; - - return $ret; } /** * @param Container $container Pimple DI Container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); diff --git a/packages/admin/src/Charcoal/Admin/Action/System/StaticWebsite/DeleteAction.php b/packages/admin/src/Charcoal/Admin/Action/System/StaticWebsite/DeleteAction.php index 66ac5cd1c..fc770f4aa 100644 --- a/packages/admin/src/Charcoal/Admin/Action/System/StaticWebsite/DeleteAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/System/StaticWebsite/DeleteAction.php @@ -59,23 +59,20 @@ public function run(RequestInterface $request, ResponseInterface $response) } } - /** - * @return array - */ - public function results() + #[\Override] + public function results(): array { - $ret = [ + return [ 'success' => $this->success(), 'feedbacks' => $this->feedbacks() ]; - - return $ret; } /** * @param Container $container Pimple DI Container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); diff --git a/packages/admin/src/Charcoal/Admin/Action/System/StaticWebsite/DeleteAllAction.php b/packages/admin/src/Charcoal/Admin/Action/System/StaticWebsite/DeleteAllAction.php index 4009cdce8..4e5496e98 100644 --- a/packages/admin/src/Charcoal/Admin/Action/System/StaticWebsite/DeleteAllAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/System/StaticWebsite/DeleteAllAction.php @@ -43,23 +43,20 @@ public function run(RequestInterface $request, ResponseInterface $response) return $response; } - /** - * @return array - */ - public function results() + #[\Override] + public function results(): array { - $ret = [ + return [ 'success' => $this->success(), 'feedbacks' => $this->feedbacks() ]; - - return $ret; } /** * @param Container $container Pimple DI Container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -69,9 +66,8 @@ protected function setDependencies(Container $container) /** * @param string $dir Directory to delete. - * @return void */ - private function recursiveDelete($dir) + private function recursiveDelete(string $dir): void { $files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS), diff --git a/packages/admin/src/Charcoal/Admin/Action/System/StaticWebsite/PreviewAction.php b/packages/admin/src/Charcoal/Admin/Action/System/StaticWebsite/PreviewAction.php index 00ea58008..ef5289d70 100644 --- a/packages/admin/src/Charcoal/Admin/Action/System/StaticWebsite/PreviewAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/System/StaticWebsite/PreviewAction.php @@ -20,10 +20,7 @@ class PreviewAction extends AdminAction */ private $basePath; - /** - * @var string - */ - private $fileContent = ''; + private string|bool $fileContent = ''; /** * @param RequestInterface $request A PSR-7 compatible Request instance. @@ -34,7 +31,7 @@ public function run(RequestInterface $request, ResponseInterface $response) { $url = $request->getParam('url'); $relativeUrl = str_replace($this->baseUrl(), '', $url); - $url = $this->baseUrl() . $relativeUrl; + $this->baseUrl(); $outputDir = $this->basePath . DIRECTORY_SEPARATOR . 'cache/static/' . $relativeUrl; if (!file_exists($outputDir)) { @@ -59,24 +56,21 @@ public function run(RequestInterface $request, ResponseInterface $response) return $response->withStatus(404); } - /** - * @return array - */ - public function results() + #[\Override] + public function results(): array { - $ret = [ + return [ 'success' => $this->success(), 'feedbacks' => $this->feedbacks(), 'content' => $this->fileContent ]; - - return $ret; } /** * @param Container $container Pimple DI Container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); diff --git a/packages/admin/src/Charcoal/Admin/Action/System/StaticWebsite/UpdateAction.php b/packages/admin/src/Charcoal/Admin/Action/System/StaticWebsite/UpdateAction.php index a9c6fa401..bee34396f 100644 --- a/packages/admin/src/Charcoal/Admin/Action/System/StaticWebsite/UpdateAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/System/StaticWebsite/UpdateAction.php @@ -44,23 +44,20 @@ public function run(RequestInterface $request, ResponseInterface $response) return $response; } - /** - * @return array - */ - public function results() + #[\Override] + public function results(): array { - $ret = [ + return [ 'success' => $this->success(), 'feedbacks' => $this->feedbacks() ]; - - return $ret; } /** * @param Container $container Pimple DI Container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -74,7 +71,7 @@ protected function setDependencies(Container $container) * @param ResponseInterface $response PSR-7 response. * @return boolean */ - protected function cacheUrl($url, $outputDir, ResponseInterface $response) + protected function cacheUrl($url, string $outputDir, ResponseInterface $response) { unset($response); @@ -95,7 +92,7 @@ protected function cacheUrl($url, $outputDir, ResponseInterface $response) try { $guzzleClient = new GuzzleClient(); $static = $guzzleClient->request('GET', $url); - } catch (Exception $e) { + } catch (Exception) { $this->setSuccess(false); return false; } @@ -112,7 +109,7 @@ protected function cacheUrl($url, $outputDir, ResponseInterface $response) return false; } - if (strstr($headers['Content-Type'][0], 'text/html') !== false) { + if (str_contains($headers['Content-Type'][0], 'text/html')) { $outputFile = $outputDir . '/index.html'; $prefix = ''; } else { diff --git a/packages/admin/src/Charcoal/Admin/Action/System/StaticWebsite/UpdateAllAction.php b/packages/admin/src/Charcoal/Admin/Action/System/StaticWebsite/UpdateAllAction.php index 9e0c6202e..5cf172193 100644 --- a/packages/admin/src/Charcoal/Admin/Action/System/StaticWebsite/UpdateAllAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/System/StaticWebsite/UpdateAllAction.php @@ -18,9 +18,9 @@ class UpdateAllAction extends UpdateAction /** * @param RequestInterface $request A PSR-7 compatible Request instance. * @param ResponseInterface $response A PSR-7 compatible Response instance. - * @return ResponseInterface */ - public function run(RequestInterface $request, ResponseInterface $response) + #[\Override] + public function run(RequestInterface $request, ResponseInterface $response): ResponseInterface { unset($request); @@ -35,17 +35,13 @@ public function run(RequestInterface $request, ResponseInterface $response) return $response; } - /** - * @return array - */ - public function results() + #[\Override] + public function results(): array { - $ret = [ + return [ 'success' => $this->success(), 'feedbacks' => $this->feedbacks() ]; - - return $ret; } /** @@ -54,7 +50,7 @@ public function results() * @param integer $flags Glob flags. * @return array */ - protected function globRecursive($dir, $pattern, $flags = 0) + protected function globRecursive(string $dir, string $pattern, $flags = 0): array|false { $files = glob($dir . '/' . $pattern, $flags); foreach (glob($dir . '/*', (GLOB_ONLYDIR | GLOB_NOSORT)) as $dir) { diff --git a/packages/admin/src/Charcoal/Admin/Action/Tinymce/UploadImageAction.php b/packages/admin/src/Charcoal/Admin/Action/Tinymce/UploadImageAction.php index b41d04adf..cd2a3e801 100644 --- a/packages/admin/src/Charcoal/Admin/Action/Tinymce/UploadImageAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/Tinymce/UploadImageAction.php @@ -19,24 +19,18 @@ class UploadImageAction extends AdminAction /** * Whether uploaded files should be accessible from the web root. - * - * @var boolean */ - private $publicAccess = self::DEFAULT_PUBLIC_ACCESS; + private bool $publicAccess = self::DEFAULT_PUBLIC_ACCESS; /** * The relative path to the storage directory. - * - * @var string */ - private $uploadPath = self::DEFAULT_UPLOAD_PATH; + private string $uploadPath = self::DEFAULT_UPLOAD_PATH; /** * Whether existing destinations should be overwritten. - * - * @var boolean */ - private $overwrite = self::DEFAULT_OVERWRITE; + private bool $overwrite = self::DEFAULT_OVERWRITE; /** * The base path for the Charcoal installation. @@ -52,10 +46,7 @@ class UploadImageAction extends AdminAction */ private $publicPath; - /** - * @var string - */ - private $uploadedPath; + private ?string $uploadedPath = null; /** * Inject dependencies from a DI Container. @@ -63,6 +54,7 @@ class UploadImageAction extends AdminAction * @param Container $container A dependencies container instance. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -78,19 +70,18 @@ protected function setDependencies(Container $container) * * @param RequestInterface $request A PSR-7 compatible Request instance. * @param ResponseInterface $response A PSR-7 compatible Response instance. - * @return ResponseInterface */ - public function run(RequestInterface $request, ResponseInterface $response) + public function run(RequestInterface $request, ResponseInterface $response): ResponseInterface { $path = $request->getParam('upload_path'); - if (!!$path) { + if ((bool) $path) { $this->setUploadPath($path); } $this->uploadedPath = $this->fileUpload($_FILES['file']); - $this->setSuccess(!!$this->uploadedPath); + $this->setSuccess((bool) $this->uploadedPath); return $response; } @@ -100,9 +91,8 @@ public function run(RequestInterface $request, ResponseInterface $response) * * @param array $fileData The file data (from $_FILES, typically). * @throws \InvalidArgumentException If the FILES data argument is missing `name` or `tmp_name`. - * @return string */ - public function fileUpload(array $fileData) + public function fileUpload(array $fileData): string { if (!isset($fileData['name'])) { throw new \InvalidArgumentException( @@ -121,18 +111,16 @@ public function fileUpload(array $fileData) } else { $this->logger->notice(sprintf('File %s uploaded succesfully', $target)); $basePath = $this->basePath(); - $target = str_replace($basePath, '', $target); - return $target; + return str_replace($basePath, '', $target); } } /** * @param string $filename Optional. The filename to save. If unset, a default filename will be generated. * @throws \Exception If the target path is not writeable. - * @return string */ - public function uploadTarget($filename = null) + public function uploadTarget($filename = null): string { $basePath = $this->basePath(); @@ -143,7 +131,7 @@ public function uploadTarget($filename = null) // @todo: Feedback $this->logger->debug( 'Path does not exist. Attempting to create path ' . $dir . '.', - [get_called_class() . '::' . __FUNCTION__] + [static::class . '::' . __FUNCTION__] ); mkdir($dir, 0777, true); } @@ -156,7 +144,7 @@ public function uploadTarget($filename = null) $target = $dir . $filename; if ($this->fileExists($target)) { - if ($this->overwrite() === true) { + if ($this->overwrite()) { return $target; } else { $target = $dir . $this->generateUniqueFilename($filename); @@ -177,9 +165,8 @@ public function uploadTarget($filename = null) * * @param string $file The full file to check. * @param boolean $caseInsensitive Case-insensitive by default. - * @return boolean */ - public function fileExists($file, $caseInsensitive = true) + public function fileExists($file, $caseInsensitive = true): bool { if (!$this->isAbsolutePath($file)) { $file = $this->basePath() . DIRECTORY_SEPARATOR . $file; @@ -197,7 +184,7 @@ public function fileExists($file, $caseInsensitive = true) if ($files) { $pattern = preg_quote($file, '#'); foreach ($files as $f) { - if (preg_match("#${pattern}#i", $f)) { + if (preg_match("#{$pattern}#i", $f)) { return true; } } @@ -216,7 +203,7 @@ public function fileExists($file, $caseInsensitive = true) * @param string $file A file path. * @return boolean Returns TRUE if the given path is absolute. Otherwise, returns FALSE. */ - protected function isAbsolutePath($file) + protected function isAbsolutePath($file): bool { return strspn($file, '/\\', 0, 1) || (strlen($file) > 3 @@ -232,7 +219,7 @@ protected function isAbsolutePath($file) * @param string $filename The filename to sanitize. * @return string The sanitized filename. */ - public function sanitizeFilename($filename) + public function sanitizeFilename($filename): string { // Remove blacklisted caharacters $blacklist = ['/', '\\', '\0', '*', ':', '?', '"', '<', '>', '|', '#', '&', '!', '`', ' ']; @@ -249,22 +236,17 @@ public function sanitizeFilename($filename) * * @param string|array $filename The filename to alter. * @throws \InvalidArgumentException If the given filename is invalid. - * @return string */ - public function generateUniqueFilename($filename) + public function generateUniqueFilename($filename): string { if (!is_string($filename) && !is_array($filename)) { throw new \InvalidArgumentException(sprintf( 'The target must be a string or an array from [pathfino()], received %s', - (is_object($filename) ? get_class($filename) : gettype($filename)) + (get_debug_type($filename)) )); } - if (is_string($filename)) { - $info = pathinfo($filename); - } else { - $info = $filename; - } + $info = is_string($filename) ? pathinfo($filename) : $filename; $filename = $info['filename'] . '-' . uniqid(); @@ -275,10 +257,7 @@ public function generateUniqueFilename($filename) return $filename; } - /** - * @return string - */ - public function uploadPath() + public function uploadPath(): string { return $this->uploadPath; } @@ -290,9 +269,8 @@ public function uploadPath() * * @param string $path The destination directory, relative to project's root. * @throws \InvalidArgumentException If the path is not a string. - * @return self */ - public function setUploadPath($path) + public function setUploadPath($path): static { if (!is_string($path)) { throw new \InvalidArgumentException( @@ -310,21 +288,18 @@ public function setUploadPath($path) * Set whether uploaded files should be publicly available. * * @param boolean $public Whether uploaded files should be accessible (TRUE) or not (FALSE) from the web root. - * @return self */ - public function setPublicAccess($public) + public function setPublicAccess($public): static { - $this->publicAccess = !!$public; + $this->publicAccess = (bool) $public; return $this; } /** * Determine if uploaded files should be publicly available. - * - * @return boolean */ - public function publicAccess() + public function publicAccess(): bool { return $this->publicAccess; } @@ -333,21 +308,18 @@ public function publicAccess() * Set whether existing destinations should be overwritten. * * @param boolean $overwrite Whether existing destinations should be overwritten (TRUE) or not (FALSE). - * @return self */ - public function setOverwrite($overwrite) + public function setOverwrite($overwrite): static { - $this->overwrite = !!$overwrite; + $this->overwrite = (bool) $overwrite; return $this; } /** * Determine if existing destinations should be overwritten. - * - * @return boolean */ - public function overwrite() + public function overwrite(): bool { return $this->overwrite; } @@ -368,10 +340,9 @@ protected function basePath() /** * Default response stub. - * - * @return array */ - public function results() + #[\Override] + public function results(): array { return [ 'success' => $this->success(), diff --git a/packages/admin/src/Charcoal/Admin/Action/Widget/LoadAction.php b/packages/admin/src/Charcoal/Admin/Action/Widget/LoadAction.php index 8fcf6d9f9..0556402b5 100644 --- a/packages/admin/src/Charcoal/Admin/Action/Widget/LoadAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/Widget/LoadAction.php @@ -115,7 +115,7 @@ public function run(RequestInterface $request, ResponseInterface $response) try { if (!$widgetType) { - $actualType = is_object($widgetType) ? get_class($widgetType) : gettype($widgetType); + $actualType = get_debug_type($widgetType); $this->addFeedback('error', strtr($reqMessage, [ '{{ parameter }}' => '"widget_type"', '{{ expectedType }}' => 'string', @@ -133,7 +133,7 @@ public function run(RequestInterface $request, ResponseInterface $response) if (isset($widgetOptions)) { if (!is_array($widgetOptions)) { - $actualType = is_object($widgetOptions) ? get_class($widgetOptions) : gettype($widgetOptions); + $actualType = get_debug_type($widgetOptions); $this->addFeedback('error', strtr($typeMessage, [ '{{ parameter }}' => '"widget_options"', '{{ expectedType }}' => 'array', @@ -190,7 +190,7 @@ public function run(RequestInterface $request, ResponseInterface $response) * @throws InvalidArgumentException If the widget ID argument is not a string. * @return LoadAction Chainable */ - public function setWidgetId($id) + public function setWidgetId($id): static { if (!is_string($id)) { throw new InvalidArgumentException( @@ -217,9 +217,8 @@ public function widgetId() * Set the widget's DATA. * * @param array|mixed $widgetData WidgetData for LoadAction. - * @return self */ - public function setWidgetData($widgetData) + public function setWidgetData($widgetData): static { $this->widgetData = $widgetData; @@ -243,7 +242,7 @@ public function widgetData() * @throws InvalidArgumentException If the widget type argument is not a string. * @return LoadAction Chainable */ - public function setWidgetType($type) + public function setWidgetType($type): static { if (!is_string($type)) { throw new InvalidArgumentException( @@ -273,7 +272,7 @@ public function widgetType() * @throws InvalidArgumentException If the widget HTML is not a string. * @return LoadAction Chainable */ - public function setWidgetHtml($html) + public function setWidgetHtml($html): static { if (!is_string($html)) { throw new InvalidArgumentException( @@ -296,10 +295,8 @@ public function widgetHtml() return $this->widgetHtml; } - /** - * @return array - */ - public function results() + #[\Override] + public function results(): array { return [ 'success' => $this->success(), @@ -314,6 +311,7 @@ public function results() * @param Container $container A dependencies container instance. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setdependencies($container); @@ -331,7 +329,7 @@ protected function setDependencies(Container $container) */ protected function widgetView() { - if (!isset($this->widgetView)) { + if ($this->widgetView === null) { throw new RuntimeException('Widget Renderer is not defined'); } @@ -346,7 +344,7 @@ protected function widgetView() */ protected function widgetFactory() { - if (!isset($this->widgetFactory)) { + if ($this->widgetFactory === null) { throw new RuntimeException('Widget Factory is not defined'); } @@ -357,9 +355,8 @@ protected function widgetFactory() * Set the widget renderer. * * @param ViewInterface $view The view renderer to create widgets. - * @return void */ - private function setWidgetView(ViewInterface $view) + private function setWidgetView(ViewInterface $view): void { $this->widgetView = $view; } @@ -368,9 +365,8 @@ private function setWidgetView(ViewInterface $view) * Set the widget factory. * * @param FactoryInterface $factory The factory to create widgets. - * @return void */ - private function setWidgetFactory(FactoryInterface $factory) + private function setWidgetFactory(FactoryInterface $factory): void { $this->widgetFactory = $factory; } diff --git a/packages/admin/src/Charcoal/Admin/Action/Widget/Table/InlineAction.php b/packages/admin/src/Charcoal/Admin/Action/Widget/Table/InlineAction.php index 620910815..a52a7818c 100644 --- a/packages/admin/src/Charcoal/Admin/Action/Widget/Table/InlineAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/Widget/Table/InlineAction.php @@ -68,7 +68,7 @@ public function run(RequestInterface $request, ResponseInterface $response) $reqMessage = $this->translator()->translation( '{{ parameter }} required, must be a {{ expectedType }}, received {{ actualType }}' ); - $typeMessage = $this->translator()->translation( + $this->translator()->translation( '{{ parameter }} must be a {{ expectedType }}, received {{ actualType }}' ); @@ -76,7 +76,7 @@ public function run(RequestInterface $request, ResponseInterface $response) $objId = $request->getParam('obj_id'); if (!$objType) { - $actualType = is_object($objType) ? get_class($objType) : gettype($objType); + $actualType = get_debug_type($objType); $this->addFeedback('error', strtr($reqMessage, [ '{{ parameter }}' => '"obj_type"', '{{ expectedType }}' => 'string', @@ -88,7 +88,7 @@ public function run(RequestInterface $request, ResponseInterface $response) } if (!$objId) { - $actualType = is_object($objId) ? get_class($objId) : gettype($objId); + $actualType = get_debug_type($objId); $this->addFeedback('error', strtr($reqMessage, [ '{{ parameter }}' => '"obj_id"', '{{ expectedType }}' => 'string or numeric', @@ -149,10 +149,8 @@ public function run(RequestInterface $request, ResponseInterface $response) } } - /** - * @return array - */ - public function results() + #[\Override] + public function results(): array { return [ 'success' => $this->success(), @@ -165,6 +163,7 @@ public function results() * @param Container $container DI container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -180,7 +179,7 @@ protected function setDependencies(Container $container) */ protected function widgetFactory() { - if (!isset($this->widgetFactory)) { + if ($this->widgetFactory === null) { throw new RuntimeException('Widget Factory is not defined'); } @@ -191,9 +190,8 @@ protected function widgetFactory() * Set the widget factory. * * @param FactoryInterface $factory The factory to create widgets. - * @return void */ - private function setWidgetFactory(FactoryInterface $factory) + private function setWidgetFactory(FactoryInterface $factory): void { $this->widgetFactory = $factory; } diff --git a/packages/admin/src/Charcoal/Admin/Action/Widget/Table/InlineMultiAction.php b/packages/admin/src/Charcoal/Admin/Action/Widget/Table/InlineMultiAction.php index 9f540d378..8f9b313a7 100644 --- a/packages/admin/src/Charcoal/Admin/Action/Widget/Table/InlineMultiAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/Widget/Table/InlineMultiAction.php @@ -48,10 +48,8 @@ class InlineMultiAction extends AdminAction /** * Store the widget factory. - * - * @var FactoryInterface */ - private $widgetFactory; + private ?\Charcoal\Factory\FactoryInterface $widgetFactory = null; /** * @param RequestInterface $request A PSR-7 compatible Request instance. @@ -68,7 +66,7 @@ public function run(RequestInterface $request, ResponseInterface $response) $reqMessage = $this->translator()->translation( '{{ parameter }} required, must be a {{ expectedType }}, received {{ actualType }}' ); - $typeMessage = $this->translator()->translation( + $this->translator()->translation( '{{ parameter }} must be a {{ expectedType }}, received {{ actualType }}' ); @@ -76,7 +74,7 @@ public function run(RequestInterface $request, ResponseInterface $response) $objIds = $request->getParam('obj_ids'); if (!$objType) { - $actualType = is_object($objType) ? get_class($objType) : gettype($objType); + $actualType = get_debug_type($objType); $this->addFeedback('error', strtr($reqMessage, [ '{{ parameter }}' => '"obj_type"', '{{ expectedType }}' => 'string', @@ -88,7 +86,7 @@ public function run(RequestInterface $request, ResponseInterface $response) } if (!$objIds || !is_array($objIds)) { - $actualType = is_object($objIds) ? get_class($objIds) : gettype($objIds); + $actualType = get_debug_type($objIds); $this->addFeedback('error', strtr($reqMessage, [ '{{ parameter }}' => '"obj_ids"', '{{ expectedType }}' => 'array of object IDs', @@ -153,10 +151,8 @@ public function run(RequestInterface $request, ResponseInterface $response) } } - /** - * @return array - */ - public function results() + #[\Override] + public function results(): array { return [ 'success' => $this->success(), @@ -169,6 +165,7 @@ public function results() * @param Container $container DI container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -180,11 +177,10 @@ protected function setDependencies(Container $container) * Retrieve the widget factory. * * @throws RuntimeException If the widget factory was not previously set. - * @return FactoryInterface */ - protected function widgetFactory() + protected function widgetFactory(): \Charcoal\Factory\FactoryInterface { - if (!isset($this->widgetFactory)) { + if (!$this->widgetFactory instanceof \Charcoal\Factory\FactoryInterface) { throw new RuntimeException('Widget Factory is not defined'); } @@ -195,9 +191,8 @@ protected function widgetFactory() * Set the widget factory. * * @param FactoryInterface $factory The factory to create widgets. - * @return void */ - private function setWidgetFactory(FactoryInterface $factory) + private function setWidgetFactory(FactoryInterface $factory): void { $this->widgetFactory = $factory; } diff --git a/packages/admin/src/Charcoal/Admin/AdminAction.php b/packages/admin/src/Charcoal/Admin/AdminAction.php index d97ee1eec..d101689ef 100644 --- a/packages/admin/src/Charcoal/Admin/AdminAction.php +++ b/packages/admin/src/Charcoal/Admin/AdminAction.php @@ -57,17 +57,13 @@ abstract class AdminAction extends AbstractAction implements /** * Store the result from the last validation by Google reCAPTCHA API. - * - * @var array|null */ - private $recaptchaLastResult; + private ?array $recaptchaLastResult = null; /** * Store the model factory. - * - * @var FactoryInterface $modelFactory */ - private $modelFactory; + private ?\Charcoal\Factory\FactoryInterface $modelFactory = null; /** * Action's init method is called automatically from `charcoal-app`'s Action Route. @@ -82,6 +78,7 @@ abstract class AdminAction extends AbstractAction implements * @return boolean * @see \Charcoal\App\Route\TemplateRoute::__invoke() */ + #[\Override] public function init(RequestInterface $request) { if (!session_id()) { @@ -165,12 +162,11 @@ public function siteName() */ public function results() { - $results = [ + return [ 'success' => $this->success(), 'next_url' => $this->redirectUrl(), 'feedbacks' => $this->feedbacks() ]; - return $results; } /** @@ -179,6 +175,7 @@ public function results() * @param Container $container DI Container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -340,11 +337,11 @@ protected function validateCaptcha($token) * with a new Response object that represents a client error. * @return boolean Returns TRUE if the user response is valid, FALSE if it is invalid. */ - protected function validateCaptchaFromRequest(RequestInterface $request, ResponseInterface &$response = null) + protected function validateCaptchaFromRequest(RequestInterface $request, ?ResponseInterface &$response = null) { $token = $request->getParam('g-recaptcha-response', false); if (empty($token)) { - if ($response !== null) { + if ($response instanceof \Psr\Http\Message\ResponseInterface) { $this->addFeedback('error', $this->translator()->translate('Missing CAPTCHA response.')); $this->setSuccess(false); @@ -355,7 +352,7 @@ protected function validateCaptchaFromRequest(RequestInterface $request, Respons } $result = $this->validateCaptcha($token); - if ($result === false && $response !== null) { + if ($result === false && $response instanceof \Psr\Http\Message\ResponseInterface) { $this->addFeedback('error', $this->translator()->translate('Invalid or malformed CAPTCHA response.')); $this->setSuccess(false); diff --git a/packages/admin/src/Charcoal/Admin/AdminModule.php b/packages/admin/src/Charcoal/Admin/AdminModule.php index 3d32be4f8..2f03e7ed5 100644 --- a/packages/admin/src/Charcoal/Admin/AdminModule.php +++ b/packages/admin/src/Charcoal/Admin/AdminModule.php @@ -32,7 +32,8 @@ class AdminModule extends AbstractModule * * @return AdminModule Chainable */ - public function setUp() + #[\Override] + public function setUp(): static { // Hack: skip if the request does not start with '/admin' $container = $this->app()->getContainer(); @@ -47,15 +48,13 @@ public function setUp() $container->register(new AdminServiceProvider()); $module = $this; - $container['charcoal/admin/module'] = function () use ($module) { - return $module; - }; + $container['charcoal/admin/module'] = (fn(): static => $module); $adminConfig = $container['admin/config']; $this->setConfig($adminConfig); - $groupIdent = '/' . trim($adminConfig['base_path'], '/'); + $groupIdent = '/' . trim((string) $adminConfig['base_path'], '/'); // Add the route group $this->app()->group($groupIdent, 'charcoal/admin/module:setupRoutes') @@ -69,7 +68,8 @@ public function setUp() * * @return AdminModule Chainable */ - public function setupRoutes() + #[\Override] + public function setupRoutes(): static { if ($this->routeManager === null) { parent::setupRoutes(); @@ -102,7 +102,7 @@ public function setupHandlers( * @param object|HandlerInterface $handler An error handler instance. * @return HandlerInterface */ - $container->extend('notFoundHandler', function ($handler, $container) { + $container->extend('notFoundHandler', function ($handler, array $container) { $appConfig = $container['config']; $adminConfig = $container['admin/config']; if ($handler instanceof HandlerInterface) { @@ -125,7 +125,7 @@ public function setupHandlers( * @param object|HandlerInterface $handler An error handler instance. * @return HandlerInterface */ - $container->extend('notAllowedHandler', function ($handler, $container) { + $container->extend('notAllowedHandler', function ($handler, array $container) { $appConfig = $container['config']; $adminConfig = $container['admin/config']; if ($handler instanceof HandlerInterface) { @@ -148,7 +148,7 @@ public function setupHandlers( * @param object|HandlerInterface $handler An error handler instance. * @return HandlerInterface */ - $container->extend('phpErrorHandler', function ($handler, $container) { + $container->extend('phpErrorHandler', function ($handler, array $container) { $appConfig = $container['config']; $adminConfig = $container['admin/config']; if ($handler instanceof HandlerInterface) { @@ -171,7 +171,7 @@ public function setupHandlers( * @param object|HandlerInterface $handler An error handler instance. * @return HandlerInterface */ - $container->extend('errorHandler', function ($handler, $container) { + $container->extend('errorHandler', function ($handler, array $container) { $appConfig = $container['config']; $adminConfig = $container['admin/config']; if ($handler instanceof HandlerInterface) { @@ -196,7 +196,7 @@ public function setupHandlers( * @param object|HandlerInterface $handler An error handler instance. * @return HandlerInterface */ - $container->extend('maintenanceHandler', function ($handler, $container) { + $container->extend('maintenanceHandler', function ($handler, array $container) { $appConfig = $container['config']; $adminConfig = $container['admin/config']; if ($handler instanceof HandlerInterface) { @@ -226,11 +226,6 @@ private function isPathAdmin($path) if ($path === 'admin') { return true; } - - if (substr($path, 0, 6) === 'admin/') { - return true; - } - - return false; + return str_starts_with($path, 'admin/'); } } diff --git a/packages/admin/src/Charcoal/Admin/AdminScript.php b/packages/admin/src/Charcoal/Admin/AdminScript.php index 0d17d9304..22ccc2583 100644 --- a/packages/admin/src/Charcoal/Admin/AdminScript.php +++ b/packages/admin/src/Charcoal/Admin/AdminScript.php @@ -27,15 +27,14 @@ abstract class AdminScript extends AbstractScript /** * The model factory. - * - * @var FactoryInterface */ - private $modelFactory; + private ?\Charcoal\Factory\FactoryInterface $modelFactory = null; /** * @param Container $container Pimple DI container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -95,11 +94,10 @@ private function booleanInput(PropertyInterface $prop, $label) 1 => $prop->trueLabel(), 0 => $prop->falseLabel() ]; - $input = $climate->radio( + return $climate->radio( $label, $opts ); - return $input; } /** @@ -114,18 +112,15 @@ private function passwordInput(PropertyInterface $prop, $label) unset($prop); $climate = $this->climate(); - - $input = $climate->password($label); - return $input; + return $climate->password($label); } /** * Set the model factory. * * @param FactoryInterface $factory The factory used to create models. - * @return void */ - private function setModelFactory(FactoryInterface $factory) + private function setModelFactory(FactoryInterface $factory): void { $this->modelFactory = $factory; } diff --git a/packages/admin/src/Charcoal/Admin/AdminTemplate.php b/packages/admin/src/Charcoal/Admin/AdminTemplate.php index 88beb929e..b33f0885f 100644 --- a/packages/admin/src/Charcoal/Admin/AdminTemplate.php +++ b/packages/admin/src/Charcoal/Admin/AdminTemplate.php @@ -78,20 +78,11 @@ class AdminTemplate extends AbstractTemplate implements */ protected $subtitle; - /** - * @var boolean - */ - private $showSecondaryMenu = true; + private bool $showSecondaryMenu = true; - /** - * @var boolean - */ - private $showMainMenu = true; + private bool $showMainMenu = true; - /** - * @var boolean - */ - private $showSystemMenu = true; + private bool $showSystemMenu = true; /** * @var boolean @@ -118,10 +109,7 @@ class AdminTemplate extends AbstractTemplate implements */ protected $secondaryMenu; - /** - * @var array - */ - private $adminDataForJs; + private ?array $adminDataForJs = null; /** * @var \Charcoal\Ui\Menu\MenuBuilder $menuBuilder @@ -133,15 +121,9 @@ class AdminTemplate extends AbstractTemplate implements */ private $menuItemBuilder; - /** - * @var FactoryInterface $modelFactory - */ - private $modelFactory; + private ?\Charcoal\Factory\FactoryInterface $modelFactory = null; - /** - * @var FactoryInterface $widgetFactory - */ - private $widgetFactory; + private ?\Charcoal\Factory\FactoryInterface $widgetFactory = null; /** * Template's init method is called automatically from `charcoal-app`'s Template Route. @@ -156,6 +138,7 @@ class AdminTemplate extends AbstractTemplate implements * @return boolean * @see \Charcoal\App\Route\TemplateRoute::__invoke() */ + #[\Override] public function init(RequestInterface $request) { if (!session_id()) { @@ -199,12 +182,11 @@ protected function authRedirect(RequestInterface $request) * Sets the template data from a PSR Request object. * * @param RequestInterface $request A PSR-7 compatible Request instance. - * @return self */ - protected function setDataFromRequest(RequestInterface $request) + protected function setDataFromRequest(RequestInterface $request): static { $keys = $this->validDataFromRequest(); - if (!empty($keys)) { + if ($keys !== []) { $this->setData($request->getParams($keys)); } @@ -216,7 +198,7 @@ protected function setDataFromRequest(RequestInterface $request) * * @return string[] */ - protected function validDataFromRequest() + protected function validDataFromRequest(): array { return [ // HTTP Handling @@ -230,7 +212,7 @@ protected function validDataFromRequest() * @param mixed $ident Template identifier. * @return AdminTemplate Chainable */ - public function setIdent($ident) + public function setIdent($ident): static { $this->ident = $ident; return $this; @@ -248,7 +230,7 @@ public function ident() * @param mixed $label Template label. * @return AdminTemplate Chainable */ - public function setLabel($label) + public function setLabel($label): static { $this->label = $this->translator()->translation($label); @@ -269,7 +251,7 @@ public function label() * @param mixed $title Template title. * @return AdminTemplate Chainable */ - public function setTitle($title) + public function setTitle($title): static { $this->title = $this->translator()->translation($title); @@ -296,7 +278,7 @@ public function title() * @param mixed $subtitle Template subtitle. * @return AdminTemplate Chainable */ - public function setSubtitle($subtitle) + public function setSubtitle($subtitle): static { $this->subtitle = $this->translator()->translation($subtitle); @@ -317,16 +299,13 @@ public function subtitle() * @param boolean $show The show main menu flag. * @return AdminTemplate Chainable */ - public function setShowMainMenu($show) + public function setShowMainMenu($show): static { - $this->showMainMenu = !!$show; + $this->showMainMenu = (bool) $show; return $this; } - /** - * @return boolean - */ - public function showMainMenu() + public function showMainMenu(): bool { return ($this->isAuthorized() && $this->showMainMenu); } @@ -359,16 +338,13 @@ public function mainMenu() * @param boolean $show The show footer menu flag. * @return AdminTemplate Chainable */ - public function setShowSystemMenu($show) + public function setShowSystemMenu($show): static { - $this->showSystemMenu = !!$show; + $this->showSystemMenu = (bool) $show; return $this; } - /** - * @return boolean - */ - public function showSystemMenu() + public function showSystemMenu(): bool { return ($this->isAuthorized() && $this->showSystemMenu && (count($this->systemMenu()) > 0)); } @@ -376,7 +352,7 @@ public function showSystemMenu() /** * @return array */ - public function systemMenu() + public function systemMenu(): \ArrayIterator { if ($this->systemMenu === null) { $this->systemMenu = $this->createSystemMenu(); @@ -389,16 +365,13 @@ public function systemMenu() * @param boolean $show The show secondary menu flag. * @return AdminTemplate Chainable */ - public function setShowSecondaryMenu($show) + public function setShowSecondaryMenu($show): static { - $this->showSecondaryMenu = !!$show; + $this->showSecondaryMenu = (bool) $show; return $this; } - /** - * @return boolean - */ - public function showSecondaryMenu() + public function showSecondaryMenu(): bool { return ($this->isAuthorized() && $this->showSecondaryMenu); } @@ -430,10 +403,7 @@ public function mainMenuLogo() return 'assets/admin/images/identicon.png'; } - /** - * @return string - */ - public function navContainerCssClasses() + public function navContainerCssClasses(): string { $classes = [ 'has-nav-logo' ]; @@ -496,7 +466,7 @@ public function documentTitle() $siteName = $this->siteName(); $pageTitle = strip_tags($this->title()); - if ($pageTitle) { + if ($pageTitle !== '' && $pageTitle !== '0') { if ($pageTitle === $siteName) { return sprintf('%1$s — Charcoal', $pageTitle); } else { @@ -580,20 +550,15 @@ public function recaptchaInvisible() if ($hasSize && $recaptcha['size'] === 'invisible') { return true; } - - if (!$hasInvisible && !$hasSize) { - return true; - } - - return false; + return !$hasInvisible && !$hasSize; } /** * Alias of {@see self::recaptchaSiteKey()}. * - * @deprecated * @return string|null */ + #[\Deprecated] public function recaptchaKey() { return $this->recaptchaSiteKey(); @@ -623,7 +588,7 @@ public function recaptchaSiteKey() * * @return string[] */ - public function recaptchaParameters() + public function recaptchaParameters(): array { $apiConfig = $this->apiConfig('google.recaptcha'); $tplConfig = $this->get('recaptcha_options') ?: []; @@ -658,10 +623,8 @@ public function recaptchaParameters() /** * Generate a string representation of HTML attributes for the Google reCAPTCHA tag. - * - * @return string */ - public function recaptchaHtmlAttr() + public function recaptchaHtmlAttr(): string { $params = $this->recaptchaParameters(); @@ -681,6 +644,7 @@ public function recaptchaHtmlAttr() * @param Container $container DI Container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -715,11 +679,11 @@ protected function setDependencies(Container $container) * @throws Exception If the factory is not set. * @return FactoryInterface The model factory. */ - protected function modelFactory() + protected function modelFactory(): \Charcoal\Factory\FactoryInterface { - if (!$this->modelFactory) { + if (!$this->modelFactory instanceof \Charcoal\Factory\FactoryInterface) { throw new Exception( - sprintf('Model factory is not set for template "%s".', get_class($this)) + sprintf('Model factory is not set for template "%s".', static::class) ); } return $this->modelFactory; @@ -727,11 +691,10 @@ protected function modelFactory() /** * @throws Exception If the widget factory dependency was not previously set / injected. - * @return FactoryInterface */ - protected function widgetFactory() + protected function widgetFactory(): \Charcoal\Factory\FactoryInterface { - if ($this->widgetFactory === null) { + if (!$this->widgetFactory instanceof \Charcoal\Factory\FactoryInterface) { throw new Exception( 'Widget factory was not set.' ); @@ -745,7 +708,7 @@ protected function widgetFactory() * @param string $name Name of the project. * @return AdminTemplate Chainable */ - protected function setSiteName($name) + protected function setSiteName($name): static { $this->siteName = $this->translator()->translation($name); return $this; @@ -756,9 +719,8 @@ protected function setSiteName($name) * * @param mixed $options The main menu widget ID or config. * @throws InvalidArgumentException If the admin config is missing, invalid, or malformed. - * @return array */ - protected function createMainMenu($options = null) + protected function createMainMenu($options = null): array { $mainMenuConfig = $this->adminConfig('main_menu'); @@ -791,7 +753,7 @@ protected function createMainMenu($options = null) $menuItems[] = $this->parseMainMenuItem($menuItem, $menuIdent, $mainMenuIdent); } - usort($menuItems, [ 'Charcoal\Admin\Support\Sorter', 'sortByPriority' ]); + usort($menuItems, \Charcoal\Admin\Support\Sorter::sortByPriority(...)); return $menuItems; } @@ -852,9 +814,8 @@ private function mainMenuIdent($options = null) * * @param string $objType The ObjType to search. * @param array|mixed $item The secondary menu item to search in. - * @return boolean */ - protected function isObjTypeInSecondaryMenuItem($objType, $item) + protected function isObjTypeInSecondaryMenuItem($objType, array $item): bool { if (isset($item['links'])) { foreach ($item['links'] as $obj => $i) { @@ -878,8 +839,9 @@ protected function isObjTypeInSecondaryMenuItem($objType, $item) /** * @throws InvalidArgumentException If the secondary menu widget is invalid. * @return \Charcoal\Admin\Widget\SecondaryMenuWidgetInterface[]| + * @return mixed[] */ - protected function createSecondaryMenu() + protected function createSecondaryMenu(): array { $secondaryMenuItems = $this->adminConfig('secondary_menu'); @@ -909,7 +871,7 @@ protected function createSecondaryMenu() } } - usort($menuItems, [ 'Charcoal\Admin\Support\Sorter', 'sortByPriority' ]); + usort($menuItems, \Charcoal\Admin\Support\Sorter::sortByPriority(...)); return $menuItems; } @@ -919,7 +881,7 @@ protected function createSecondaryMenu() * @throws InvalidArgumentException If the menu is missing, invalid, or malformed. * @return array|Generator */ - protected function createSystemMenu($options = null) + protected function createSystemMenu($options = null): array { $menuConfig = $this->adminConfig('system_menu'); @@ -964,7 +926,7 @@ protected function createSystemMenu($options = null) $menuItems[$menuIdent] = $menuItem; } - usort($menuItems, [ 'Charcoal\Admin\Support\Sorter', 'sortByPriority' ]); + usort($menuItems, \Charcoal\Admin\Support\Sorter::sortByPriority(...)); return $menuItems; } @@ -973,18 +935,16 @@ protected function createSystemMenu($options = null) * As a convenience, all admin templates have a model factory to easily create objects. * * @param FactoryInterface $factory The factory used to create models. - * @return void */ - private function setModelFactory(FactoryInterface $factory) + private function setModelFactory(FactoryInterface $factory): void { $this->modelFactory = $factory; } /** * @param FactoryInterface $factory The widget factory, to create the dashboard and secondary menu widgets. - * @return void */ - private function setWidgetFactory(FactoryInterface $factory) + private function setWidgetFactory(FactoryInterface $factory): void { $this->widgetFactory = $factory; } @@ -995,7 +955,7 @@ private function setWidgetFactory(FactoryInterface $factory) * @param string|null $currentIdent The current menu identifier. * @return array Finalized menu structure. */ - private function parseMainMenuItem(array $menuItem, $menuIdent = null, $currentIdent = null) + private function parseMainMenuItem(array $menuItem, $menuIdent = null, $currentIdent = null): array { $svgUri = $this->baseUrl() . 'assets/admin/images/svgs.svg#icon-'; @@ -1007,7 +967,7 @@ private function parseMainMenuItem(array $menuItem, $menuIdent = null, $currentI if (!empty($menuItem['url'])) { $url = $menuItem['url']; - if ($url && strpos($url, ':') === false && !in_array($url[0], [ '/', '#', '?' ])) { + if ($url && !str_contains((string) $url, ':') && !in_array($url[0], [ '/', '#', '?' ])) { $url = $this->adminUrl() . $url; } } else { @@ -1018,7 +978,7 @@ private function parseMainMenuItem(array $menuItem, $menuIdent = null, $currentI if (isset($menuItem['icon'])) { $icon = $menuItem['icon']; - if ($icon && strpos($icon, ':') === false && !in_array($icon[0], [ '/', '#', '?' ])) { + if ($icon && !str_contains((string) $icon, ':') && !in_array($icon[0], [ '/', '#', '?' ])) { $icon = $svgUri . $icon; } } else { @@ -1037,7 +997,7 @@ private function parseMainMenuItem(array $menuItem, $menuIdent = null, $currentI $menuItem['label'] = $this->translator()->translation($menuItem['label']); } - $menuItem['show_label'] = (isset($menuItem['show_label']) ? !!$menuItem['show_label'] : true); + $menuItem['show_label'] = (isset($menuItem['show_label']) ? (bool) $menuItem['show_label'] : true); $menuItem['selected'] = ($menuItem['ident'] === $currentIdent); @@ -1048,9 +1008,7 @@ private function parseMainMenuItem(array $menuItem, $menuIdent = null, $currentI $secondaryMenuWidget = current( array_filter( $this->secondaryMenu(), - function ($item) use ($menuIdent) { - return $item->ident() === $menuIdent; - } + fn($item): bool => $item->ident() === $menuIdent ) ); @@ -1070,7 +1028,7 @@ function ($item) use ($menuIdent) { * @param string|null $currentIdent The current menu identifier. * @return array Finalized menu structure. */ - private function parseSystemMenuItem(array $menuItem, $menuIdent = null, $currentIdent = null) + private function parseSystemMenuItem(array $menuItem, $menuIdent = null, $currentIdent = null): array { if (!isset($menuItem['ident'])) { $menuItem['ident'] = $menuIdent; @@ -1078,7 +1036,7 @@ private function parseSystemMenuItem(array $menuItem, $menuIdent = null, $curren if (!empty($menuItem['url'])) { $url = $menuItem['url']; - if ($url && strpos($url, ':') === false && !in_array($url[0], [ '/', '#', '?' ])) { + if ($url && !str_contains((string) $url, ':') && !in_array($url[0], [ '/', '#', '?' ])) { $url = $this->adminUrl() . $url; } } else { @@ -1125,10 +1083,8 @@ public function htmlAttr() /** * Generate an array containing a list of CSS classes to be used by the tag. - * - * @return array */ - public function htmlClasses() + public function htmlClasses(): array { $classes = [ 'has-no-js' @@ -1143,20 +1099,16 @@ public function htmlClasses() /** * Determine if main & secondary menu should appear as mobile in a desktop resolution. - * - * @return boolean */ - public function isFullscreenTemplate() + public function isFullscreenTemplate(): bool { return false; } /** * Retrieve the default data to the global Admin JavaScript application. - * - * @return array */ - final protected function getDefaultAdminDataForJs() + final protected function getDefaultAdminDataForJs(): array { return [ 'debug' => $this->debug(), @@ -1169,10 +1121,8 @@ final protected function getDefaultAdminDataForJs() /** * Retrieve all data options for the global Admin JavaScript application. - * - * @return array */ - final protected function getAdminDataForJs() + final protected function getAdminDataForJs(): array { if ($this->adminDataForJs === null) { $this->adminDataForJs = $this->getDefaultAdminDataForJs(); @@ -1185,9 +1135,8 @@ final protected function getAdminDataForJs() * Add extra data to the global Admin JavaScript application. * * @param array $data Additional options. - * @return self */ - final public function addAdminDataForJs(array $data) + final public function addAdminDataForJs(array $data): static { $this->adminDataForJs = array_merge($this->getAdminDataForJs(), $data); @@ -1196,14 +1145,10 @@ final public function addAdminDataForJs(array $data) /** * Retrieve the resolved data options for the global Admin JavaScript application. - * - * @return array */ - final public function adminDataForJs() + final public function adminDataForJs(): array { - return array_map(function ($datum) { - return is_callable($datum) ? $datum($this) : $datum; - }, $this->getAdminDataForJs()); + return array_map(fn($datum) => is_callable($datum) ? $datum($this) : $datum, $this->getAdminDataForJs()); } /** @@ -1216,7 +1161,7 @@ final public function adminDataForJsAsJson() $options = (JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); if ($this->debug()) { - $options = ($options | JSON_PRETTY_PRINT); + $options |= JSON_PRETTY_PRINT; } return json_encode($this->adminDataForJs(), $options); @@ -1227,7 +1172,7 @@ final public function adminDataForJsAsJson() * * @return string Returns a stringified JSON object, protected from Mustache rendering. */ - final public function escapedAdminDataForJsAsJson() + final public function escapedAdminDataForJsAsJson(): string { return '{{=<% %>=}}' . $this->adminDataForJsAsJson() . '<%={{ }}=%>'; } diff --git a/packages/admin/src/Charcoal/Admin/AdminWidget.php b/packages/admin/src/Charcoal/Admin/AdminWidget.php index 02d15bef2..5547ab9d5 100644 --- a/packages/admin/src/Charcoal/Admin/AdminWidget.php +++ b/packages/admin/src/Charcoal/Admin/AdminWidget.php @@ -50,20 +50,11 @@ class AdminWidget extends AbstractWidget implements */ public $widgetId; - /** - * @var string $type - */ - private $type; + private ?string $type = null; - /** - * @var string $template - */ - private $template; + private ?string $template = null; - /** - * @var string $ident - */ - private $ident = ''; + private ?string $ident = ''; /** * @var Translation|string|null $label @@ -75,15 +66,9 @@ class AdminWidget extends AbstractWidget implements */ private $lang; - /** - * @var boolean $showLabel - */ - private $showLabel; + private ?bool $showLabel = null; - /** - * @var boolean $showActions - */ - private $showActions; + private ?bool $showActions = null; /** * The widget's conditional logic. @@ -101,22 +86,15 @@ class AdminWidget extends AbstractWidget implements /** * Extra data sources to merge when setting data on an entity. - * - * @var array */ - private $dataSources; + private ?array $dataSources = null; /** * Associative array of source identifiers and options to apply when merging. - * - * @var array */ - private $dataSourceFilters = []; + private array $dataSourceFilters = []; - /** - * @var FactoryInterface $modelFactory - */ - private $modelFactory; + private ?\Charcoal\Factory\FactoryInterface $modelFactory = null; /** * Enable / Disable the widget. @@ -126,13 +104,10 @@ class AdminWidget extends AbstractWidget implements * @param mixed $active The active flag or condition. * @return self */ + #[\Override] public function setActive($active) { - if (is_callable($active) || is_string($active)) { - $condition = $active; - } else { - $condition = null; - } + $condition = is_callable($active) || is_string($active) ? $active : null; $this->activeCondition = $condition; @@ -142,6 +117,7 @@ public function setActive($active) /** * @return boolean */ + #[\Override] public function active() { if ($this->activeCondition !== null) { @@ -154,9 +130,8 @@ public function active() /** * @param string $template The UI item's template (identifier). * @throws InvalidArgumentException If the template identifier is not a string. - * @return self */ - public function setTemplate($template) + public function setTemplate($template): static { if ($template === null) { $this->template = null; @@ -177,7 +152,7 @@ public function setTemplate($template) /** * @return string */ - public function template() + public function template(): ?string { if ($this->template === null) { return $this->type(); @@ -188,9 +163,8 @@ public function template() /** * @param string $widgetId The widget identifier. - * @return self */ - public function setWidgetId($widgetId) + public function setWidgetId($widgetId): static { $this->widgetId = $widgetId; @@ -212,9 +186,8 @@ public function widgetId() /** * @param string $type The widget type. * @throws InvalidArgumentException If the argument is not a string. - * @return self */ - public function setType($type) + public function setType($type): static { if ($type === null) { $this->type = null; @@ -235,7 +208,7 @@ public function setType($type) /** * @return string */ - public function type() + public function type(): ?string { return $this->type; } @@ -245,7 +218,7 @@ public function type() * @throws InvalidArgumentException If the ident is not a string. * @return AdminWidget (Chainable) */ - public function setIdent($ident) + public function setIdent($ident): static { if ($ident === null) { $this->ident = null; @@ -266,7 +239,7 @@ public function setIdent($ident) /** * @return string */ - public function ident() + public function ident(): ?string { return $this->ident; } @@ -277,9 +250,8 @@ public function ident() * @param mixed $sources One or more data source identifiers to merge data from. * Pass NULL to reset the entity back to default sources. * Pass FALSE, an empty string or array to disable extra sources. - * @return self */ - public function setDataSources($sources) + public function setDataSources($sources): static { if ($sources === null) { $this->dataSources = null; @@ -327,19 +299,13 @@ public function dataSourceFilter($sourceIdent) $filters = array_merge($this->defaultDataSourceFilters(), $this->dataSourceFilters); - if (isset($filters[$sourceIdent])) { - return $filters[$sourceIdent]; - } - - return null; + return $filters[$sourceIdent] ?? null; } /** * Retrieve the widget's data options for JavaScript components. - * - * @return array */ - public function widgetDataForJs() + public function widgetDataForJs(): array { return []; } @@ -354,7 +320,7 @@ final public function widgetDataForJsAsJson() $options = (JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); if ($this->debug()) { - $options = ($options | JSON_PRETTY_PRINT); + $options |= JSON_PRETTY_PRINT; } return json_encode($this->widgetDataForJs(), $options); @@ -365,16 +331,15 @@ final public function widgetDataForJsAsJson() * * @return string Returns a stringified JSON object, protected from Mustache rendering. */ - final public function escapedWidgetDataForJsAsJson() + final public function escapedWidgetDataForJsAsJson(): string { return '{{=<% %>=}}' . $this->widgetDataForJsAsJson() . '<%={{ }}=%>'; } /** * @param mixed $label The label. - * @return self */ - public function setLabel($label) + public function setLabel($label): static { $this->label = $this->translator()->translation($label); @@ -389,21 +354,17 @@ public function label() return $this->label; } - /** - * @return array - */ - public function actions() + public function actions(): array { return []; } /** * @param boolean $show The show actions flag. - * @return self */ - public function setShowActions($show) + public function setShowActions($show): static { - $this->showActions = !!$show; + $this->showActions = (bool) $show; return $this; } @@ -421,11 +382,10 @@ public function showActions() /** * @param boolean $show The show label flag. - * @return self */ - public function setShowLabel($show) + public function setShowLabel($show): static { - $this->showLabel = !!$show; + $this->showLabel = (bool) $show; return $this; } @@ -435,7 +395,7 @@ public function setShowLabel($show) public function showLabel() { if ($this->showLabel !== false) { - return !!strval($this->label()); + return (bool) strval($this->label()); } else { return false; } @@ -447,6 +407,7 @@ public function showLabel() * @param Container $container DI Container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -483,7 +444,7 @@ protected function setModelFactory(FactoryInterface $factory) /** * @return FactoryInterface The model factory. */ - protected function modelFactory() + protected function modelFactory(): ?\Charcoal\Factory\FactoryInterface { return $this->modelFactory; } @@ -492,19 +453,18 @@ protected function modelFactory() * Parse the widget's conditional logic. * * @param callable|string $condition The callable or renderable condition. - * @return boolean */ - protected function resolveConditionalLogic($condition) + protected function resolveConditionalLogic($condition): bool { if (is_callable([ $this, $condition ])) { - return !!$this->{$condition}(); + return (bool) $this->{$condition}(); } elseif (is_callable($condition)) { - return !!$condition(); - } elseif ($this->view()) { - return !!$this->renderTemplate($condition); + return (bool) $condition(); + } elseif ($this->view() instanceof \Charcoal\View\ViewInterface) { + return (bool) $this->renderTemplate($condition); } - return !!$condition; + return (bool) $condition; } /** @@ -513,9 +473,8 @@ protected function resolveConditionalLogic($condition) * @param mixed $sourceIdent The data source identifier. * @param mixed $sourceFilter Optional filter to apply to the source's data. * @throws InvalidArgumentException If the data source is invalid. - * @return self */ - protected function addDataSources($sourceIdent, $sourceFilter = null) + protected function addDataSources($sourceIdent, $sourceFilter = null): static { $validSources = $this->acceptedDataSources(); @@ -552,7 +511,7 @@ protected function addDataSources($sourceIdent, $sourceFilter = null) * * @return string[] */ - protected function acceptedDataSources() + protected function acceptedDataSources(): array { return [ static::DATA_SOURCE_REQUEST, static::DATA_SOURCE_OBJECT, static::DATA_SOURCE_METADATA ]; } @@ -562,17 +521,15 @@ protected function acceptedDataSources() * * @return string[] */ - protected function defaultDataSources() + protected function defaultDataSources(): array { return []; } /** * Retrieve the default data source filters (when setting data on an entity). - * - * @return array */ - protected function defaultDataSourceFilters() + protected function defaultDataSourceFilters(): array { return []; } @@ -625,9 +582,8 @@ protected function resolveDataSourceFilter($toResolve) * Retrieve the available data sources (when setting data on an entity). * * @param array|mixed $dataset The entity data. - * @return self */ - protected function mergeDataSources($dataset = null) + protected function mergeDataSources($dataset = null): static { $sources = $this->dataSources(); foreach ($sources as $sourceIdent) { diff --git a/packages/admin/src/Charcoal/Admin/AssetsConfig.php b/packages/admin/src/Charcoal/Admin/AssetsConfig.php index 77e66aa65..dccf6e9c2 100644 --- a/packages/admin/src/Charcoal/Admin/AssetsConfig.php +++ b/packages/admin/src/Charcoal/Admin/AssetsConfig.php @@ -1,5 +1,7 @@ collections = $collections; diff --git a/packages/admin/src/Charcoal/Admin/Config.php b/packages/admin/src/Charcoal/Admin/Config.php index 0f750216a..d417a566d 100644 --- a/packages/admin/src/Charcoal/Admin/Config.php +++ b/packages/admin/src/Charcoal/Admin/Config.php @@ -18,20 +18,15 @@ class Config extends AbstractConfig /** * The base path for the admin module's route group. - * - * @var string $basePath */ - private $basePath = self::DEFAULT_BASE_PATH; + private string $basePath = self::DEFAULT_BASE_PATH; /** * @var array */ public $routes = []; - /** - * @var array - */ - private $handlers = []; + private array $handlers = []; /** * @var array @@ -50,6 +45,7 @@ class Config extends AbstractConfig * * @return array */ + #[\Override] public function defaults() { $baseDir = rtrim(realpath(__DIR__ . '/../../../'), '/'); @@ -63,9 +59,8 @@ public function defaults() * * @param string $path The admin module base path. * @throws InvalidArgumentException If the route group is invalid. - * @return self */ - public function setBasePath($path) + public function setBasePath($path): static { if (!is_string($path)) { throw new InvalidArgumentException( @@ -74,7 +69,7 @@ public function setBasePath($path) } // Can not be empty - if ($path == '') { + if ($path === '') { throw new InvalidArgumentException( 'Path can not be empty' ); @@ -86,10 +81,8 @@ public function setBasePath($path) /** * Retrieve the admin module's route group. - * - * @return string */ - public function basePath() + public function basePath(): string { return $this->basePath; } @@ -99,9 +92,8 @@ public function basePath() * * @see \Charcoal\App\AppConfig::setRoutes() For a similar implementation. * @param array $routes The route configuration structure to set. - * @return self */ - public function setRoutes(array $routes) + public function setRoutes(array $routes): static { $toIterate = RouteConfig::defaultRouteTypes(); foreach ($routes as $key => $val) { @@ -126,9 +118,8 @@ public function setRoutes(array $routes) * - "phpErrorHandler" * * @param array $handlers The handlers configuration structure to set. - * @return self */ - public function setHandlers(array $handlers) + public function setHandlers(array $handlers): static { $this->handlers = array_fill_keys(HandlerConfig::defaultHandlerTypes(), []); $this->handlers['defaults'] = []; @@ -143,10 +134,7 @@ public function setHandlers(array $handlers) return $this; } - /** - * @return array - */ - public function handlers() + public function handlers(): array { return $this->handlers; } @@ -156,9 +144,8 @@ public function handlers() * * @param array $view The global configset for the application's view service. * @throws InvalidArgumentException If the argument is not a configset. - * @return self */ - public function setView(array $view) + public function setView(array $view): static { $this->view = $view; return $this; diff --git a/packages/admin/src/Charcoal/Admin/Decorator/GridStackWidgetDecorator.php b/packages/admin/src/Charcoal/Admin/Decorator/GridStackWidgetDecorator.php index c4b3860d1..f60fa0484 100644 --- a/packages/admin/src/Charcoal/Admin/Decorator/GridStackWidgetDecorator.php +++ b/packages/admin/src/Charcoal/Admin/Decorator/GridStackWidgetDecorator.php @@ -21,18 +21,12 @@ class GridStackWidgetDecorator */ protected $gridStack = []; - /** - * @var WidgetInterface - */ - protected $widget; - /** * GridStackWidgetDecorator constructor. * @param WidgetInterface $widget The widget to decorate. */ - public function __construct(WidgetInterface $widget) + public function __construct(protected \Charcoal\App\Template\WidgetInterface $widget) { - $this->widget = $widget; } /** @@ -60,10 +54,8 @@ public function gridStack() /** * The default Grid Stack dataset. - * - * @return array */ - private function defaultGridStack() + private function defaultGridStack(): array { return [ 'width' => self::GS_WIDTH, diff --git a/packages/admin/src/Charcoal/Admin/Docs/Template/Object/DocTemplate.php b/packages/admin/src/Charcoal/Admin/Docs/Template/Object/DocTemplate.php index a3f8bdee5..3d037bcd7 100644 --- a/packages/admin/src/Charcoal/Admin/Docs/Template/Object/DocTemplate.php +++ b/packages/admin/src/Charcoal/Admin/Docs/Template/Object/DocTemplate.php @@ -19,6 +19,7 @@ class DocTemplate extends AdminTemplate implements DashboardContainerInterface, ObjectContainerInterface { + public $headerMenu; use DashboardContainerTrait; use ObjectContainerTrait; @@ -27,7 +28,8 @@ class DocTemplate extends AdminTemplate implements * * @return string[] */ - protected function validDataFromRequest() + #[\Override] + protected function validDataFromRequest(): array { return array_merge([ 'obj_type' @@ -59,9 +61,10 @@ public function headerMenu() * * @return \Charcoal\Translator\Translation */ + #[\Override] public function title() { - if (isset($this->title)) { + if ($this->title !== null) { return $this->title; } @@ -81,7 +84,7 @@ public function title() $metadata = $obj->metadata(); $objLabel = null; - if (!$objLabel && isset($metadata['admin']['forms'])) { + if (isset($metadata['admin']['forms'])) { $adminMetadata = $metadata['admin']; $formIdent = filter_input(INPUT_GET, 'form_ident', FILTER_SANITIZE_STRING); @@ -113,11 +116,7 @@ public function title() } } - if ($this->isObjRenderable($obj)) { - $this->title = $obj->render((string)$objLabel, $obj); - } else { - $this->title = (string)$objLabel; - } + $this->title = $this->isObjRenderable($obj) ? $obj->render((string)$objLabel, $obj) : (string)$objLabel; return $this->title; } @@ -126,6 +125,7 @@ public function title() * @param Container $container DI container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -150,10 +150,8 @@ protected function createDashboardConfig() $dashboardIdent = filter_input(INPUT_GET, 'dashboard_ident', FILTER_SANITIZE_STRING); } - if (empty($dashboardIdent)) { - if (isset($adminMetadata['default_doc_dashboard'])) { - $dashboardIdent = $adminMetadata['default_doc_dashboard']; - } + if (empty($dashboardIdent) && isset($adminMetadata['default_doc_dashboard'])) { + $dashboardIdent = $adminMetadata['default_doc_dashboard']; } $overrideType = false; @@ -162,7 +160,7 @@ protected function createDashboardConfig() if (!isset($adminMetadata['default_edit_dashboard'])) { throw new Exception(sprintf( 'No default doc dashboard defined in admin metadata for %s', - get_class($this->obj()) + $this->obj()::class )); } $overrideType = true; @@ -199,11 +197,11 @@ protected function objAdminMetadata() $objMetadata = $obj->metadata(); - $adminMetadata = isset($objMetadata['admin']) ? $objMetadata['admin'] : null; + $adminMetadata = $objMetadata['admin'] ?? null; if ($adminMetadata === null) { throw new Exception(sprintf( 'The object %s does not have an admin metadata.', - get_class($obj) + $obj::class )); } diff --git a/packages/admin/src/Charcoal/Admin/Docs/Widget/DocFormPropertyWidget.php b/packages/admin/src/Charcoal/Admin/Docs/Widget/DocFormPropertyWidget.php index 18fc3ab6e..7d3e22559 100644 --- a/packages/admin/src/Charcoal/Admin/Docs/Widget/DocFormPropertyWidget.php +++ b/packages/admin/src/Charcoal/Admin/Docs/Widget/DocFormPropertyWidget.php @@ -19,12 +19,9 @@ class DocFormPropertyWidget extends FormPropertyWidget */ protected $displayOptions; - /** - * @return boolean - */ - public function hasExtraData() + public function hasExtraData(): bool { - return !!count($this->extraData()); + return (bool) count($this->extraData()); } /** @@ -34,7 +31,7 @@ public function collapsible() { $displayOps = $this->displayOptions(); - return isset($displayOps['collapsible']) ? $displayOps['collapsible'] : false; + return $displayOps['collapsible'] ?? false; } /** @@ -44,7 +41,7 @@ public function collapsed() { $displayOps = $this->displayOptions(); - return isset($displayOps['collapsed']) ? $displayOps['collapsed'] : false; + return $displayOps['collapsed'] ?? false; } /** @@ -54,7 +51,7 @@ public function parented() { $displayOps = $this->displayOptions(); - return isset($displayOps['parented']) ? $displayOps['parented'] : false; + return $displayOps['parented'] ?? false; } /** @@ -64,34 +61,25 @@ public function typeDescription() { $type = $this->prop()->type(); - switch ($type) { - case 'boolean': - return $this->translator()->translation(' + return match ($type) { + 'boolean' => $this->translator()->translation(' The field is a TRUE | FALSE statement - '); - case 'image': - case 'audio': - case 'file': - return $this->translator()->translation(' + '), + 'image', 'audio', 'file' => $this->translator()->translation(' The field will ask to upload a file using the file manager - '); - case 'string': - case 'text': - return $this->translator()->translation(' + '), + 'string', 'text' => $this->translator()->translation(' The field is a simple text input - '); - case 'object': - return $this->translator()->translation(' + '), + 'object' => $this->translator()->translation(' The field is a relation to another object in the back-end (ex: a category object) - '); - case 'date-time': - return $this->translator()->translation(' + '), + 'date-time' => $this->translator()->translation(' The field requires a date and will prompt a date picker
as an easy way to provide it in a supported format - '); - default: - return ''; - } + '), + default => '', + }; } /** @@ -99,7 +87,7 @@ public function typeDescription() */ public function extraData() { - if (isset($this->extraData)) { + if ($this->extraData !== null) { return $this->extraData; } @@ -147,9 +135,8 @@ public function displayOptions() /** * @param array|mixed $displayOptions The display options array. * @throws \InvalidArgumentException If argument is not of type "array". - * @return self */ - public function setDisplayOptions($displayOptions) + public function setDisplayOptions($displayOptions): static { if (!is_array($displayOptions)) { throw new \InvalidArgumentException('display_options should be of type array'); diff --git a/packages/admin/src/Charcoal/Admin/Docs/Widget/FormGroup/DocFormGroup.php b/packages/admin/src/Charcoal/Admin/Docs/Widget/FormGroup/DocFormGroup.php index a21352a26..151d94264 100644 --- a/packages/admin/src/Charcoal/Admin/Docs/Widget/FormGroup/DocFormGroup.php +++ b/packages/admin/src/Charcoal/Admin/Docs/Widget/FormGroup/DocFormGroup.php @@ -1,5 +1,7 @@ description() || $this->notes() || count($this->groupProperties())) { - return false; - } - - return true; + return !($this->description() || $this->notes() || count($this->groupProperties())); } } diff --git a/packages/admin/src/Charcoal/Admin/Mustache/AssetsHelpers.php b/packages/admin/src/Charcoal/Admin/Mustache/AssetsHelpers.php index 21e8ef86a..e5c94411f 100644 --- a/packages/admin/src/Charcoal/Admin/Mustache/AssetsHelpers.php +++ b/packages/admin/src/Charcoal/Admin/Mustache/AssetsHelpers.php @@ -19,27 +19,18 @@ class AssetsHelpers implements HelpersInterface /** * @var AssetManager|mixed $assets The assetic assets manager. */ - private $assets; + private ?\Assetic\AssetManager $assets = null; - /** - * @var string $action - */ - private $action; + private ?string $action = null; - /** - * @var string $collection - */ - private $collection; + private ?string $collection = null; - /** - * @var string $ident - */ - private $ident; + private ?string $ident = null; /** * @param array $data Class Dependencies. */ - public function __construct(array $data = null) + public function __construct(?array $data = null) { if (isset($data['assets']) && $data['assets'] instanceof AssetManager) { $this->assets = $data['assets']; @@ -48,8 +39,6 @@ public function __construct(array $data = null) /** * Get the collection of helpers as a plain array. - * - * @return array */ public function toArray(): array { @@ -80,9 +69,9 @@ protected function reset() * @param LambdaHelper|null $helper For rendering strings in the current context. * @return string */ - public function __invoke($text = null, LambdaHelper $helper = null) + public function __invoke($text = null, ?LambdaHelper $helper = null) { - if ($helper) { + if ($helper instanceof \Mustache_LambdaHelper) { $text = $helper->render($text); } $return = $this->{$this->action}($this->collection, $text); @@ -90,7 +79,7 @@ public function __invoke($text = null, LambdaHelper $helper = null) $this->reset(); - if ($helper) { + if ($helper instanceof \Mustache_LambdaHelper) { return $helper->render($text); } @@ -105,7 +94,7 @@ public function __invoke($text = null, LambdaHelper $helper = null) * @param string $macro A domain, locale, or number. * @return boolean */ - public function __isset($macro) + public function __isset(string $macro) { return boolval($macro); } @@ -116,9 +105,8 @@ public function __isset($macro) * Required by Mustache. * * @param string $macro A domain, locale, or number. - * @return mixed */ - public function __get($macro) + public function __get(string $macro): mixed { if (!$this->action) { $macro = '__' . $macro; @@ -154,7 +142,7 @@ public function __get($macro) * @param string $text Asset string to inject. * @return string */ - protected function __inject($collection, $text) + protected function __inject($collection, $text): null { if (!$this->assets->has($collection)) { $this->assets->set($collection, new AssetCollection()); @@ -180,9 +168,8 @@ protected function __enqueue($collection) /** * @param string $collection The collection ident. - * @return string */ - protected function __output($collection) + protected function __output($collection): string { $dump = $this->assets->get($collection)->dump(); return '{{=<<<<% %>>>>=}}' . $dump . '<<<<%={{ }}=%>>>>'; diff --git a/packages/admin/src/Charcoal/Admin/Object/Notification.php b/packages/admin/src/Charcoal/Admin/Object/Notification.php index aa1da3ffb..ecb2657f9 100644 --- a/packages/admin/src/Charcoal/Admin/Object/Notification.php +++ b/packages/admin/src/Charcoal/Admin/Object/Notification.php @@ -1,5 +1,7 @@ users = []; @@ -58,14 +56,11 @@ public function setUsers($users) 'Users must be an array or a comma-separated string.' ); } - $this->users = array_map('trim', $users); + $this->users = array_map(trim(...), $users); return $this; } - /** - * @return array - */ - public function users() + public function users(): array { return $this->users; } @@ -75,7 +70,7 @@ public function users() * @throws InvalidArgumentException If the types are not an array or a comma-separated string. * @return Notification Chainable */ - public function setTargetTypes($targetTypes) + public function setTargetTypes($targetTypes): static { if ($targetTypes === null) { $this->targetTypes = null; @@ -89,14 +84,11 @@ public function setTargetTypes($targetTypes) 'Object types must be an array or a comma-separated string.' ); } - $this->targetTypes = array_map('trim', $targetTypes); + $this->targetTypes = array_map(trim(...), $targetTypes); return $this; } - /** - * @return array|null - */ - public function targetTypes() + public function targetTypes(): ?array { return $this->targetTypes; } @@ -106,7 +98,7 @@ public function targetTypes() * @throws InvalidArgumentException If the emails are not an array or a comma-separated string. * @return Notification Chainable */ - public function setExtraEmails($extraEmails) + public function setExtraEmails($extraEmails): static { if ($extraEmails === null) { $this->extraEmails = []; @@ -120,14 +112,11 @@ public function setExtraEmails($extraEmails) 'Extra emails must be an array or a comma-separated string.' ); } - $this->extraEmails = array_map('trim', $extraEmails); + $this->extraEmails = array_map(trim(...), $extraEmails); return $this; } - /** - * @return array|null - */ - public function extraEmails() + public function extraEmails(): array { return $this->extraEmails; } @@ -137,7 +126,7 @@ public function extraEmails() * @throws InvalidArgumentException If the frequency is not a valid mode. * @return Notification Chainable */ - public function setFrequency($frequency) + public function setFrequency($frequency): static { if ($frequency === null) { $this->frequency = null; @@ -162,7 +151,7 @@ public function setFrequency($frequency) /** * @return boolean */ - public function frequency() + public function frequency(): ?string { return $this->frequency; } @@ -171,16 +160,13 @@ public function frequency() * @param boolean $active The active flag. * @return Notification Chainable */ - public function setActive($active) + public function setActive($active): static { - $this->active = !!$active; + $this->active = (bool) $active; return $this; } - /** - * @return boolean - */ - public function active() + public function active(): bool { return $this->active; } diff --git a/packages/admin/src/Charcoal/Admin/Property/AbstractProperty.php b/packages/admin/src/Charcoal/Admin/Property/AbstractProperty.php index 2b03c9f7d..075effcce 100644 --- a/packages/admin/src/Charcoal/Admin/Property/AbstractProperty.php +++ b/packages/admin/src/Charcoal/Admin/Property/AbstractProperty.php @@ -51,6 +51,8 @@ abstract class AbstractProperty implements */ private $property; + protected $inputName; + /** * @var array $propertyData */ @@ -371,9 +373,7 @@ public function parseEscapeOptions($escape) */ protected function wrapEscapeFunction(callable $callback) { - return function ($value) use ($callback) { - return call_user_func_array($callback, func_get_args()); - }; + return fn($value): mixed => call_user_func_array($callback, func_get_args()); } /** @@ -385,19 +385,17 @@ protected function wrapEscapeFunction(callable $callback) */ protected function assertValidEscapeFunction($escape) { - if (is_string($escape)) { - if (!function_exists($escape)) { - throw new InvalidArgumentException(sprintf( - 'Undefined escape function named "%s"', - $escape - )); - } + if (is_string($escape) && !function_exists($escape)) { + throw new InvalidArgumentException(sprintf( + 'Undefined escape function named "%s"', + $escape + )); } if (!is_callable($escape)) { throw new InvalidArgumentException(sprintf( 'Expected escape function name or function expression, received "%s"', - is_object($escape) ? get_class($escape) : gettype($escape) + get_debug_type($escape) )); } } @@ -420,7 +418,7 @@ public function getDefaultEscapeOptions() */ public function setMultiple($multiple) { - $this->multiple = !!$multiple; + $this->multiple = (bool) $multiple; return $this; } @@ -453,9 +451,7 @@ public function renderTranslatableTemplate($templateString) if (!$isBlank) { $this->translator()->setLocale($lang); $translation = $this->renderTemplate($translation); - if ($translation !== null) { - $templateString[$lang] = $translation; - } + $templateString[$lang] = $translation; } } $this->translator()->setLocale($origLang); @@ -463,7 +459,7 @@ public function renderTranslatableTemplate($templateString) return $templateString; } elseif (is_string($templateString)) { - $isBlank = empty($templateString) && !is_numeric($templateString); + $isBlank = ($templateString === '' || $templateString === '0') && !is_numeric($templateString); if (!$isBlank) { return $this->renderTemplate($templateString); } @@ -495,7 +491,7 @@ protected function isObjRenderable($obj, $toString = false) return false; } - $key = get_class($obj); + $key = $obj::class; if (isset(static::$objRenderableCache[$key])) { return static::$objRenderableCache[$key]; @@ -542,7 +538,7 @@ protected function setDependencies(Container $container) * @param array $data Optional metadata to merge on the object. * @return PropertyMetadata */ - protected function createMetadata(array $data = null) + protected function createMetadata(?array $data = null) { $class = $this->metadataClass(); return new $class($data); @@ -576,7 +572,7 @@ protected function getter($key) * @param string $key The key to get the setter from. * @return string The setter method name, for a given key. */ - protected function setter($key) + protected function setter(string $key) { $setter = 'set_' . $key; return $this->camelize($setter); @@ -588,8 +584,8 @@ protected function setter($key) * @param string $str The snake_case string to camelize. * @return string The camelCase string. */ - private function camelize($str) + private function camelize($str): string { - return lcfirst(implode('', array_map('ucfirst', explode('_', $str)))); + return lcfirst(implode('', array_map(ucfirst(...), explode('_', $str)))); } } diff --git a/packages/admin/src/Charcoal/Admin/Property/AbstractPropertyDisplay.php b/packages/admin/src/Charcoal/Admin/Property/AbstractPropertyDisplay.php index 886255d5c..174ab09da 100644 --- a/packages/admin/src/Charcoal/Admin/Property/AbstractPropertyDisplay.php +++ b/packages/admin/src/Charcoal/Admin/Property/AbstractPropertyDisplay.php @@ -56,6 +56,7 @@ abstract class AbstractPropertyDisplay extends AbstractProperty implements * @param PropertyInterface $property The property. * @return self */ + #[\Override] public function setProperty(PropertyInterface $property) { parent::setProperty($property); @@ -165,11 +166,7 @@ public function setDisplayName($displayName) */ public function displayName() { - if ($this->displayName) { - $name = $this->displayName; - } else { - $name = $this->propertyIdent(); - } + $name = $this->displayName ?: $this->propertyIdent(); if ($this->p()['l10n']) { $name .= '[' . $this->lang() . ']'; @@ -206,11 +203,7 @@ public function getDisplayOption($key, $default = null) { $options = $this->getDisplayOptions(); - if (isset($options[$key])) { - return $options[$key]; - } - - return $default; + return $options[$key] ?? $default; } /** @@ -291,6 +284,7 @@ public function getDisplayEscapeOptions() * @throws InvalidArgumentException If the value to escape is not a string. * @return string */ + #[\Override] public function escapeVal($val, array $options = []) { if (!is_string($val)) { @@ -342,7 +336,7 @@ public function displayVal() if (!is_scalar($val)) { throw new UnexpectedValueException(sprintf( 'Property Display Value must be a string, received %s', - (is_object($val) ? get_class($val) : gettype($val)) + (get_debug_type($val)) )); } diff --git a/packages/admin/src/Charcoal/Admin/Property/AbstractPropertyInput.php b/packages/admin/src/Charcoal/Admin/Property/AbstractPropertyInput.php index cc220d2d8..266e9263c 100644 --- a/packages/admin/src/Charcoal/Admin/Property/AbstractPropertyInput.php +++ b/packages/admin/src/Charcoal/Admin/Property/AbstractPropertyInput.php @@ -33,6 +33,7 @@ abstract class AbstractPropertyInput extends AbstractProperty implements /** * @var string $inputName */ + #[\Override] protected $inputName; /** @@ -97,6 +98,7 @@ abstract class AbstractPropertyInput extends AbstractProperty implements * @param PropertyInterface $property The property. * @return self */ + #[\Override] public function setProperty(PropertyInterface $property) { parent::setProperty($property); @@ -212,11 +214,7 @@ public function setInputName($inputName) */ public function inputName() { - if ($this->inputName) { - $name = $this->inputName; - } else { - $name = $this->propertyIdent(); - } + $name = $this->inputName ?: $this->propertyIdent(); if ($this->p()['l10n']) { $name .= '[' . $this->lang() . ']'; @@ -253,11 +251,7 @@ public function getInputOption($key, $default = null) { $options = $this->getInputOptions(); - if (isset($options[$key])) { - return $options[$key]; - } - - return $default; + return $options[$key] ?? $default; } /** @@ -342,6 +336,7 @@ public function getInputEscapeOptions() * @throws InvalidArgumentException If the value to escape is not a string. * @return string */ + #[\Override] public function escapeVal($val, array $options = []) { if (!is_string($val)) { @@ -379,8 +374,6 @@ public function escapeVal($val, array $options = []) /** * Overridable. * Makes it easier to pass InputVal options from children input types. - * - * @return array **/ public function getInputValOptions(): array { @@ -409,7 +402,7 @@ public function inputVal() if (!is_scalar($val)) { throw new UnexpectedValueException(sprintf( 'Property Input Value must be a string, received %s', - (is_object($val) ? get_class($val) : gettype($val)) + (get_debug_type($val)) )); } @@ -510,7 +503,7 @@ public function hasInputPrefix() public function inputPrefix() { if ($this->inputPrefix instanceof Translation) { - if (isset($this->inputPrefix->isRendered) && $this->inputPrefix->isRendered === false) { + if (property_exists($this->inputPrefix, 'isRendered') && $this->inputPrefix->isRendered !== null && $this->inputPrefix->isRendered === false) { $this->inputPrefix = $this->renderTranslatableTemplate($this->inputPrefix); } @@ -561,7 +554,7 @@ public function hasInputSuffix() public function inputSuffix() { if ($this->inputSuffix instanceof Translation) { - if (isset($this->inputSuffix->isRendered) && $this->inputSuffix->isRendered === false) { + if (property_exists($this->inputSuffix, 'isRendered') && $this->inputSuffix->isRendered !== null && $this->inputSuffix->isRendered === false) { $this->inputSuffix = $this->renderTranslatableTemplate($this->inputSuffix); } @@ -578,13 +571,7 @@ public function inputSuffix() */ public function hidden() { - if ($this->p()['l10n']) { - if ($this->lang() != $this->translator()->getLocale()) { - return true; - } - } - - return false; + return $this->p()['l10n'] && $this->lang() != $this->translator()->getLocale(); } /** @@ -593,7 +580,7 @@ public function hidden() */ public function setReadOnly($readOnly) { - $this->readOnly = !!$readOnly; + $this->readOnly = (bool) $readOnly; return $this; } @@ -611,7 +598,7 @@ public function readOnly() */ public function setRequired($required) { - $this->required = !!$required; + $this->required = (bool) $required; return $this; } @@ -629,7 +616,7 @@ public function required() */ public function setDisabled($disabled) { - $this->disabled = !!$disabled; + $this->disabled = (bool) $disabled; return $this; } @@ -689,7 +676,7 @@ public function placeholder() } if ($this->placeholder instanceof Translation) { - if (isset($this->placeholder->isRendered) && $this->placeholder->isRendered === false) { + if (property_exists($this->placeholder, 'isRendered') && $this->placeholder->isRendered !== null && $this->placeholder->isRendered === false) { $this->placeholder = $this->renderTranslatableTemplate($this->placeholder); } @@ -721,7 +708,7 @@ final public function controlDataForJsAsJson() $options = (JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); if ($this->debug()) { - $options = ($options | JSON_PRETTY_PRINT); + $options |= JSON_PRETTY_PRINT; } return json_encode($this->controlDataForJs(), $options); diff --git a/packages/admin/src/Charcoal/Admin/Property/AbstractSelectableInput.php b/packages/admin/src/Charcoal/Admin/Property/AbstractSelectableInput.php index 97ed1631b..c012815c3 100644 --- a/packages/admin/src/Charcoal/Admin/Property/AbstractSelectableInput.php +++ b/packages/admin/src/Charcoal/Admin/Property/AbstractSelectableInput.php @@ -140,7 +140,7 @@ public function parsedVal() // Doing this in the parseVal method of abstract property // was causing multiple && l10n properties not to save. if (!is_array($val) && $this->p()['multiple']) { - $val = explode($this->p()->multipleSeparator(), $val); + $val = explode($this->p()->multipleSeparator(), (string) $val); } $this->parsedVal[$this->lang()] = $val; @@ -196,7 +196,7 @@ public function setEmptyChoice($choice) } else { throw new InvalidArgumentException(sprintf( 'Empty choice must be an array, received %s', - (is_object($choice) ? get_class($choice) : gettype($choice)) + (get_debug_type($choice)) )); } @@ -256,7 +256,7 @@ protected function renderChoiceObjMap($obj, $prop) return ''; } - if (strpos($prop, '{{') === false) { + if (!str_contains($prop, '{{')) { if (isset($obj[$prop])) { return $this->parseChoiceVal($obj[$prop]); } @@ -268,7 +268,7 @@ protected function renderChoiceObjMap($obj, $prop) return $obj->renderTemplate($prop); } else { $callback = function ($matches) use ($obj) { - $prop = trim($matches[1]); + $prop = trim((string) $matches[1]); if (isset($obj[$prop])) { return $this->parseChoiceVal($obj[$prop]); } @@ -341,7 +341,7 @@ public function choiceObjMap() public function inputNameFallback() { $name = $this->inputName(); - if (substr($name, -2, 2) === '[]') { + if (str_ends_with($name, '[]')) { return substr($name, 0, -2); } diff --git a/packages/admin/src/Charcoal/Admin/Property/AbstractTickableInput.php b/packages/admin/src/Charcoal/Admin/Property/AbstractTickableInput.php index 99c90a766..7a0bbca7c 100644 --- a/packages/admin/src/Charcoal/Admin/Property/AbstractTickableInput.php +++ b/packages/admin/src/Charcoal/Admin/Property/AbstractTickableInput.php @@ -18,10 +18,8 @@ abstract class AbstractTickableInput extends AbstractSelectableInput /** * How radio controls should be displayed. - * - * @var string|null */ - private $inputLayout; + private ?string $inputLayout = null; /** * Prepare a single tickable option for output. @@ -30,6 +28,7 @@ abstract class AbstractTickableInput extends AbstractSelectableInput * @param array|object $choice The choice structure. * @return array|null */ + #[\Override] protected function parseChoice($ident, $choice) { $choice = parent::parseChoice($ident, $choice); @@ -63,7 +62,7 @@ public function setInputLayout($layout) if (!is_string($layout)) { throw new InvalidArgumentException(sprintf( 'Layout must be a string, received %s', - (is_object($layout) ? get_class($layout) : gettype($layout)) + (get_debug_type($layout)) )); } diff --git a/packages/admin/src/Charcoal/Admin/Property/Display/BooleanDisplay.php b/packages/admin/src/Charcoal/Admin/Property/Display/BooleanDisplay.php index 50b93c8df..ce09a3910 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Display/BooleanDisplay.php +++ b/packages/admin/src/Charcoal/Admin/Property/Display/BooleanDisplay.php @@ -1,5 +1,7 @@ '', @@ -91,6 +92,7 @@ protected function defaultEmptyChoice() * * @return string */ + #[\Override] public function displayVal() { $prop = $this->p(); diff --git a/packages/admin/src/Charcoal/Admin/Property/Display/ColorSwatchDisplay.php b/packages/admin/src/Charcoal/Admin/Property/Display/ColorSwatchDisplay.php index da3ae4cbf..2cd596706 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Display/ColorSwatchDisplay.php +++ b/packages/admin/src/Charcoal/Admin/Property/Display/ColorSwatchDisplay.php @@ -1,5 +1,7 @@ p(); $pad = str_repeat($this->indentation(), ($this->currentLevel() - 1)); @@ -51,7 +45,7 @@ public function displayVal() * @throws InvalidArgumentException If the indentation is not a string. * @return AbstractConfig Chainable */ - public function setIndentation($indent) + public function setIndentation($indent): static { if (!is_string($indent)) { throw new InvalidArgumentException( @@ -66,10 +60,8 @@ public function setIndentation($indent) /** * Retrieve the indentation string. - * - * @return integer */ - public function indentation() + public function indentation(): string { return $this->indentation; } @@ -81,9 +73,8 @@ public function indentation() * * @param integer $level The level of depth. * @throws InvalidArgumentException If the level is not an integer. - * @return self */ - public function setCurrentLevel($level) + public function setCurrentLevel($level): static { if (!is_int($level)) { throw new InvalidArgumentException( @@ -98,10 +89,8 @@ public function setCurrentLevel($level) /** * Retrieve the current level for output of the associated object. - * - * @return integer */ - public function currentLevel() + public function currentLevel(): int { return $this->currentLevel; } diff --git a/packages/admin/src/Charcoal/Admin/Property/Display/ImageDisplay.php b/packages/admin/src/Charcoal/Admin/Property/Display/ImageDisplay.php index 0af14cadf..5be86f70d 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Display/ImageDisplay.php +++ b/packages/admin/src/Charcoal/Admin/Property/Display/ImageDisplay.php @@ -28,6 +28,7 @@ class ImageDisplay extends AbstractPropertyDisplay * @see \Charcoal\Admin\Property\Display\LinkDisplay::hrefVal() * @return string */ + #[\Override] public function displayVal() { $val = parent::displayVal(); @@ -38,8 +39,8 @@ public function displayVal() $parts = parse_url($val); if (empty($parts['scheme']) && !in_array($val[0], [ '/', '#', '?' ])) { $path = isset($parts['path']) ? ltrim($parts['path'], '/') : ''; - $query = isset($parts['query']) ? $parts['query'] : ''; - $hash = isset($parts['fragment']) ? $parts['fragment'] : ''; + $query = $parts['query'] ?? ''; + $hash = $parts['fragment'] ?? ''; $val = $this->baseUrl->withPath($path)->withQuery($query)->withFragment($hash); } @@ -52,6 +53,7 @@ public function displayVal() * @param Container $container A dependencies container instance. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); diff --git a/packages/admin/src/Charcoal/Admin/Property/Display/LinkDisplay.php b/packages/admin/src/Charcoal/Admin/Property/Display/LinkDisplay.php index 38d1a07a4..7898dc034 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Display/LinkDisplay.php +++ b/packages/admin/src/Charcoal/Admin/Property/Display/LinkDisplay.php @@ -29,6 +29,7 @@ class LinkDisplay extends AbstractPropertyDisplay /** * @return string */ + #[\Override] public function displayVal() { $prop = $this->property(); @@ -86,9 +87,8 @@ public function displayValList() * * @param string $url The link URL. * @param string $text The link text. - * @return string */ - protected function formatHtmlLink($url, $text = null) + protected function formatHtmlLink($url, $text = null): string { if ($text === null) { $text = $url; @@ -99,13 +99,11 @@ protected function formatHtmlLink($url, $text = null) $text = $format($text); } - $link = sprintf( + return sprintf( '%s', $this->getLocalUrl($url), $text ); - - return $link; } /** @@ -117,14 +115,12 @@ protected function formatHtmlLink($url, $text = null) protected function getLocalUrl($path) { $prop = $this->property(); - if ($prop instanceof FileProperty) { - if ($prop['publicAccess'] === false) { - $query = http_build_query([ - 'disk' => $prop['filesystem'], - 'path' => $path, - ]); - return $this->adminUrl('filesystem/download')->withQuery($query); - } + if ($prop instanceof FileProperty && $prop['publicAccess'] === false) { + $query = http_build_query([ + 'disk' => $prop['filesystem'], + 'path' => $path, + ]); + return $this->adminUrl('filesystem/download')->withQuery($query); } return $this->baseUrl($path); @@ -133,9 +129,8 @@ protected function getLocalUrl($path) /** * @param callable|string|null $format The link textt format. * @throws InvalidArgumentException If the format is not a valid callable. - * @return self */ - public function setLinkTextFormat($format) + public function setLinkTextFormat($format): static { if ($format !== null && !function_exists($format)) { throw new InvalidArgumentException( @@ -161,6 +156,7 @@ public function getLinkTextFormat() * @param Container $container A dependencies container instance. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); diff --git a/packages/admin/src/Charcoal/Admin/Property/Display/MessageDisplay.php b/packages/admin/src/Charcoal/Admin/Property/Display/MessageDisplay.php index 2954d1480..426f674da 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Display/MessageDisplay.php +++ b/packages/admin/src/Charcoal/Admin/Property/Display/MessageDisplay.php @@ -44,9 +44,8 @@ class MessageDisplay extends AbstractPropertyDisplay implements /** * @param mixed $message The message to display. - * @return self */ - public function setMessage($message) + public function setMessage($message): static { $this->message = $this->translator()->translation($message); if ($this->message instanceof Translation) { @@ -56,12 +55,9 @@ public function setMessage($message) return $this; } - /** - * @return bool - */ - public function hasMessage() + public function hasMessage(): bool { - return !!$this->message; + return (bool) $this->message; } /** @@ -78,7 +74,7 @@ public function getMessage() public function displayMessage() { if ($this->message instanceof Translation) { - if (isset($this->message->isRendered) && $this->message->isRendered === false) { + if (property_exists($this->message, 'isRendered') && $this->message->isRendered !== null && $this->message->isRendered === false) { $this->message = $this->renderTranslatableTemplate($this->message); } diff --git a/packages/admin/src/Charcoal/Admin/Property/Display/ObjectDisplay.php b/packages/admin/src/Charcoal/Admin/Property/Display/ObjectDisplay.php index f8609270e..91aa8e0d6 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Display/ObjectDisplay.php +++ b/packages/admin/src/Charcoal/Admin/Property/Display/ObjectDisplay.php @@ -1,5 +1,7 @@ propertyVal() ? static::STATE_SUCCESS : static::STATE_DEFAULT; + return $this->propertyVal() ? static::STATE_SUCCESS : static::STATE_DEFAULT; } /** @@ -88,7 +89,7 @@ private function fallbackStatus() * @throws UnexpectedValueException When an unsupported operator is used. * @return boolean */ - private function testConditionWithOperator($condition) + private function testConditionWithOperator($condition): ?bool { $value = $condition; $operator = null; @@ -101,7 +102,7 @@ private function testConditionWithOperator($condition) throw new UnexpectedValueException(sprintf( 'The operator [%s] is not supported in [%s]', $operator, - get_class($this) + static::class )); } @@ -139,13 +140,12 @@ private function calculateState() if (is_array($state)) { foreach ($state as $stateIdent => $conditions) { - $result = is_string($conditions) ? - $result = $this->testConditionWithOperator($conditions) : false; + $result = is_string($conditions) && $result = $this->testConditionWithOperator($conditions); $result = !$result && is_array($conditions) ? - !!count(array_filter($conditions, [$this, 'testConditionWithOperator'])) : $result; + (bool) count(array_filter($conditions, $this->testConditionWithOperator(...))) : $result; - if (!!$result && in_array($stateIdent, static::SUPPORTED_STATES)) { + if ($result && in_array($stateIdent, static::SUPPORTED_STATES)) { return $stateIdent; } }; @@ -177,9 +177,8 @@ public function state() /** * @param array|callable $state State for StatusDisplay. - * @return self */ - public function setState($state) + public function setState($state): static { $this->state = $state; diff --git a/packages/admin/src/Charcoal/Admin/Property/Display/TextDisplay.php b/packages/admin/src/Charcoal/Admin/Property/Display/TextDisplay.php index 874ef842e..8cb2ae448 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Display/TextDisplay.php +++ b/packages/admin/src/Charcoal/Admin/Property/Display/TextDisplay.php @@ -1,5 +1,7 @@ objId = $objId; diff --git a/packages/admin/src/Charcoal/Admin/Property/Input/AudioInput.php b/packages/admin/src/Charcoal/Admin/Property/Input/AudioInput.php index df44ac2e0..06f9ef30c 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Input/AudioInput.php +++ b/packages/admin/src/Charcoal/Admin/Property/Input/AudioInput.php @@ -12,18 +12,15 @@ class AudioInput extends FileInput { /** * Retrieve list of default file type specifiers. - * - * @return string */ - public function getDefaultAccept() + #[\Override] + public function getDefaultAccept(): string { return 'audio/*'; } - /** - * @return string|null - */ - public function filePreview() + #[\Override] + public function filePreview(): string { $value = $this->inputVal(); if ($value) { diff --git a/packages/admin/src/Charcoal/Admin/Property/Input/AudioWidgetInput.php b/packages/admin/src/Charcoal/Admin/Property/Input/AudioWidgetInput.php index 7974d7e69..bda7fcb0e 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Input/AudioWidgetInput.php +++ b/packages/admin/src/Charcoal/Admin/Property/Input/AudioWidgetInput.php @@ -2,6 +2,7 @@ namespace Charcoal\Admin\Property\Input; +use Aws\signer\signerClient; use InvalidArgumentException; use UnexpectedValueException; // From Mustache @@ -24,24 +25,18 @@ class AudioWidgetInput extends AudioInput /** * Whether text-to-speech is enabled. - * - * @var boolean */ - private $textEnabled = true; + private bool $textEnabled = true; /** * Whether audio recording is enabled. - * - * @var boolean */ - private $captureEnabled = true; + private bool $captureEnabled = true; /** * Whether file upload is enabled. - * - * @var boolean */ - private $uploadEnabled = true; + private bool $uploadEnabled = true; /** * URL for the "audio recorder" plugin. @@ -59,10 +54,8 @@ class AudioWidgetInput extends AudioInput /** * The text property for TTS. - * - * @var PropertyInterface */ - private $textProperty; + private ?\Charcoal\Property\PropertyInterface $textProperty = null; /** * The HTML input name attribute for TTS. @@ -87,125 +80,98 @@ class AudioWidgetInput extends AudioInput /** * Retrieve the control type for the HTML element ``. - * - * @return string */ - public function type() + #[\Override] + public function type(): string { return 'hidden'; } - /** - * @return boolean - */ - public function displayAudioWidget() + public function displayAudioWidget(): bool { return $this->textEnabled() || $this->captureEnabled() || $this->uploadEnabled(); } /** * @param boolean $textEnabled If TTS is enabled or not for this widget. - * @return self */ - public function setTextEnabled($textEnabled) + public function setTextEnabled($textEnabled): static { - $this->textEnabled = !!$textEnabled; + $this->textEnabled = (bool) $textEnabled; return $this; } - /** - * @return boolean - */ - public function textEnabled() + public function textEnabled(): bool { return $this->textEnabled; } /** * @param boolean $captureEnabled If recording is enabled or not for this widget. - * @return self */ - public function setCaptureEnabled($captureEnabled) + public function setCaptureEnabled($captureEnabled): static { - $this->captureEnabled = !!$captureEnabled; + $this->captureEnabled = (bool) $captureEnabled; return $this; } - /** - * @return boolean - */ - public function captureEnabled() + public function captureEnabled(): bool { return $this->captureEnabled; } /** - * @deprecated In favour of {@see self::setCaptureEnabled()} * * @param boolean $recordingEnabled If recording is enabled or not for this widget. - * @return self */ - public function setRecordingEnabled($recordingEnabled) + #[\Deprecated(message: 'In favour of {@see self::setCaptureEnabled()}')] + public function setRecordingEnabled($recordingEnabled): static { - $this->captureEnabled = !!$recordingEnabled; + $this->captureEnabled = (bool) $recordingEnabled; return $this; } - /** - * @deprecated In favour of {@see self::captureEnabled()} - * - * @return boolean - */ - public function recordingEnabled() + #[\Deprecated(message: 'In favour of {@see self::captureEnabled()}')] + public function recordingEnabled(): bool { return $this->captureEnabled; } /** * @param boolean $uploadEnabled If file upload is enabled or not for this widget. - * @return self */ - public function setUploadEnabled($uploadEnabled) + public function setUploadEnabled($uploadEnabled): static { - $this->uploadEnabled = !!$uploadEnabled; + $this->uploadEnabled = (bool) $uploadEnabled; return $this; } - /** - * @return boolean - */ - public function uploadEnabled() + public function uploadEnabled(): bool { return $this->uploadEnabled; } /** - * @deprecated In favour of {@see self::setUploadEnabled()} * * @param boolean $fileEnabled If file upload is enabled or not for this widget. - * @return self */ - public function setFileEnabled($fileEnabled) + #[\Deprecated(message: 'In favour of {@see self::setUploadEnabled()}')] + public function setFileEnabled($fileEnabled): static { - $this->uploadEnabled = !!$fileEnabled; + $this->uploadEnabled = (bool) $fileEnabled; return $this; } - /** - * @deprecated In favour of {@see self::uploadEnabled()} - * - * @return boolean - */ - public function fileEnabled() + #[\Deprecated(message: 'In favour of {@see self::uploadEnabled()}')] + public function fileEnabled(): bool { return $this->uploadEnabled; } /** * @param string $url The recording/exporting plugin URL. - * @return self */ - public function setRecorderPluginUrl($url) + public function setRecorderPluginUrl($url): static { $this->recorderPluginUrl = $url; return $this; @@ -238,7 +204,7 @@ public function prepareRecorderPluginUrl() { $uri = $this->getRecorderPluginUrlTemplate(); - return function ($noop, LambdaHelper $helper) use ($uri) { + return function ($noop, LambdaHelper $helper) use ($uri): null { $uri = $helper->render($uri); $this->setRecorderPluginUrl($uri); @@ -251,15 +217,12 @@ public function prepareRecorderPluginUrl() * * This method is overriden to change the `callback` value to reflect * the correct input control ID. - * - * @return string */ - protected function getRecorderPluginUrlTemplate() + protected function getRecorderPluginUrlTemplate(): string { $uri = 'assets/admin/scripts/vendors/recorderjs/recorder.js'; - $uri = '{{# withBaseUrl }}' . $uri . '{{/ withBaseUrl }}'; - return $uri; + return '{{# withBaseUrl }}' . $uri . '{{/ withBaseUrl }}'; } /** @@ -267,9 +230,8 @@ protected function getRecorderPluginUrlTemplate() * * @param string $activePane The active widget pane. * @throws InvalidArgumentException If the provided argument is not a string. - * @return self */ - public function setActivePane($activePane) + public function setActivePane($activePane): static { if ($activePane === null || $activePane === '') { $this->activePane = null; @@ -325,9 +287,8 @@ public function activePane() * Alias of {@see AbstractPropertyInput::setPropertyVal()}. * * @param mixed $val The audio property value. - * @return self */ - public function setAudioPropertyVal($val) + public function setAudioPropertyVal($val): static { $this->setPropertyVal($val); return $this; @@ -343,10 +304,7 @@ public function audioPropertyVal() return $this->propertyVal(); } - /** - * @return boolean - */ - public function hasAudioPropertyVal() + public function hasAudioPropertyVal(): bool { $prop = $this->audioProperty(); $val = $prop->inputVal($this->audioPropertyVal(), [ @@ -360,9 +318,8 @@ public function hasAudioPropertyVal() * Alias of {@see AbstractPropertyInput::setProperty()}. * * @param PropertyInterface $p The property for TTS. - * @return self */ - public function setAudioProperty(PropertyInterface $p) + public function setAudioProperty(PropertyInterface $p): static { $this->setProperty($p); return $this; @@ -392,9 +349,8 @@ public function audioPropertyIdent() * Alias of {@see AbstractPropertyInput::setInputName()}. * * @param string $inputName HTML input id attribute. - * @return self */ - public function setAudioInputName($inputName) + public function setAudioInputName($inputName): static { $this->setInputName($inputName); @@ -425,9 +381,8 @@ public function audioInputVal() * Set the property value for TTS. * * @param mixed $val The property value. - * @return self */ - public function setTextPropertyVal($val) + public function setTextPropertyVal($val): static { $this->textPropertyVal = $val; return $this; @@ -443,10 +398,7 @@ public function textPropertyVal() return $this->textPropertyVal; } - /** - * @return boolean - */ - public function hasTextPropertyVal() + public function hasTextPropertyVal(): bool { $prop = $this->textProperty(); $val = $prop->inputVal($this->textPropertyVal(), [ @@ -460,9 +412,8 @@ public function hasTextPropertyVal() * Set the property instance for TTS. * * @param PropertyInterface $p The property for TTS. - * @return self */ - public function setTextProperty(PropertyInterface $p) + public function setTextProperty(PropertyInterface $p): static { $this->textProperty = $p; return $this; @@ -473,7 +424,7 @@ public function setTextProperty(PropertyInterface $p) * * @return PropertyInterface */ - public function textProperty() + public function textProperty(): ?\Charcoal\Property\PropertyInterface { return $this->textProperty; } @@ -493,9 +444,8 @@ public function textPropertyIdent() * * @see AbstractPropertyInput::setInputName() * @param string $inputName HTML input name attribute. - * @return self */ - public function setTextInputName($inputName) + public function setTextInputName($inputName): static { $this->textInputName = $inputName; return $this; @@ -509,11 +459,7 @@ public function setTextInputName($inputName) */ public function textInputName() { - if ($this->textInputName) { - $name = $this->textInputName; - } else { - $name = $this->textPropertyIdent(); - } + $name = $this->textInputName ?: $this->textPropertyIdent(); if ($this->textProperty()['l10n']) { $name .= '[' . $this->lang() . ']'; @@ -529,7 +475,7 @@ public function textInputName() * @throws UnexpectedValueException If the value is invalid. * @return string */ - public function textInputVal() + public function textInputVal(): int|float|string|bool { $prop = $this->textProperty(); $val = $prop->inputVal($this->textPropertyVal(), [ @@ -543,7 +489,7 @@ public function textInputVal() if (!is_scalar($val)) { throw new UnexpectedValueException(sprintf( 'Property Input Value must be a string, received %s', - (is_object($val) ? get_class($val) : gettype($val)) + (get_debug_type($val)) )); } @@ -552,40 +498,32 @@ public function textInputVal() /** * Retrieve the input ID for the TTS property. - * - * @return string */ - public function textInputId() + public function textInputId(): string { return 'audio_text_' . $this->inputId(); } /** * Retrieve the input ID for the audio recorder property. - * - * @return string */ - public function captureInputId() + public function captureInputId(): string { return 'audio_capture_' . $this->inputId(); } /** * Retrieve the input ID for the audio file property. - * - * @return string */ - public function uploadInputId() + public function uploadInputId(): string { return 'audio_upload_' . $this->inputId(); } /** * Retrieve the input ID for the widget's hidden property. - * - * @return string */ - public function hiddenInputId() + public function hiddenInputId(): string { return 'audio_hidden_' . $this->inputId(); } @@ -595,7 +533,7 @@ public function hiddenInputId() * * @return callable|null */ - public function textPropertyContext() + public function textPropertyContext(): ?\Closure { if (!$this->textEnabled() || $this->currentContext) { return null; @@ -622,7 +560,7 @@ public function textPropertyContext() * * @return callable|null */ - public function capturePropertyContext() + public function capturePropertyContext(): ?\Closure { if (!$this->captureEnabled() || $this->currentContext) { return null; @@ -649,7 +587,7 @@ public function capturePropertyContext() * * @return callable|null */ - public function uploadPropertyContext() + public function uploadPropertyContext(): ?\Closure { if (!$this->uploadEnabled() || $this->currentContext) { return null; @@ -676,25 +614,22 @@ public function uploadPropertyContext() * * This method is overriden to change the `callback` value to reflect * the correct input control ID. - * - * @return string */ - protected function getFilePickerUrlTemplate() + #[\Override] + protected function getFilePickerUrlTemplate(): string { $uri = 'obj_type={{ objType }}&obj_id={{ objId }}&property={{ p.ident }}&callback={{ uploadInputId }}'; - $uri = '{{# withAdminUrl }}elfinder?' . $uri . '{{/ withAdminUrl }}'; - return $uri; + return '{{# withAdminUrl }}elfinder?' . $uri . '{{/ withAdminUrl }}'; } /** * Retrieve the control's data options for JavaScript components. - * - * @return array */ - public function controlDataForJs() + #[\Override] + public function controlDataForJs(): array { - $inputId = $this->inputId(); + $this->inputId(); $data = parent::controlDataForJs(); return array_replace($data, [ diff --git a/packages/admin/src/Charcoal/Admin/Property/Input/CheckboxBtnGroupInput.php b/packages/admin/src/Charcoal/Admin/Property/Input/CheckboxBtnGroupInput.php index 444ede0c4..5e4b69413 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Input/CheckboxBtnGroupInput.php +++ b/packages/admin/src/Charcoal/Admin/Property/Input/CheckboxBtnGroupInput.php @@ -1,5 +1,7 @@ `. - * - * @return string */ - public function type() + public function type(): string { return 'checkbox'; } /** * Always accept multiple values. - * - * @return boolean */ - public function multiple() + #[\Override] + public function multiple(): bool { return true; } diff --git a/packages/admin/src/Charcoal/Admin/Property/Input/ColorPickerInput.php b/packages/admin/src/Charcoal/Admin/Property/Input/ColorPickerInput.php index f268dc581..22bb2414c 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Input/ColorPickerInput.php +++ b/packages/admin/src/Charcoal/Admin/Property/Input/ColorPickerInput.php @@ -12,17 +12,13 @@ class ColorPickerInput extends AbstractPropertyInput { /** * Settings for {@link https://github.com/claviska/jquery-minicolors/ jQuery MiniColors}. - * - * @var array */ - private $pickerOptions; + private ?array $pickerOptions = null; /** * Retrieve the control type for the HTML element ``. - * - * @return string */ - public function type() + public function type(): string { return 'color'; } @@ -35,7 +31,7 @@ public function type() * @param array $settings The color picker options. * @return ColorpickerInput Chainable */ - public function setPickerOptions(array $settings) + public function setPickerOptions(array $settings): static { $this->pickerOptions = array_merge($this->defaultPickerOptions(), $settings); @@ -48,7 +44,7 @@ public function setPickerOptions(array $settings) * @param array $settings The color picker options. * @return ColorpickerInput Chainable */ - public function mergePickerOptions(array $settings) + public function mergePickerOptions(array $settings): static { $this->pickerOptions = array_merge($this->pickerOptions, $settings); @@ -63,7 +59,7 @@ public function mergePickerOptions(array $settings) * @throws InvalidArgumentException If the identifier is not a string. * @return ColorpickerInput Chainable */ - public function addPickerOption($key, $val) + public function addPickerOption($key, $val): static { if (!is_string($key)) { throw new InvalidArgumentException( @@ -83,10 +79,8 @@ public function addPickerOption($key, $val) /** * Retrieve the color picker's options. - * - * @return array */ - public function pickerOptions() + public function pickerOptions(): array { if ($this->pickerOptions === null) { $this->pickerOptions = $this->defaultPickerOptions(); @@ -97,10 +91,8 @@ public function pickerOptions() /** * Retrieve the default color picker options. - * - * @return array */ - public function defaultPickerOptions() + public function defaultPickerOptions(): array { return [ 'format' => 'hex', diff --git a/packages/admin/src/Charcoal/Admin/Property/Input/DateTimePickerInput.php b/packages/admin/src/Charcoal/Admin/Property/Input/DateTimePickerInput.php index 72650319a..0185af601 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Input/DateTimePickerInput.php +++ b/packages/admin/src/Charcoal/Admin/Property/Input/DateTimePickerInput.php @@ -19,17 +19,13 @@ class DateTimePickerInput extends AbstractPropertyInput /** * Settings for {@link https://eonasdan.github.io/bootstrap-datetimepicker/ Bootstrap Datepicker}. - * - * @var array */ - private $pickerOptions; + private ?array $pickerOptions = null; /** * Retrieve the control type for the HTML element ``. - * - * @return string */ - public function type() + public function type(): string { return 'datetime-local'; } @@ -37,9 +33,8 @@ public function type() /** * @param string $class The input group class attribute. * @throws InvalidArgumentException If the class is not a string. - * @return self */ - public function setInputGroupClass($class) + public function setInputGroupClass($class): static { if (!is_string($class)) { throw new InvalidArgumentException('CSS Class(es) must be a string'); @@ -64,7 +59,7 @@ public function inputGroupClass() * @param array $settings The color picker options. * @return ColorpickerInput Chainable */ - public function setPickerOptions(array $settings) + public function setPickerOptions(array $settings): static { $this->pickerOptions = array_merge($this->defaultPickerOptions(), $settings); @@ -77,7 +72,7 @@ public function setPickerOptions(array $settings) * @param array $settings The color picker options. * @return ColorpickerInput Chainable */ - public function mergePickerOptions(array $settings) + public function mergePickerOptions(array $settings): static { $this->pickerOptions = array_merge($this->pickerOptions, $settings); @@ -92,7 +87,7 @@ public function mergePickerOptions(array $settings) * @throws InvalidArgumentException If the identifier is not a string. * @return ColorpickerInput Chainable */ - public function addPickerOption($key, $val) + public function addPickerOption($key, $val): static { if (!is_string($key)) { throw new InvalidArgumentException( @@ -112,10 +107,8 @@ public function addPickerOption($key, $val) /** * Retrieve the color picker's options. - * - * @return array */ - public function pickerOptions() + public function pickerOptions(): array { if ($this->pickerOptions === null) { $this->pickerOptions = $this->defaultPickerOptions(); @@ -126,10 +119,8 @@ public function pickerOptions() /** * Retrieve the default color picker options. - * - * @return array */ - public function defaultPickerOptions() + public function defaultPickerOptions(): array { $date = null; @@ -139,7 +130,7 @@ public function defaultPickerOptions() return [ 'format' => self::DEFAULT_JS_FORMAT, - 'defaultDate' => $date ? $date->format(\DateTime::ISO8601) : null + 'defaultDate' => $date instanceof \DateTime ? $date->format(\DateTime::ISO8601) : null ]; } diff --git a/packages/admin/src/Charcoal/Admin/Property/Input/DualSelectInput.php b/packages/admin/src/Charcoal/Admin/Property/Input/DualSelectInput.php index 06847f268..a4a100c10 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Input/DualSelectInput.php +++ b/packages/admin/src/Charcoal/Admin/Property/Input/DualSelectInput.php @@ -23,10 +23,8 @@ class DualSelectInput extends AbstractSelectableInput /** * How the dual-select controls should be displayed. - * - * @var string|null */ - private $inputLayout; + private ?string $inputLayout = null; /** * Whether the lists can be filtered. @@ -44,10 +42,8 @@ class DualSelectInput extends AbstractSelectableInput /** * Settings for {@link http://crlcu.github.io/multiselect/ Multiselect}. - * - * @var array */ - private $dualSelectOptions; + private ?array $dualSelectOptions = null; /** * Retrieve the unselected options. @@ -123,11 +119,7 @@ public function searchable() $placeholder = $searchable['placeholder']; } - if (isset($placeholder)) { - $searchable[$ident]['placeholder'] = $this->translator()->translation($placeholder); - } else { - $searchable[$ident]['placeholder'] = $label; - } + $searchable[$ident]['placeholder'] = isset($placeholder) ? $this->translator()->translation($placeholder) : $label; } } else { $searchable = false; @@ -168,7 +160,7 @@ public function reorderable() * @throws OutOfBoundsException If the given layout is unsupported. * @return AbstractTickableInput Chainable */ - public function setInputLayout($layout) + public function setInputLayout($layout): static { if ($layout === null) { $this->inputLayout = null; @@ -179,7 +171,7 @@ public function setInputLayout($layout) if (!is_string($layout)) { throw new InvalidArgumentException(sprintf( 'Layout must be a string, received %s', - (is_object($layout) ? get_class($layout) : gettype($layout)) + (get_debug_type($layout)) )); } @@ -213,10 +205,8 @@ public function inputLayout() /** * Retrieve the input layouts; for templating. - * - * @return array */ - public function inputLayouts() + public function inputLayouts(): array { $supported = $this->supportedInputLayouts(); $layouts = []; @@ -229,10 +219,8 @@ public function inputLayouts() /** * Retrieve the supported input layouts. - * - * @return array */ - protected function supportedInputLayouts() + protected function supportedInputLayouts(): array { return [ self::COLS_INPUT_LAYOUT, @@ -258,7 +246,7 @@ protected function defaultInputLayout() * @param array $settings The dual-select options. * @return Selectinput Chainable */ - public function setDualSelectOptions(array $settings) + public function setDualSelectOptions(array $settings): static { $this->dualSelectOptions = array_merge($this->defaultDualSelectOptions(), $settings); @@ -271,7 +259,7 @@ public function setDualSelectOptions(array $settings) * @param array $settings The dual-select options. * @return Selectinput Chainable */ - public function mergeDualSelectOptions(array $settings) + public function mergeDualSelectOptions(array $settings): static { $this->dualSelectOptions = array_merge($this->dualSelectOptions, $settings); @@ -286,7 +274,7 @@ public function mergeDualSelectOptions(array $settings) * @throws InvalidArgumentException If the identifier is not a string. * @return Selectinput Chainable */ - public function addSelectOption($key, $val) + public function addSelectOption($key, $val): static { if (!is_string($key)) { throw new InvalidArgumentException( @@ -306,10 +294,8 @@ public function addSelectOption($key, $val) /** * Retrieve the dual-select's options. - * - * @return array */ - public function dualSelectOptions() + public function dualSelectOptions(): array { if ($this->dualSelectOptions === null) { $this->dualSelectOptions = $this->defaultDualSelectOptions(); @@ -320,10 +306,8 @@ public function dualSelectOptions() /** * Retrieve the default dual-select options. - * - * @return array */ - public function defaultDualSelectOptions() + public function defaultDualSelectOptions(): array { return []; } diff --git a/packages/admin/src/Charcoal/Admin/Property/Input/EmailInput.php b/packages/admin/src/Charcoal/Admin/Property/Input/EmailInput.php index 410ebe205..2fca5bc6a 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Input/EmailInput.php +++ b/packages/admin/src/Charcoal/Admin/Property/Input/EmailInput.php @@ -1,5 +1,7 @@ `. - * - * @return string */ - public function type() + #[\Override] + public function type(): string { return 'email'; } diff --git a/packages/admin/src/Charcoal/Admin/Property/Input/FileInput.php b/packages/admin/src/Charcoal/Admin/Property/Input/FileInput.php index b3e76b526..5462e5d3c 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Input/FileInput.php +++ b/packages/admin/src/Charcoal/Admin/Property/Input/FileInput.php @@ -30,24 +30,18 @@ class FileInput extends AbstractPropertyInput /** * Flag wether the "file preview" should be displayed. - * - * @var boolean */ - private $showFilePreview = true; + private bool $showFilePreview = true; /** * Flag wether the "file upload" input should be displayed. - * - * @var boolean */ - private $showFileUpload; + private ?bool $showFileUpload = null; /** * Flag wether the "file picker" popup button should be displaed. - * - * @var boolean */ - private $showFilePicker; + private ?bool $showFilePicker = null; /** * URL for the "file picker" popup. @@ -79,10 +73,8 @@ class FileInput extends AbstractPropertyInput /** * Retrieve the control type for the HTML element ``. - * - * @return string */ - public function type() + public function type(): string { return 'file'; } @@ -92,9 +84,8 @@ public function type() * * @link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file * @param string|string[] $types The accepted MIME types. - * @return self */ - public function setAccept($types) + public function setAccept($types): static { if (is_array($types)) { $types = implode(',', $types); @@ -108,10 +99,8 @@ public function setAccept($types) * Retrieve a comma-separated list of default file type specifiers. * * This method concatenates the file property's "acceptedMimetypes". - * - * @return string */ - public function getDefaultAccept() + public function getDefaultAccept(): string { $types = $this->property()['acceptedMimetypes']; return implode(',', $types); @@ -134,14 +123,14 @@ public function accept() /** * @return string|null */ - public function abridgedInputVal() + public function abridgedInputVal(): string|array|null { $val = (string)$this->inputVal(); - $val = preg_replace('!^' . preg_quote($this->p()['uploadPath'], '!') . '!', '', $val); + $val = preg_replace('!^' . preg_quote((string) $this->p()['uploadPath'], '!') . '!', '', $val); - if (strpos($val, '://') !== false) { - $host = parse_url($val, PHP_URL_HOST); - $path = ltrim(substr($val, (strpos($val, $host) + strlen($host) + 1)), '/'); + if (str_contains((string) $val, '://')) { + $host = parse_url((string) $val, PHP_URL_HOST); + $path = ltrim(substr((string) $val, (strpos((string) $val, (string) $host) + strlen($host) + 1)), '/'); if (mb_strlen($path) > 30) { $a = 12; $z = 12; @@ -153,10 +142,7 @@ public function abridgedInputVal() return $val; } - /** - * @return string|null - */ - public function filePreview() + public function filePreview(): string { $value = $this->inputVal(); if ($value) { @@ -181,8 +167,8 @@ public function placeholderVal() $parts = parse_url($val); if (empty($parts['scheme']) && !in_array($val[0], [ '/', '#', '?' ])) { $path = isset($parts['path']) ? ltrim($parts['path'], '/') : ''; - $query = isset($parts['query']) ? $parts['query'] : ''; - $hash = isset($parts['fragment']) ? $parts['fragment'] : ''; + $query = $parts['query'] ?? ''; + $hash = $parts['fragment'] ?? ''; $val = $this->baseUrl->withPath($path)->withQuery($query)->withFragment($hash); } @@ -204,8 +190,8 @@ public function previewVal() $parts = parse_url($val); if (empty($parts['scheme']) && !in_array($val[0], [ '/', '#', '?' ])) { $path = isset($parts['path']) ? ltrim($parts['path'], '/') : ''; - $query = isset($parts['query']) ? $parts['query'] : ''; - $hash = isset($parts['fragment']) ? $parts['fragment'] : ''; + $query = $parts['query'] ?? ''; + $hash = $parts['fragment'] ?? ''; $val = $this->baseUrl->withPath($path)->withQuery($query)->withFragment($hash); } @@ -216,17 +202,14 @@ public function previewVal() * @param boolean $show The show file preview flag. * @return FileInput Chainable */ - public function setShowFilePreview($show) + public function setShowFilePreview($show): static { - $this->showFilePreview = !!$show; + $this->showFilePreview = (bool) $show; return $this; } - /** - * @return boolean - */ - public function showFilePreview() + public function showFilePreview(): bool { return $this->showFilePreview; } @@ -235,9 +218,9 @@ public function showFilePreview() * @param boolean $show The show file upload flag. * @return FileInput Chainable */ - public function setShowFileUpload($show) + public function setShowFileUpload($show): static { - $this->showFileUpload = !!$show; + $this->showFileUpload = (bool) $show; return $this; } @@ -258,29 +241,23 @@ public function showFileUpload() * @param boolean $show The show file picker flag. * @return FileInput Chainable */ - public function setShowFilePicker($show) + public function setShowFilePicker($show): static { - $this->showFilePicker = !!$show; + $this->showFilePicker = (bool) $show; return $this; } - /** - * @return boolean - */ - public function showFilePicker() + public function showFilePicker(): bool { if ($this->showFilePicker === null) { - return !($this->showFileUpload === true); + return $this->showFileUpload !== true; } return $this->showFilePicker && $this->hasFilePicker(); } - /** - * @return boolean - */ - public function hasFilePicker() + public function hasFilePicker(): bool { return class_exists('\\elFinder'); } @@ -289,7 +266,7 @@ public function hasFilePicker() * @param string $url The file picker AJAX URL. * @return FileInput Chainable */ - public function setFilePickerUrl($url) + public function setFilePickerUrl($url): static { $this->filePickerUrl = $url; return $this; @@ -317,7 +294,7 @@ public function filePickerUrl() * * @return callable|null */ - public function prepareFilePickerUrl() + public function prepareFilePickerUrl(): ?\Closure { if (!$this->showFilePicker()) { return null; @@ -325,7 +302,7 @@ public function prepareFilePickerUrl() $uri = $this->getFilePickerUrlTemplate(); - return function ($noop, LambdaHelper $helper) use ($uri) { + return function ($noop, LambdaHelper $helper) use ($uri): null { $uri = $helper->render($uri); $this->setFilePickerUrl($uri); @@ -335,24 +312,20 @@ public function prepareFilePickerUrl() /** * Retrieve the elFinder connector URL template for rendering. - * - * @return string */ - protected function getFilePickerUrlTemplate() + protected function getFilePickerUrlTemplate(): string { $uri = 'obj_type={{ objType }}&obj_id={{ objId }}&property={{ p.ident }}&callback={{ inputId }}'; - $uri = '{{# withAdminUrl }}elfinder?' . $uri . '{{/ withAdminUrl }}'; - return $uri; + return '{{# withAdminUrl }}elfinder?' . $uri . '{{/ withAdminUrl }}'; } /** * Set the title for the file picker dialog. * * @param string|string[] $title The dialog title. - * @return self */ - public function setDialogTitle($title) + public function setDialogTitle($title): static { $this->dialogTitle = $this->translator()->translation($title); @@ -377,9 +350,8 @@ public function dialogTitle() * Set the label for the file picker button. * * @param string|string[] $label The button label. - * @return self */ - public function setChooseButtonLabel($label) + public function setChooseButtonLabel($label): static { $this->chooseButtonLabel = $this->translator()->translation($label); @@ -404,9 +376,8 @@ public function chooseButtonLabel() * Set the label for the file removal button. * * @param string|string[] $label The button label. - * @return self */ - public function setRemoveButtonLabel($label) + public function setRemoveButtonLabel($label): static { $this->removeButtonLabel = $this->translator()->translation($label); @@ -433,6 +404,7 @@ public function removeButtonLabel() * @param Container $container A dependencies container instance. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -445,7 +417,7 @@ protected function setDependencies(Container $container) * * @return \Charcoal\Translator\Translation|string|null */ - protected function defaultDialogTitle() + protected function defaultDialogTitle(): ?\Charcoal\Translator\Translation { return $this->translator()->translation('filesystem.library.media'); } @@ -455,7 +427,7 @@ protected function defaultDialogTitle() * * @return \Charcoal\Translator\Translation|string|null */ - protected function defaultChooseButtonLabel() + protected function defaultChooseButtonLabel(): ?\Charcoal\Translator\Translation { if ($this->property()['multiple']) { return $this->translator()->translation('Choose files…'); @@ -469,7 +441,7 @@ protected function defaultChooseButtonLabel() * * @return \Charcoal\Translator\Translation|string|null */ - protected function defaultRemoveButtonLabel() + protected function defaultRemoveButtonLabel(): ?\Charcoal\Translator\Translation { if ($this->property()['multiple']) { return $this->translator()->translation('Clear selected files'); @@ -480,10 +452,9 @@ protected function defaultRemoveButtonLabel() /** * Retrieve the control's data options for JavaScript components. - * - * @return array */ - public function controlDataForJs() + #[\Override] + public function controlDataForJs(): array { return [ 'input_name' => $this->inputName(), diff --git a/packages/admin/src/Charcoal/Admin/Property/Input/HiddenInput.php b/packages/admin/src/Charcoal/Admin/Property/Input/HiddenInput.php index d182cabf3..eb1b55000 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Input/HiddenInput.php +++ b/packages/admin/src/Charcoal/Admin/Property/Input/HiddenInput.php @@ -1,5 +1,7 @@ `. - * - * @return string */ - public function type() + #[\Override] + public function type(): string { return 'hidden'; } diff --git a/packages/admin/src/Charcoal/Admin/Property/Input/ImageInput.php b/packages/admin/src/Charcoal/Admin/Property/Input/ImageInput.php index d4b5f4ff1..20608e4f8 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Input/ImageInput.php +++ b/packages/admin/src/Charcoal/Admin/Property/Input/ImageInput.php @@ -15,18 +15,15 @@ class ImageInput extends FileInput /** * Retrieve list of default file type specifiers. - * - * @return string */ - public function getDefaultAccept() + #[\Override] + public function getDefaultAccept(): string { return 'image/*'; } - /** - * @return string|null - */ - public function filePreview() + #[\Override] + public function filePreview(): string { $value = $this->inputVal(); if ($value) { @@ -41,7 +38,8 @@ public function filePreview() * * @return \Charcoal\Translator\Translation|string|null */ - protected function defaultChooseButtonLabel() + #[\Override] + protected function defaultChooseButtonLabel(): ?\Charcoal\Translator\Translation { if ($this->property()['multiple']) { return $this->translator()->translation('Choose images…'); @@ -56,7 +54,7 @@ protected function defaultChooseButtonLabel() * @param string|string[] $classes A space-separated list of CSS classes. * @return ImageDisplay Chainable */ - public function setClassAttr($classes) + public function setClassAttr($classes): static { if (is_array($classes)) { $classes = implode(' ', $classes); diff --git a/packages/admin/src/Charcoal/Admin/Property/Input/JsonEditorInput.php b/packages/admin/src/Charcoal/Admin/Property/Input/JsonEditorInput.php index a87d41f68..502f44059 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Input/JsonEditorInput.php +++ b/packages/admin/src/Charcoal/Admin/Property/Input/JsonEditorInput.php @@ -27,7 +27,7 @@ class JsonEditorInput extends TextareaInput * @param array $settings The editor options. * @return Tinymce Chainable */ - public function setEditorOptions(array $settings) + public function setEditorOptions(array $settings): static { $this->editorOptions = array_merge($this->defaultEditorOptions(), $settings); @@ -40,7 +40,7 @@ public function setEditorOptions(array $settings) * @param array $settings The editor options. * @return Tinymce Chainable */ - public function mergeEditorOptions(array $settings) + public function mergeEditorOptions(array $settings): static { $this->editorOptions = array_merge($this->editorOptions, $settings); @@ -55,7 +55,7 @@ public function mergeEditorOptions(array $settings) * @throws InvalidArgumentException If the identifier is not a string. * @return Tinymce Chainable */ - public function addEditorOption($key, $val) + public function addEditorOption($key, $val): static { if (!is_string($key)) { throw new InvalidArgumentException( @@ -96,16 +96,10 @@ public function defaultEditorOptions() { $defaultData = $this->metadata()->defaultData(); - if (isset($defaultData['editor_options'])) { - return $defaultData['editor_options']; - } - - return []; + return $defaultData['editor_options'] ?? []; } - /** - * @return array - */ + #[\Override] public function getInputValOptions(): array { return [ diff --git a/packages/admin/src/Charcoal/Admin/Property/Input/MapWidgetInput.php b/packages/admin/src/Charcoal/Admin/Property/Input/MapWidgetInput.php index b9f80d917..21888746a 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Input/MapWidgetInput.php +++ b/packages/admin/src/Charcoal/Admin/Property/Input/MapWidgetInput.php @@ -22,18 +22,15 @@ class MapWidgetInput extends AbstractPropertyInput /** * Settings for the map widget. - * - * @var array */ - private $mapOptions; + private ?array $mapOptions = null; /** * Sets the API key for the mapping service. * * @param string $key An API key. - * @return self */ - public function setApiKey($key) + public function setApiKey($key): static { $this->apiKey = $key; @@ -58,7 +55,7 @@ public function apiKey() * @param array $settings The map widget options. * @return MapWidgetInput Chainable */ - public function setMapOptions(array $settings) + public function setMapOptions(array $settings): static { if (isset($settings['api_key'])) { $this->setApiKey($settings['api_key']); @@ -79,7 +76,7 @@ public function setMapOptions(array $settings) * @param array $settings The map widget options. * @return MapWidgetInput Chainable */ - public function mergeMapOptions(array $settings) + public function mergeMapOptions(array $settings): static { if (isset($settings['api_key'])) { $this->setApiKey($settings['api_key']); @@ -98,7 +95,7 @@ public function mergeMapOptions(array $settings) * @throws InvalidArgumentException If the identifier is not a string. * @return MapWidgetInput Chainable */ - public function addMapOption($key, $val) + public function addMapOption($key, $val): static { if (!is_string($key)) { throw new InvalidArgumentException( @@ -122,10 +119,8 @@ public function addMapOption($key, $val) /** * Retrieve the map widget's options. - * - * @return array */ - public function mapOptions() + public function mapOptions(): array { if ($this->mapOptions === null) { $this->mapOptions = $this->defaultMapOptions(); @@ -135,10 +130,8 @@ public function mapOptions() /** * Retrieve the default map widget options. - * - * @return array */ - public function defaultMapOptions() + public function defaultMapOptions(): array { return [ 'api_key' => $this->apiKey() ]; } @@ -159,6 +152,7 @@ public function mapOptionsAsJson() * @param Container $container A dependencies container instance. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); diff --git a/packages/admin/src/Charcoal/Admin/Property/Input/NestedWidgetInput.php b/packages/admin/src/Charcoal/Admin/Property/Input/NestedWidgetInput.php index 3f0411686..879eab382 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Input/NestedWidgetInput.php +++ b/packages/admin/src/Charcoal/Admin/Property/Input/NestedWidgetInput.php @@ -34,24 +34,18 @@ class NestedWidgetInput extends AbstractPropertyInput implements /** * Store the widget factory instance for the current class. - * - * @var FactoryInterface */ - private $widgetFactory; + private ?\Charcoal\Factory\FactoryInterface $widgetFactory = null; /** * Store the form group widget factory instance for the current class. - * - * @var FactoryInterface */ - private $formGroupFactory; + private ?\Charcoal\Factory\FactoryInterface $formGroupFactory = null; /** * The form group the input belongs to. - * - * @var FormGroupInterface */ - private $formGroup; + private ?\Charcoal\Ui\FormGroup\FormGroupInterface $formGroup = null; /** * Set the form input's parent group. @@ -59,7 +53,7 @@ class NestedWidgetInput extends AbstractPropertyInput implements * @param FormGroupInterface $formGroup The parent form group object. * @return FormInputInterface Chainable */ - public function setFormGroup(FormGroupInterface $formGroup) + public function setFormGroup(FormGroupInterface $formGroup): static { $this->formGroup = $formGroup; @@ -71,7 +65,7 @@ public function setFormGroup(FormGroupInterface $formGroup) * * @return FormGroupInterface */ - public function formGroup() + public function formGroup(): ?\Charcoal\Ui\FormGroup\FormGroupInterface { return $this->formGroup; } @@ -82,6 +76,7 @@ public function formGroup() * @param Container $container A dependencies container instance. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -94,9 +89,8 @@ protected function setDependencies(Container $container) * Set the widget factory. * * @param FactoryInterface $factory The factory to create widgets. - * @return self */ - protected function setWidgetFactory(FactoryInterface $factory) + protected function setWidgetFactory(FactoryInterface $factory): static { $this->widgetFactory = $factory; @@ -107,14 +101,13 @@ protected function setWidgetFactory(FactoryInterface $factory) * Retrieve the widget factory. * * @throws RuntimeException If the widget factory was not previously set. - * @return FactoryInterface */ - protected function widgetFactory() + protected function widgetFactory(): \Charcoal\Factory\FactoryInterface { - if ($this->widgetFactory === null) { + if (!$this->widgetFactory instanceof \Charcoal\Factory\FactoryInterface) { throw new RuntimeException(sprintf( 'Widget Factory is not defined for "%s"', - get_class($this) + static::class )); } @@ -125,9 +118,8 @@ protected function widgetFactory() * Set the form group factory. * * @param FactoryInterface $factory The factory to create form groups. - * @return self */ - protected function setFormGroupFactory(FactoryInterface $factory) + protected function setFormGroupFactory(FactoryInterface $factory): static { $this->formGroupFactory = $factory; @@ -138,14 +130,13 @@ protected function setFormGroupFactory(FactoryInterface $factory) * Retrieve the form group factory. * * @throws RuntimeException If the form group factory was not previously set. - * @return FactoryInterface */ - protected function formGroupFactory() + protected function formGroupFactory(): \Charcoal\Factory\FactoryInterface { - if ($this->formGroupFactory === null) { + if (!$this->formGroupFactory instanceof \Charcoal\Factory\FactoryInterface) { throw new RuntimeException(sprintf( 'Form Group Factory is not defined for "%s"', - get_class($this) + static::class )); } diff --git a/packages/admin/src/Charcoal/Admin/Property/Input/NumberInput.php b/packages/admin/src/Charcoal/Admin/Property/Input/NumberInput.php index 4b43977da..b4bb9496b 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Input/NumberInput.php +++ b/packages/admin/src/Charcoal/Admin/Property/Input/NumberInput.php @@ -1,5 +1,7 @@ min = null; @@ -56,18 +52,12 @@ public function setMin($min) return $this; } - /** - * @return boolean - */ - public function hasMin() + public function hasMin(): bool { return !(empty($this->min) && !is_numeric($this->min)); } - /** - * @return integer|float|null - */ - public function min() + public function min(): int|float|null { return $this->min; } @@ -77,7 +67,7 @@ public function min() * @throws InvalidArgumentException If the argument is not a number. * @return Text Chainable */ - public function setMax($max) + public function setMax($max): static { if ($max === null || $max === '') { $this->max = null; @@ -94,18 +84,12 @@ public function setMax($max) return $this; } - /** - * @return boolean - */ - public function hasMax() + public function hasMax(): bool { return !(empty($this->max) && !is_numeric($this->max)); } - /** - * @return integer|float|null - */ - public function max() + public function max(): int|float|null { return $this->max; } @@ -115,7 +99,7 @@ public function max() * @throws InvalidArgumentException If the value is not a number. * @return Text Chainable */ - public function setStep($step) + public function setStep($step): static { if ($step === null || $step === '') { $this->step = null; @@ -137,18 +121,12 @@ public function setStep($step) return $this; } - /** - * @return boolean - */ - public function hasStep() + public function hasStep(): bool { return !(empty($this->step) && !is_numeric($this->step)); } - /** - * @return string|integer|float|null - */ - public function step() + public function step(): string|int|float|null { return $this->step; } diff --git a/packages/admin/src/Charcoal/Admin/Property/Input/PasswordInput.php b/packages/admin/src/Charcoal/Admin/Property/Input/PasswordInput.php index 19d206109..200db6481 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Input/PasswordInput.php +++ b/packages/admin/src/Charcoal/Admin/Property/Input/PasswordInput.php @@ -1,5 +1,7 @@ `. - * - * @return string */ - public function type() + #[\Override] + public function type(): string { return 'password'; } diff --git a/packages/admin/src/Charcoal/Admin/Property/Input/PermalinkInput.php b/packages/admin/src/Charcoal/Admin/Property/Input/PermalinkInput.php index 9c30149f5..91520639c 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Input/PermalinkInput.php +++ b/packages/admin/src/Charcoal/Admin/Property/Input/PermalinkInput.php @@ -42,6 +42,7 @@ class PermalinkInput extends TextInput * @param Container $container A service locator. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -55,9 +56,9 @@ protected function setDependencies(Container $container) * Used for the HTML "ID" attribute. * * @param string $inputId HTML input id attribute. - * @return self */ - public function setInputId($inputId) + #[\Override] + public function setInputId($inputId): static { parent::setInputId($inputId); $this->sampleId = $inputId; @@ -66,10 +67,8 @@ public function setInputId($inputId) /** * Get the permalink's absolute URI. - * - * @return string|null */ - public function viewLink() + public function viewLink(): string { $link = null; $locale = $this->lang(); @@ -88,9 +87,8 @@ public function viewLink() * Set the permalink's immutable base. * * @param mixed $route The base URI. - * @return self */ - protected function setBaseRoute($route) + protected function setBaseRoute($route): static { $this->baseRoute = $this->translator()->translation($route); return $this; @@ -98,10 +96,8 @@ protected function setBaseRoute($route) /** * Get the permalink's immutable base. - * - * @return string|null */ - public function baseRoute() + public function baseRoute(): string { if ($this->baseRoute === null) { $this->baseRoute = $this->baseUrl(); @@ -117,7 +113,7 @@ public function baseRoute() $translator->setLocale($origLocale); } - return rtrim((string)$link, '/') . '/'; + return rtrim($link, '/') . '/'; } /** @@ -128,7 +124,7 @@ public function baseRoute() public function editableRoute() { $link = $this->inputVal(); - if (empty($link)) { + if (in_array($link, [null, '', '0'], true)) { $link = $this->placeholder(); } @@ -139,9 +135,8 @@ public function editableRoute() * Set the base URI of the project. * * @param UriInterface $uri The base URI. - * @return self */ - protected function setBaseUrl(UriInterface $uri) + protected function setBaseUrl(UriInterface $uri): static { $this->baseUrl = $uri; return $this; @@ -155,10 +150,10 @@ protected function setBaseUrl(UriInterface $uri) */ public function baseUrl() { - if (!isset($this->baseUrl)) { + if ($this->baseUrl === null) { throw new RuntimeException(sprintf( 'The base URI is not defined for [%s]', - get_class($this) + static::class )); } @@ -195,9 +190,8 @@ public function samples() * Used for the HTML "ID" attribute. * * @param string $id HTML sample ID attribute. - * @return self */ - public function setSampleId($id) + public function setSampleId($id): static { $this->sampleId = $id; return $this; @@ -221,10 +215,8 @@ public function sampleId() /** * Generate a unique sample ID. - * - * @return string */ - protected function generateSampleId() + protected function generateSampleId(): string { return 'sample_' . uniqid(); } diff --git a/packages/admin/src/Charcoal/Admin/Property/Input/PhoneInput.php b/packages/admin/src/Charcoal/Admin/Property/Input/PhoneInput.php index e9faf80d3..c9d623803 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Input/PhoneInput.php +++ b/packages/admin/src/Charcoal/Admin/Property/Input/PhoneInput.php @@ -1,5 +1,7 @@ `. - * - * @return string */ - public function type() + #[\Override] + public function type(): string { return 'tel'; } diff --git a/packages/admin/src/Charcoal/Admin/Property/Input/RadioBtnGroupInput.php b/packages/admin/src/Charcoal/Admin/Property/Input/RadioBtnGroupInput.php index 84311e0a9..c14504e2f 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Input/RadioBtnGroupInput.php +++ b/packages/admin/src/Charcoal/Admin/Property/Input/RadioBtnGroupInput.php @@ -1,5 +1,7 @@ `. - * - * @return string */ - public function type() + public function type(): string { return 'radio'; } /** * Never accept multiple values. - * - * @return boolean */ - public function multiple() + #[\Override] + public function multiple(): bool { return false; } diff --git a/packages/admin/src/Charcoal/Admin/Property/Input/RangeInput.php b/packages/admin/src/Charcoal/Admin/Property/Input/RangeInput.php index 709ca214d..571a0a4aa 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Input/RangeInput.php +++ b/packages/admin/src/Charcoal/Admin/Property/Input/RangeInput.php @@ -1,5 +1,7 @@ showRangeValue = (bool)$show; $this->setRangeValueLocation($show); @@ -39,10 +38,8 @@ public function setShowRangeValue($show) /** * Determine if the property's value should be displayed. - * - * @return boolean */ - public function showRangeValue() + public function showRangeValue(): bool { return $this->showRangeValue; } @@ -56,9 +53,8 @@ public function showRangeValue() * CSS selector, the query selector lookup will be done with the input's * ID prefix (e.g., "my_range_output" → `#input_5db6fc900736b_my_range_output`). * @throws InvalidArgumentException If the show flag is invalid. - * @return self */ - public function setRangeValueLocation($location) + public function setRangeValueLocation($location): static { switch ($location) { case false: @@ -79,7 +75,7 @@ public function setRangeValueLocation($location) throw new InvalidArgumentException(sprintf( 'Invalid range value location: %s ', - (is_object($location) ? get_class($location) : gettype($location)) + (get_debug_type($location)) )); } @@ -95,10 +91,9 @@ public function rangeValueLocation() /** * Retrieve the control's data options for JavaScript components. - * - * @return array */ - public function controlDataForJs() + #[\Override] + public function controlDataForJs(): array { return [ // Base Control diff --git a/packages/admin/src/Charcoal/Admin/Property/Input/ReadonlyInput.php b/packages/admin/src/Charcoal/Admin/Property/Input/ReadonlyInput.php index 74b8fa780..7b9b0432c 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Input/ReadonlyInput.php +++ b/packages/admin/src/Charcoal/Admin/Property/Input/ReadonlyInput.php @@ -40,17 +40,13 @@ class ReadonlyInput extends AbstractPropertyInput /** * Whether the placeholder text should be shown when the value is empty. - * - * @var boolean */ - private $showPlaceholder = false; + private bool $showPlaceholder = false; /** * Store the factory instance for the current class. - * - * @var FactoryInterface */ - private $propertyDisplayFactory; + private ?\Charcoal\Factory\FactoryInterface $propertyDisplayFactory = null; /** * @return string @@ -113,6 +109,7 @@ public function displayVal() * @throws UnexpectedValueException If the value is invalid. * @return string */ + #[\Override] public function inputVal() { $property = $this->property(); @@ -139,7 +136,7 @@ public function inputVal() if (!is_scalar($val)) { throw new UnexpectedValueException(sprintf( 'Property Input Value must be a string, received %s', - (is_object($val) ? get_class($val) : gettype($val)) + (get_debug_type($val)) )); } @@ -149,6 +146,7 @@ public function inputVal() /** * @return boolean */ + #[\Override] public function hasPropertyVal() { if ($this->hasPropertyVal === null) { @@ -164,9 +162,9 @@ public function hasPropertyVal() * @param boolean $show Show (TRUE) or hide (FALSE) the notes. * @return UiItemInterface Chainable */ - public function setShowPlaceholder($show) + public function setShowPlaceholder($show): static { - $this->showPlaceholder = !!$show; + $this->showPlaceholder = (bool) $show; return $this; } @@ -201,7 +199,7 @@ public function maybeSerializeValue($input) return $output; } } - } catch (JsonException $e) { + } catch (JsonException) { // do nothing } } @@ -223,7 +221,7 @@ public function maybeUnserializeValue($input) if (!is_scalar($output) && !is_null($output)) { return $output; } - } catch (JsonException $e) { + } catch (JsonException) { // do nothing } } @@ -263,10 +261,9 @@ public function getRenderType() /** * Retrieve the default display options. - * - * @return array */ - public function getDefaultInputOptions() + #[\Override] + public function getDefaultInputOptions(): array { return [ 'maybe_input_is_serialized' => static::DEFAULT_MAYBE_INPUT_IS_SERIALIZED, @@ -279,6 +276,7 @@ public function getDefaultInputOptions() * @param Container $container Pimple DI container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -302,13 +300,12 @@ protected function setPropertyDisplayFactory(FactoryInterface $factory) * Retrieve the property display factory. * * @throws RuntimeException If the property display factory was not previously set. - * @return FactoryInterface */ - protected function getPropertyDisplayFactory() + protected function getPropertyDisplayFactory(): \Charcoal\Factory\FactoryInterface { - if (!isset($this->propertyDisplayFactory)) { + if (!$this->propertyDisplayFactory instanceof \Charcoal\Factory\FactoryInterface) { throw new RuntimeException( - sprintf('Property Display Factory is not defined for "%s"', get_class($this)) + sprintf('Property Display Factory is not defined for "%s"', static::class) ); } diff --git a/packages/admin/src/Charcoal/Admin/Property/Input/ReadonlySelectInput.php b/packages/admin/src/Charcoal/Admin/Property/Input/ReadonlySelectInput.php index cb7b57e45..0e613688b 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Input/ReadonlySelectInput.php +++ b/packages/admin/src/Charcoal/Admin/Property/Input/ReadonlySelectInput.php @@ -1,5 +1,7 @@ p()['allowNull'] && !$this->p()['multiple']) { @@ -52,6 +51,7 @@ public function choices() * @param array|object $choice The choice structure. * @return array|null */ + #[\Override] protected function parseChoice($ident, $choice) { $choice = parent::parseChoice($ident, $choice); @@ -85,7 +85,7 @@ protected function parseChoice($ident, $choice) * @param array $settings The select picker options. * @return Selectinput Chainable */ - public function setSelectOptions(array $settings) + public function setSelectOptions(array $settings): static { $this->selectOptions = array_merge($this->defaultSelectOptions(), $settings); @@ -98,7 +98,7 @@ public function setSelectOptions(array $settings) * @param array $settings The select picker options. * @return Selectinput Chainable */ - public function mergeSelectOptions(array $settings) + public function mergeSelectOptions(array $settings): static { $this->selectOptions = array_merge($this->selectOptions, $settings); @@ -113,7 +113,7 @@ public function mergeSelectOptions(array $settings) * @throws InvalidArgumentException If the identifier is not a string. * @return Selectinput Chainable */ - public function addSelectOption($key, $val) + public function addSelectOption($key, $val): static { if (!is_string($key)) { throw new InvalidArgumentException( @@ -133,10 +133,8 @@ public function addSelectOption($key, $val) /** * Retrieve the select picker's options. - * - * @return array */ - public function selectOptions() + public function selectOptions(): array { if ($this->selectOptions === null) { $this->selectOptions = $this->defaultSelectOptions(); @@ -147,10 +145,8 @@ public function selectOptions() /** * Retrieve the default select picker options. - * - * @return array */ - public function defaultSelectOptions() + public function defaultSelectOptions(): array { return [ 'style' => '', diff --git a/packages/admin/src/Charcoal/Admin/Property/Input/Selectize/ListInput.php b/packages/admin/src/Charcoal/Admin/Property/Input/Selectize/ListInput.php index e6f7887a4..d62ba53b2 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Input/Selectize/ListInput.php +++ b/packages/admin/src/Charcoal/Admin/Property/Input/Selectize/ListInput.php @@ -1,5 +1,7 @@ inputName) { - $name = $this->inputName; - } else { - $name = $this->propertyIdent(); - } + $name = $this->inputName ?: $this->propertyIdent(); if ($this->p()['l10n']) { $name .= '[' . $this->lang() . ']'; @@ -86,6 +79,7 @@ public function inputName() * @todo [^1]: With PHP7 we can simply do `yield from $choices;`. * @return \Generator */ + #[\Override] public function choices() { if ($this->p()['allowNull'] && !$this->p()['multiple']) { @@ -112,9 +106,8 @@ public function formWidget() /** * @param string $formWidget The form widget for object creation and modification. - * @return self */ - public function setFormWidget($formWidget) + public function setFormWidget($formWidget): static { $this->formWidget = $formWidget; @@ -125,11 +118,10 @@ public function setFormWidget($formWidget) * Show/hide the "Copy to Clipboard" button. * * @param boolean $flag Show (TRUE) or hide (FALSE) the copy button. - * @return self */ - public function setAllowClipboardCopy($flag) + public function setAllowClipboardCopy($flag): static { - $this->allowClipboardCopy = !!$flag; + $this->allowClipboardCopy = (bool) $flag; return $this; } @@ -152,7 +144,7 @@ public function allowClipboardCopy() * @param array $settings The selectize picker options. * @return TagsInput Chainable */ - public function setSelectizeOptions(array $settings) + public function setSelectizeOptions(array $settings): static { $this->selectizeOptions = array_merge( $this->defaultSelectizeOptions(), @@ -168,7 +160,7 @@ public function setSelectizeOptions(array $settings) * @param array $settings The selectize picker options. * @return TagsInput Chainable */ - public function mergeSelectizeOptions(array $settings) + public function mergeSelectizeOptions(array $settings): static { $this->selectizeOptions = array_merge( $this->selectizeOptions, @@ -186,7 +178,7 @@ public function mergeSelectizeOptions(array $settings) * @throws InvalidArgumentException If the identifier is not a string. * @return self Chainable */ - public function addSelectizeOption($key, $val) + public function addSelectizeOption($key, $val): static { if (!is_string($key)) { throw new InvalidArgumentException( @@ -260,10 +252,9 @@ public function selectizeOptionsAsJson() /** * Retrieve the default object-to-choice data map. - * - * @return array */ - public function defaultChoiceObjMap() + #[\Override] + public function defaultChoiceObjMap(): array { return [ 'value' => 'id', @@ -274,10 +265,9 @@ public function defaultChoiceObjMap() /** * Retrieve the control's data options for JavaScript components. - * - * @return array */ - public function controlDataForJs() + #[\Override] + public function controlDataForJs(): array { $prop = $this->property(); @@ -298,11 +288,9 @@ public function controlDataForJs() 'multiple_options' => $this->property()['multipleOptions'], ]; - if ($prop instanceof ObjectProperty) { - if ($prop['objType']) { - $data['pattern'] = $prop['pattern']; - $data['obj_type'] = $prop['objType']; - } + if ($prop instanceof ObjectProperty && $prop['objType']) { + $data['pattern'] = $prop['pattern']; + $data['obj_type'] = $prop['objType']; } return $data; @@ -314,6 +302,7 @@ public function controlDataForJs() * @param Container $container A dependencies container instance. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -326,14 +315,13 @@ protected function setDependencies(Container $container) * Retrieve the object model factory. * * @throws RuntimeException If the model factory was not previously set. - * @return FactoryInterface */ - protected function modelFactory() + protected function modelFactory(): \Charcoal\Factory\FactoryInterface { - if (!isset($this->modelFactory)) { + if (!$this->modelFactory instanceof \Charcoal\Factory\FactoryInterface) { throw new RuntimeException(sprintf( 'Model Factory is not defined for "%s"', - get_class($this) + static::class )); } @@ -346,7 +334,7 @@ protected function modelFactory() * @param array $settings The selectize picker options. * @return array Returns the parsed options. */ - protected function parseSelectizeOptions(array $settings) + protected function parseSelectizeOptions(array $settings): array { return $settings; } @@ -355,9 +343,8 @@ protected function parseSelectizeOptions(array $settings) * Set an object model factory. * * @param FactoryInterface $factory The model factory, to create objects. - * @return self */ - private function setModelFactory(FactoryInterface $factory) + private function setModelFactory(FactoryInterface $factory): static { $this->modelFactory = $factory; @@ -368,9 +355,8 @@ private function setModelFactory(FactoryInterface $factory) * Set a model collection loader. * * @param CollectionLoader $loader The collection loader. - * @return self */ - private function setCollectionLoader(CollectionLoader $loader) + private function setCollectionLoader(CollectionLoader $loader): static { $this->collectionLoader = $loader; @@ -382,7 +368,7 @@ private function setCollectionLoader(CollectionLoader $loader) * * @return CollectionLoader */ - private function collectionLoader() + private function collectionLoader(): ?\Charcoal\Loader\CollectionLoader { return $this->collectionLoader; } @@ -392,9 +378,8 @@ private function collectionLoader() * * @param mixed $val The value to parse into selectize choices. * @param array $options Optional structure options. - * @return array */ - private function selectizeVal($val = null, array $options = []) + private function selectizeVal($val = null, array $options = []): array { /** @todo Find a use for this */ unset($options); diff --git a/packages/admin/src/Charcoal/Admin/Property/Input/Selectize/Template/SpriteTemplate.php b/packages/admin/src/Charcoal/Admin/Property/Input/Selectize/Template/SpriteTemplate.php index 0dc45e5ee..0c1d11717 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Input/Selectize/Template/SpriteTemplate.php +++ b/packages/admin/src/Charcoal/Admin/Property/Input/Selectize/Template/SpriteTemplate.php @@ -30,6 +30,7 @@ class SpriteTemplate extends AbstractTemplate * @param Container $container A Pimple DI container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -47,9 +48,8 @@ public function showSpriteId() /** * @param boolean $flag Show the sprite id besides the icon. - * @return self */ - public function setShowSpriteId($flag) + public function setShowSpriteId($flag): static { $this->showSpriteId = $flag; diff --git a/packages/admin/src/Charcoal/Admin/Property/Input/SelectizeInput.php b/packages/admin/src/Charcoal/Admin/Property/Input/SelectizeInput.php index 06f60e543..5a0be9d45 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Input/SelectizeInput.php +++ b/packages/admin/src/Charcoal/Admin/Property/Input/SelectizeInput.php @@ -48,24 +48,18 @@ class SelectizeInput extends SelectInput /** * Store the factory instance for the current class. - * - * @var FactoryInterface */ - private $modelFactory; + private ?\Charcoal\Factory\FactoryInterface $modelFactory = null; /** * Store the collection loader for the current class. - * - * @var CollectionLoader */ - private $collectionLoader; + private ?\Charcoal\Loader\CollectionLoader $collectionLoader = null; /** * Should the object be loaded in deferred mode. - * - * @var boolean */ - private $deferred; + private ?bool $deferred = null; /** * Whether to show a button to allow update items. @@ -104,10 +98,8 @@ class SelectizeInput extends SelectInput * The form data to use while creating objects through Selectize. * * Must be an array - * - * @var array|null */ - private $formData; + private ?array $formData = null; /** * Label for the create item dialog. @@ -130,20 +122,14 @@ class SelectizeInput extends SelectInput */ protected $isChoiceObjMapFinalized = false; - /** - * @var array - */ - private $selectizeTemplates; + private object|array|null $selectizeTemplates = null; /** * @var SelectizeRenderer */ private $selectizeRenderer; - /** - * @var array - */ - private $disabledFields = []; + private array $disabledFields = []; /** * @var string $remoteSource @@ -162,10 +148,8 @@ class SelectizeInput extends SelectInput /** * Check used to parse multi Optgroup map against the obj properties. - * - * @var boolean */ - private $isOptgroupObjMapFinalized = false; + private bool $isOptgroupObjMapFinalized = false; /** * This function takes an array and fill the model object with its value. @@ -179,9 +163,9 @@ class SelectizeInput extends SelectInput * on the metadata object, because the method `set_foobar()` does not exist. * * @param array $data The input data. - * @return self */ - public function setData(array $data) + #[\Override] + public function setData(array $data): static { // Push selectize options back at the end of the data container. if (isset($data['selectizeOptions'])) { @@ -199,14 +183,13 @@ public function setData(array $data) * Retrieve the object model factory. * * @throws RuntimeException If the model factory was not previously set. - * @return FactoryInterface */ - public function modelFactory() + public function modelFactory(): \Charcoal\Factory\FactoryInterface { - if (!isset($this->modelFactory)) { + if (!$this->modelFactory instanceof \Charcoal\Factory\FactoryInterface) { throw new RuntimeException(sprintf( 'Model Factory is not defined for "%s"', - get_class($this) + static::class )); } @@ -217,9 +200,8 @@ public function modelFactory() * Set a model collection loader. * * @param CollectionLoader $loader The collection loader. - * @return self */ - private function setCollectionLoader(CollectionLoader $loader) + private function setCollectionLoader(CollectionLoader $loader): static { $this->collectionLoader = $loader; @@ -230,14 +212,13 @@ private function setCollectionLoader(CollectionLoader $loader) * Retrieve the model collection loader. * * @throws RuntimeException If the collection loader was not previously set. - * @return CollectionLoader */ - protected function collectionLoader() + protected function collectionLoader(): \Charcoal\Loader\CollectionLoader { - if (!isset($this->collectionLoader)) { + if (!$this->collectionLoader instanceof \Charcoal\Loader\CollectionLoader) { throw new RuntimeException(sprintf( 'Collection Loader is not defined for "%s"', - get_class($this) + static::class )); } @@ -246,10 +227,9 @@ protected function collectionLoader() /** * Retrieve the default empty option structure. - * - * @return array */ - protected function defaultEmptyChoice() + #[\Override] + protected function defaultEmptyChoice(): array { return [ 'value' => '', @@ -265,6 +245,7 @@ protected function defaultEmptyChoice() * @todo [^1]: With PHP7 we can simply do `yield from $choices;`. * @return \Generator|array */ + #[\Override] public function choices() { if ($this->p()['allowNull'] && !$this->p()['multiple']) { @@ -273,11 +254,7 @@ public function choices() } // When deferred, we want to fetch choices for current values only. - if ($this->deferred()) { - $choices = $this->selectizeVal($this->propertyVal()); - } else { - $choices = $this->selectizeVal($this->p()->choices()); - } + $choices = $this->deferred() ? $this->selectizeVal($this->propertyVal()) : $this->selectizeVal($this->p()->choices()); /* Pass along the Generator from the parent method [^1] */ /* Filter the all options down to those *not* selected */ @@ -291,22 +268,20 @@ public function choices() /** * Create an input group to nest extra inputs alongside selectize - * @return boolean */ - public function inputGroup() + public function inputGroup(): bool { - return !!($this->allowClipboardCopy() || $this->allowUpdate() || $this->allowCreate()); + return $this->allowClipboardCopy() || $this->allowUpdate() || $this->allowCreate(); } /** * Show/hide the "Copy to Clipboard" button. * * @param boolean $flag Show (TRUE) or hide (FALSE) the copy button. - * @return self */ - public function setAllowClipboardCopy($flag) + public function setAllowClipboardCopy($flag): static { - $this->allowClipboardCopy = !!$flag; + $this->allowClipboardCopy = (bool) $flag; return $this; } @@ -323,11 +298,10 @@ public function allowClipboardCopy() /** * @param boolean $allowUpdate Show (TRUE) or hide (FALSE) the update button. - * @return self */ - public function setAllowUpdate($allowUpdate) + public function setAllowUpdate($allowUpdate): static { - $this->allowUpdate = !!$allowUpdate; + $this->allowUpdate = (bool) $allowUpdate; return $this; } @@ -344,11 +318,10 @@ public function allowUpdate() /** * @param boolean $allowCreate Show (TRUE) or hide (FALSE) the create button. - * @return self */ - public function setAllowCreate($allowCreate) + public function setAllowCreate($allowCreate): static { - $this->allowCreate = !!$allowCreate; + $this->allowCreate = (bool) $allowCreate; return $this; } @@ -366,18 +339,17 @@ public function allowCreate() /** * @return boolean */ - public function deferred() + public function deferred(): ?bool { return $this->deferred; } /** * @param boolean $deferred Should the object be loaded in deferred mode. - * @return self */ - public function setDeferred($deferred) + public function setDeferred($deferred): static { - $this->deferred = ($this->property() instanceof ObjectProperty || $this->remoteSource()) ? $deferred : false; + $this->deferred = ($this->property() instanceof ObjectProperty || $this->remoteSource()) && $deferred; return $this; } @@ -392,9 +364,8 @@ public function formWidget() /** * @param string $formWidget The form widget for object creation and modification. - * @return self */ - public function setFormWidget($formWidget) + public function setFormWidget($formWidget): static { $this->formWidget = $formWidget; @@ -411,18 +382,14 @@ public function formIdent() /** * @param mixed $formIdent The form ident(s) for object creation and modification. - * @return self */ - public function setFormIdent($formIdent) + public function setFormIdent($formIdent): static { $this->formIdent = $formIdent; return $this; } - /** - * @return array|null - */ public function getFormData(): ?array { return $this->formData; @@ -430,7 +397,6 @@ public function getFormData(): ?array /** * @param array|null $formData FormData for SelectizeInput. - * @return self */ public function setFormData(?array $formData): self { @@ -451,9 +417,8 @@ public function formIdentAsJson() * Set the title for the create item dialog. * * @param string|string[] $title The dialog title. - * @return self */ - public function setDialogTitleCreate($title) + public function setDialogTitleCreate($title): static { $this->dialogTitleCreate = $this->translator()->translation($title); @@ -474,9 +439,8 @@ public function getDialogTitleCreate() * Set the title for the update item dialog. * * @param string|string[] $title The dialog title. - * @return self */ - public function setDialogTitleUpdate($title) + public function setDialogTitleUpdate($title): static { $this->dialogTitleUpdate = $this->translator()->translation($title); @@ -501,7 +465,7 @@ public function getDialogTitleUpdate() * @param array $settings The selectize picker options. * @return self Chainable */ - public function setSelectizeOptions(array $settings) + public function setSelectizeOptions(array $settings): static { $this->selectizeOptions = array_merge( $this->defaultSelectizeOptions(), @@ -517,7 +481,7 @@ public function setSelectizeOptions(array $settings) * @param array $settings The selectize picker options. * @return self Chainable */ - public function mergeSelectizeOptions(array $settings) + public function mergeSelectizeOptions(array $settings): static { $this->selectizeOptions = array_merge( $this->selectizeOptions, @@ -535,7 +499,7 @@ public function mergeSelectizeOptions(array $settings) * @throws InvalidArgumentException If the identifier is not a string. * @return self Chainable */ - public function addSelectizeOption($key, $val) + public function addSelectizeOption($key, $val): static { if (!is_string($key)) { throw new InvalidArgumentException( @@ -582,11 +546,7 @@ public function defaultSelectizeOptions() $options = $this->parseSelectizeOptions($options); } - if ($this->deferred()) { - $placeholder = $this->translator()->trans('Search…'); - } else { - $placeholder = $this->translator()->trans('Select…'); - } + $placeholder = $this->deferred() ? $this->translator()->trans('Search…') : $this->translator()->trans('Select…'); $options['placeholder'] = $placeholder; $prop = $this->property(); @@ -598,7 +558,7 @@ public function defaultSelectizeOptions() $optgroupProp = $model->p($this->optgroupProperty()); if ($optgroupProp instanceof ObjectProperty) { - $method = [ $this, 'mapObjToOptgroup' ]; + $method = $this->mapObjToOptgroup(...); $loader = $this->collectionLoader()->setModel($optgroupProp['objType']); @@ -621,7 +581,7 @@ public function defaultSelectizeOptions() $optgroups = array_values($optgroupProp->choices()); // Make sure label is converted to string. - array_walk($optgroups, function (&$item) { + array_walk($optgroups, function (array &$item): void { $item['label'] = (string)$item['label']; }); @@ -632,14 +592,10 @@ public function defaultSelectizeOptions() if ($prop instanceof SelectablePropertyInterface) { $choices = iterator_to_array($this->choices()); - if (isset($options['options'])) { - $options['options'] = array_merge($options['options'], $choices); - } else { - $options['options'] = $choices; - } + $options['options'] = isset($options['options']) ? array_merge($options['options'], $choices) : $choices; // L10n properties is not supported through selectize items array, - $items = !$prop['l10n'] ? $this->propertyVal() : null; + $items = $prop['l10n'] ? null : $this->propertyVal(); if ($items !== null && $prop instanceof AbstractProperty) { $items = $this->property()->inputVal($items); @@ -691,7 +647,7 @@ public function selectizeOptionsAsJson() * @param array $settings The selectize picker options. * @return array Returns the parsed options. */ - protected function parseSelectizeOptions(array $settings) + protected function parseSelectizeOptions(array $settings): array { // Translate labels $settings = $this->recursiveTranslation($settings); @@ -701,9 +657,8 @@ protected function parseSelectizeOptions(array $settings) /** * @param array $array The array of possible translation. - * @return array */ - private function recursiveTranslation(array $array) + private function recursiveTranslation(array $array): array { foreach ($array as &$item) { if (is_array($item)) { @@ -719,12 +674,9 @@ private function recursiveTranslation(array $array) return $array; } - /** - * @return boolean - */ - public function isObject() + public function isObject(): bool { - return !!($this->p() instanceof ObjectProperty); + return $this->p() instanceof ObjectProperty; } /** @@ -734,13 +686,10 @@ public function isObject() * * @return string */ + #[\Override] public function inputName() { - if ($this->inputName) { - $name = $this->inputName; - } else { - $name = $this->propertyIdent(); - } + $name = $this->inputName ?: $this->propertyIdent(); if ($this->p()['l10n']) { $name .= '[' . $this->lang() . ']'; @@ -756,7 +705,7 @@ public function inputName() /** * @return array */ - public function selectizeTemplates() + public function selectizeTemplates(): object|array|null { return $this->selectizeTemplates; } @@ -764,9 +713,8 @@ public function selectizeTemplates() /** * @param array|object|mixed $selectizeTemplates Selectize Templates array. * @throws \InvalidArgumentException If the supplied argument is not of type object. - * @return self */ - public function setSelectizeTemplates($selectizeTemplates) + public function setSelectizeTemplates($selectizeTemplates): static { if (!is_object($selectizeTemplates) && !is_array($selectizeTemplates)) { $selectizeTemplates = [ @@ -795,9 +743,8 @@ public function selectizeTemplatesAsJson() * @param mixed $val The value to parse into selectize choices. * @param array $options Optional structure options. * @throws InvalidArgumentException If the choice structure is missing a value. - * @return array */ - public function selectizeVal($val, array $options = []) + public function selectizeVal($val, array $options = []): array { /** @todo Find a use for this */ unset($options); @@ -822,10 +769,10 @@ public function selectizeVal($val, array $options = []) } $selectizeTemplates = $this->selectizeTemplates(); - $itemTemplate = isset($selectizeTemplates['item']) ? $selectizeTemplates['item'] : null; - $optionTemplate = isset($selectizeTemplates['option']) ? $selectizeTemplates['option'] : null; - $selectizeController = isset($selectizeTemplates['controller']) ? $selectizeTemplates['controller'] : null; - $selectizeData = isset($selectizeTemplates['data']) ? $selectizeTemplates['data'] : []; + $itemTemplate = $selectizeTemplates['item'] ?? null; + $optionTemplate = $selectizeTemplates['option'] ?? null; + $selectizeController = $selectizeTemplates['controller'] ?? null; + $selectizeData = $selectizeTemplates['data'] ?? []; if ($prop instanceof ObjectProperty) { foreach ($val as &$v) { @@ -945,6 +892,7 @@ public function sortObjects($objects) * * @return array Returns a data map to abide. */ + #[\Override] public function choiceObjMap() { $map = parent::choiceObjMap(); @@ -963,7 +911,7 @@ public function choiceObjMap() } foreach ($map as &$mapProp) { - $props = explode(':', $mapProp); + $props = explode(':', (string) $mapProp); foreach ($props as $p) { if (isset($objProperties[$p])) { $mapProp = $p; @@ -981,10 +929,9 @@ public function choiceObjMap() /** * Retrieve the default object-to-choice data map. - * - * @return array */ - public function defaultChoiceObjMap() + #[\Override] + public function defaultChoiceObjMap(): array { return [ 'value' => 'id', @@ -995,9 +942,8 @@ public function defaultChoiceObjMap() /** * @param array|\ArrayAccess|ModelInterface $obj The object to map to a optgroup. - * @return array */ - public function mapObjToOptgroup($obj) + public function mapObjToOptgroup($obj): array { $map = $this->optgroupObjMap(); @@ -1029,9 +975,8 @@ public function optgroupObjMap() /** * @param array|null $optgroupObjMap OptgroupObjMap for SelectizeInput. - * @return self */ - public function setOptgroupObjMap($optgroupObjMap) + public function setOptgroupObjMap($optgroupObjMap): static { $map = $optgroupObjMap ?: $this->defaultOptgroupObjMap(); @@ -1057,7 +1002,7 @@ public function setOptgroupObjMap($optgroupObjMap) } foreach ($map as &$mapProp) { - $props = explode(':', $mapProp); + $props = explode(':', (string) $mapProp); foreach ($props as $p) { if (isset($objProperties[$p])) { $mapProp = $p; @@ -1077,10 +1022,8 @@ public function setOptgroupObjMap($optgroupObjMap) /** * Retrieve the default object-to-optgroup data map. - * - * @return array */ - public function defaultOptgroupObjMap() + public function defaultOptgroupObjMap(): array { return [ 'value' => 'id', @@ -1091,10 +1034,9 @@ public function defaultOptgroupObjMap() /** * Retrieve the control's data options for JavaScript components. - * - * @return array */ - public function controlDataForJs() + #[\Override] + public function controlDataForJs(): array { $prop = $this->property(); @@ -1130,29 +1072,23 @@ public function controlDataForJs() 'multiple_options' => $this->property()['multipleOptions'], ]; - if ($prop instanceof ObjectProperty) { - if ($prop['objType']) { - $data['pattern'] = $prop['pattern']; - $data['obj_type'] = $prop['objType']; - } + if ($prop instanceof ObjectProperty && $prop['objType']) { + $data['pattern'] = $prop['pattern']; + $data['obj_type'] = $prop['objType']; } return $data; } - /** - * @return array - */ - public function disabledFields() + public function disabledFields(): array { return $this->disabledFields; } /** * @param array $disabledFields DisabledFields for SelectizeInput. - * @return self */ - public function setDisabledFields(array $disabledFields) + public function setDisabledFields(array $disabledFields): static { $this->disabledFields = $disabledFields; @@ -1169,9 +1105,8 @@ public function remoteSource() /** * @param string $remoteSource RemoteSource for SelectizeInput. - * @return self */ - public function setRemoteSource($remoteSource) + public function setRemoteSource($remoteSource): static { $this->remoteSource = $remoteSource; @@ -1192,9 +1127,8 @@ public function optgroupProperty() /** * @param string|null $optgroupProperty OptgroupProperty for SelectizeInput. - * @return self */ - public function setOptgroupProperty($optgroupProperty) + public function setOptgroupProperty($optgroupProperty): static { $this->optgroupProperty = $optgroupProperty; @@ -1207,6 +1141,7 @@ public function setOptgroupProperty($optgroupProperty) * @param Container $container A dependencies container instance. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); diff --git a/packages/admin/src/Charcoal/Admin/Property/Input/StructureWidgetInput.php b/packages/admin/src/Charcoal/Admin/Property/Input/StructureWidgetInput.php index 7561e3057..4bd467e07 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Input/StructureWidgetInput.php +++ b/packages/admin/src/Charcoal/Admin/Property/Input/StructureWidgetInput.php @@ -20,6 +20,7 @@ class StructureWidgetInput extends NestedWidgetInput * @throws DomainException If the widget is not a structure widget. * @return WidgetInterface */ + #[\Override] protected function createWidget() { $widget = parent::createWidget(); @@ -30,7 +31,7 @@ protected function createWidget() throw new DomainException(sprintf( 'Widget must an instance of %s, received %s', StructureFormGroup::class, - get_class($widget) + $widget::class )); } @@ -39,10 +40,9 @@ protected function createWidget() /** * Retrieve the default structure widget options. - * - * @return array */ - public function defaultWidgetData() + #[\Override] + public function defaultWidgetData(): array { return [ 'type' => StructureFormGroup::class diff --git a/packages/admin/src/Charcoal/Admin/Property/Input/SwitchInput.php b/packages/admin/src/Charcoal/Admin/Property/Input/SwitchInput.php index d89ea67df..db1970ca7 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Input/SwitchInput.php +++ b/packages/admin/src/Charcoal/Admin/Property/Input/SwitchInput.php @@ -1,5 +1,7 @@ `. - * - * @return string */ - public function type() + public function type(): string { return 'checkbox'; } - /** - * @return boolean - */ - public function checked() + public function checked(): bool { - return !!$this->inputVal(); + return (bool) $this->inputVal(); } - /** - * @return integer - */ - public function value() + public function value(): int { return $this->inputVal() ? 1 : 0; } diff --git a/packages/admin/src/Charcoal/Admin/Property/Input/TabulatorInput.php b/packages/admin/src/Charcoal/Admin/Property/Input/TabulatorInput.php index b767638a5..e2e69cf67 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Input/TabulatorInput.php +++ b/packages/admin/src/Charcoal/Admin/Property/Input/TabulatorInput.php @@ -27,6 +27,7 @@ class TabulatorInput extends AbstractPropertyInput * * @param array $options The input options. */ + #[\Override] public function setInputOptions(array $options): self { parent::setInputOptions($options); @@ -79,6 +80,7 @@ public function addInputOption(string $key, $val): self * * @return array */ + #[\Override] public function getDefaultInputOptions(): array { $translator = $this->translator(); @@ -175,9 +177,8 @@ public function getDefaultTabulatorOptions(): array /** * Retrieve the control's data options for JavaScript components. - * - * @return array */ + #[\Override] public function controlDataForJs(): array { $inputOptions = $this->getInputOptions(); diff --git a/packages/admin/src/Charcoal/Admin/Property/Input/TextInput.php b/packages/admin/src/Charcoal/Admin/Property/Input/TextInput.php index d314e9211..506ce455e 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Input/TextInput.php +++ b/packages/admin/src/Charcoal/Admin/Property/Input/TextInput.php @@ -10,10 +10,7 @@ */ class TextInput extends AbstractPropertyInput { - /** - * @var integer $size - */ - private $size = 0; + private int $size = 0; /** * The minimum number of characters allowed. @@ -21,10 +18,8 @@ class TextInput extends AbstractPropertyInput * Note: * - In Unicode code points. * - If zero or a negative value is specified, the length is ignored. - * - * @var integer */ - private $minLength = 0; + private int $minLength = 0; /** * The maximum number of characters allowed. @@ -33,22 +28,15 @@ class TextInput extends AbstractPropertyInput * - In UTF-16 code units. * - If it is not specified, the control allows an unlimited number of characters. * - If zero or a negative value is specified, the length is ignored. - * - * @var integer */ - private $maxLength = 0; + private int $maxLength = 0; - /** - * @var string $pattern - */ - private $pattern = ''; + private string $pattern = ''; /** * Retrieve the control type for the HTML element ``. - * - * @return string */ - public function type() + public function type(): string { return 'text'; } @@ -61,7 +49,8 @@ public function type() * @see AbstractPropertyInput::inputVal() * @return string */ - public function inputVal() + #[\Override] + public function inputVal(): ?string { return preg_replace('~[\n\r]~', '', parent::inputVal()); } @@ -71,7 +60,7 @@ public function inputVal() * @throws InvalidArgumentException If the argument is not a number. * @return Text Chainable */ - public function setMinLength($minLength) + public function setMinLength($minLength): static { if (!is_numeric($minLength)) { throw new InvalidArgumentException( @@ -83,10 +72,7 @@ public function setMinLength($minLength) return $this; } - /** - * @return integer - */ - public function minLength() + public function minLength(): int { return $this->minLength; } @@ -96,7 +82,7 @@ public function minLength() * @throws InvalidArgumentException If the argument is not a number. * @return Text Chainable */ - public function setMaxLength($maxLength) + public function setMaxLength($maxLength): static { if (!is_numeric($maxLength)) { throw new InvalidArgumentException( @@ -108,10 +94,7 @@ public function setMaxLength($maxLength) return $this; } - /** - * @return integer - */ - public function maxLength() + public function maxLength(): int { return $this->maxLength; } @@ -121,7 +104,7 @@ public function maxLength() * @throws InvalidArgumentException If the argument is not a number. * @return Text Chainable */ - public function setSize($size) + public function setSize($size): static { if (!is_numeric($size)) { throw new InvalidArgumentException( @@ -132,10 +115,7 @@ public function setSize($size) return $this; } - /** - * @return integer - */ - public function size() + public function size(): int { return $this->size; } @@ -145,7 +125,7 @@ public function size() * @throws InvalidArgumentException If the argument is not a string. * @return Text Chainable */ - public function setPattern($pattern) + public function setPattern($pattern): static { if (!is_string($pattern)) { throw new InvalidArgumentException( @@ -156,20 +136,16 @@ public function setPattern($pattern) return $this; } - /** - * @return string - */ - public function pattern() + public function pattern(): string { return $this->pattern; } /** * Retrieve the control's data options for JavaScript components. - * - * @return array */ - public function controlDataForJs() + #[\Override] + public function controlDataForJs(): array { return [ // Text Control diff --git a/packages/admin/src/Charcoal/Admin/Property/Input/TextareaInput.php b/packages/admin/src/Charcoal/Admin/Property/Input/TextareaInput.php index 03f0432d0..15a83a1a9 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Input/TextareaInput.php +++ b/packages/admin/src/Charcoal/Admin/Property/Input/TextareaInput.php @@ -1,5 +1,7 @@ cols; } @@ -60,7 +50,7 @@ public function cols() * @throws InvalidArgumentException If the argument is not a number. * @return Text Chainable */ - public function setRows($rows) + public function setRows($rows): static { if (!is_numeric($rows)) { throw new InvalidArgumentException( @@ -74,7 +64,7 @@ public function setRows($rows) /** * @return integer */ - public function rows() + public function rows(): ?int { return $this->rows; } @@ -84,7 +74,7 @@ public function rows() * @throws InvalidArgumentException If the argument is not a number. * @return Text Chainable */ - public function setMinLength($minLength) + public function setMinLength($minLength): static { if (!is_numeric($minLength)) { throw new InvalidArgumentException( @@ -95,10 +85,7 @@ public function setMinLength($minLength) return $this; } - /** - * @return integer - */ - public function minLength() + public function minLength(): int { return $this->minLength; } @@ -108,7 +95,7 @@ public function minLength() * @throws InvalidArgumentException If the argument is not a number. * @return Text Chainable */ - public function setMaxLength($maxLength) + public function setMaxLength($maxLength): static { if (!is_numeric($maxLength)) { throw new InvalidArgumentException( @@ -119,9 +106,7 @@ public function setMaxLength($maxLength) return $this; } - /** - * @return array - */ + #[\Override] public function getInputValOptions(): array { return [ @@ -129,10 +114,7 @@ public function getInputValOptions(): array ]; } - /** - * @return integer - */ - public function maxLength() + public function maxLength(): int { return $this->maxLength; } diff --git a/packages/admin/src/Charcoal/Admin/Property/Input/Tinymce/BasicInput.php b/packages/admin/src/Charcoal/Admin/Property/Input/Tinymce/BasicInput.php index 9875f5ebe..887f38960 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Input/Tinymce/BasicInput.php +++ b/packages/admin/src/Charcoal/Admin/Property/Input/Tinymce/BasicInput.php @@ -15,7 +15,7 @@ class BasicInput extends TinymceInput * * @param array $data Dependencies. */ - public function __construct(array $data = null) + public function __construct(?array $data = null) { parent::__construct($data); @@ -25,10 +25,8 @@ public function __construct(array $data = null) } } - /** - * @return string - */ - public function inputType() + #[\Override] + public function inputType(): string { return 'charcoal/admin/property/input/tinymce'; } diff --git a/packages/admin/src/Charcoal/Admin/Property/Input/TinymceInput.php b/packages/admin/src/Charcoal/Admin/Property/Input/TinymceInput.php index e0e6fb396..5dab828d4 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Input/TinymceInput.php +++ b/packages/admin/src/Charcoal/Admin/Property/Input/TinymceInput.php @@ -29,10 +29,8 @@ class TinymceInput extends TextareaInput /** * Flag wether the "file picker" popup button should be displaed. - * - * @var boolean */ - private $showFilePicker; + private ?bool $showFilePicker = null; /** * URL for the "file picker" popup. @@ -49,7 +47,7 @@ class TinymceInput extends TextareaInput * @param array $settings The editor options. * @return Tinymce Chainable */ - public function setEditorOptions(array $settings) + public function setEditorOptions(array $settings): static { $this->editorOptions = array_merge($this->defaultEditorOptions(), $settings); @@ -62,7 +60,7 @@ public function setEditorOptions(array $settings) * @param array $settings The editor options. * @return Tinymce Chainable */ - public function mergeEditorOptions(array $settings) + public function mergeEditorOptions(array $settings): static { $this->editorOptions = array_merge($this->editorOptions, $settings); @@ -77,7 +75,7 @@ public function mergeEditorOptions(array $settings) * @throws InvalidArgumentException If the identifier is not a string. * @return Tinymce Chainable */ - public function addEditorOption($key, $val) + public function addEditorOption($key, $val): static { if (!is_string($key)) { throw new InvalidArgumentException( @@ -118,11 +116,7 @@ public function defaultEditorOptions() { $defaultData = $this->metadata()->defaultData(); - if (isset($defaultData['editor_options'])) { - return $defaultData['editor_options']; - } - - return []; + return $defaultData['editor_options'] ?? []; } /** @@ -139,9 +133,8 @@ public function editorOptionsAsJson() * Set the title for the file picker dialog. * * @param mixed $title The dialog title. - * @return self */ - public function setDialogTitle($title) + public function setDialogTitle($title): static { $this->dialogTitle = $this->translator()->translation($title); @@ -153,7 +146,7 @@ public function setDialogTitle($title) * * @return \Charcoal\Translator\Translation|string|null */ - protected function defaultDialogTitle() + protected function defaultDialogTitle(): ?\Charcoal\Translator\Translation { return $this->translator()->translation('filesystem.library.media'); } @@ -176,9 +169,9 @@ public function dialogTitle() * @param boolean $show The show file picker flag. * @return FileInput Chainable */ - public function setShowFilePicker($show) + public function setShowFilePicker($show): static { - $this->showFilePicker = !!$show; + $this->showFilePicker = (bool) $show; return $this; } @@ -195,10 +188,7 @@ public function showFilePicker() return $this->showFilePicker; } - /** - * @return boolean - */ - public function hasFilePicker() + public function hasFilePicker(): bool { return class_exists('\\elFinder'); } @@ -207,7 +197,7 @@ public function hasFilePicker() * @param string $url The file picker AJAX URL. * @return FileInput Chainable */ - public function setFilePickerUrl($url) + public function setFilePickerUrl($url): static { $this->filePickerUrl = $url; return $this; @@ -235,7 +225,7 @@ public function filePickerUrl() * * @return callable|null */ - public function prepareFilePickerUrl() + public function prepareFilePickerUrl(): ?\Closure { if (!$this->showFilePicker()) { return null; @@ -243,7 +233,7 @@ public function prepareFilePickerUrl() $uri = $this->getFilePickerUrlTemplate(); - return function ($noop, LambdaHelper $helper) use ($uri) { + return function ($noop, LambdaHelper $helper) use ($uri): null { $uri = $helper->render($uri); $this->setFilePickerUrl($uri); @@ -253,23 +243,19 @@ public function prepareFilePickerUrl() /** * Retrieve the elFinder connector URL template for rendering. - * - * @return string */ - protected function getFilePickerUrlTemplate() + protected function getFilePickerUrlTemplate(): string { $uri = 'obj_type={{ objType }}&obj_id={{ objId }}&property={{ p.ident }}&callback={{ inputId }}'; - $uri = '{{# withAdminUrl }}elfinder?' . $uri . '{{/ withAdminUrl }}'; - return $uri; + return '{{# withAdminUrl }}elfinder?' . $uri . '{{/ withAdminUrl }}'; } /** * Retrieve the control's data options for JavaScript components. - * - * @return array */ - public function controlDataForJs() + #[\Override] + public function controlDataForJs(): array { return [ 'editor_options' => $this->editorOptions(), diff --git a/packages/admin/src/Charcoal/Admin/Property/Input/UrlInput.php b/packages/admin/src/Charcoal/Admin/Property/Input/UrlInput.php index bb8ea6d70..a5ad5829a 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Input/UrlInput.php +++ b/packages/admin/src/Charcoal/Admin/Property/Input/UrlInput.php @@ -1,5 +1,7 @@ `. - * - * @return string */ - public function type() + #[\Override] + public function type(): string { return 'url'; } diff --git a/packages/admin/src/Charcoal/Admin/Property/Input/VideoInput.php b/packages/admin/src/Charcoal/Admin/Property/Input/VideoInput.php index 0b58728c6..f0a5645b0 100644 --- a/packages/admin/src/Charcoal/Admin/Property/Input/VideoInput.php +++ b/packages/admin/src/Charcoal/Admin/Property/Input/VideoInput.php @@ -12,18 +12,15 @@ class VideoInput extends FileInput { /** * Retrieve list of default file type specifiers. - * - * @return string */ - public function getDefaultAccept() + #[\Override] + public function getDefaultAccept(): string { return 'video/*'; } - /** - * @return string|null - */ - public function filePreview() + #[\Override] + public function filePreview(): string { $value = $this->inputVal(); if ($value) { diff --git a/packages/admin/src/Charcoal/Admin/Property/PropertyDisplayInterface.php b/packages/admin/src/Charcoal/Admin/Property/PropertyDisplayInterface.php index 5c3fa340d..30acfadd7 100644 --- a/packages/admin/src/Charcoal/Admin/Property/PropertyDisplayInterface.php +++ b/packages/admin/src/Charcoal/Admin/Property/PropertyDisplayInterface.php @@ -1,5 +1,7 @@ 'now' ] ]; - - $arguments = array_merge(parent::defaultArguments(), $arguments); - return $arguments; + return array_merge(parent::defaultArguments(), $arguments); } /** @@ -78,7 +75,7 @@ public function run(RequestInterface $request, ResponseInterface $response) $this->startLock(); - $climate = $this->climate(); + $this->climate(); $frequency = $this->frequency(); @@ -101,6 +98,7 @@ public function run(RequestInterface $request, ResponseInterface $response) * @param Container $container Pimple DI container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -141,7 +139,7 @@ abstract protected function emailData(Notification $notification, array $objects * @param string $frequency The frequency type to load. * @return Charcoal\Model\CollectionInterface */ - private function loadNotifications($frequency) + private function loadNotifications($frequency): \ArrayAccess|array { $loader = new CollectionLoader([ 'logger' => $this->logger, @@ -152,35 +150,33 @@ private function loadNotifications($frequency) 'property' => 'frequency', 'val' => $frequency ]); - $notifications = $loader->load(); - return $notifications; + return $loader->load(); } /** * Handle a notification request * * @param Notification $notification The notification object to handle. - * @return void */ - private function handleNotification(Notification $notification) + private function handleNotification(Notification $notification): void { - if (empty($notification->targetTypes())) { + if (in_array($notification->targetTypes(), [null, []], true)) { return; } $objectsByTypes = []; $numTotal = 0; foreach ($notification->targetTypes() as $objType) { - $objType = trim($objType); + $objType = trim((string) $objType); $objects = $this->updatedObjects($objType); $num = count($objects); - if ($num == 0) { + if ($num === 0) { continue; } $obj = []; $obj['objects'] = $objects; $obj['num'] = $num; $obj['type'] = $objType; - $obj['typeLabel'] = isset($objects[0]['targetTypeLabel']) ? $objects[0]['targetTypeLabel'] : $objType; + $obj['typeLabel'] = $objects[0]['targetTypeLabel'] ?? $objType; $objectsByTypes[$objType] = $obj; $numTotal += $num; @@ -192,11 +188,10 @@ private function handleNotification(Notification $notification) * @param Notification $notification The notification object. * @param array $objects The objects that were modified. * @param integer $numTotal Total number of modified objects. - * @return void */ - private function sendEmail(Notification $notification, array $objects, $numTotal) + private function sendEmail(Notification $notification, array $objects, int $numTotal): void { - if ($numTotal == 0) { + if ($numTotal === 0) { return; } @@ -237,7 +232,7 @@ private function sendEmail(Notification $notification, array $objects, $numTotal * @param string $objType The object (target) type to process. * @return CollectionInterface */ - private function updatedObjects($objType) + private function updatedObjects(string $objType): \ArrayAccess|array { $loader = new CollectionLoader([ 'logger' => $this->logger, @@ -266,7 +261,7 @@ private function updatedObjects($objType) $userFactory = $this->userFactory; $baseUrl = $this->baseUrl(); - $loader->setCallback(function (&$obj) use ($objFactory, $userFactory, $baseUrl) { + $loader->setCallback(function (array &$obj) use ($objFactory, $userFactory, $baseUrl): void { $diff = $obj->dataDiff(); $obj->updatedProperties = isset($diff[0]) ? array_keys($diff[0]) : []; $obj->dateStr = $obj['rev_ts']->format('Y-m-d H:i:s'); @@ -296,9 +291,8 @@ private function updatedObjects($objType) /** * @param FactoryInterface $factory The factory used to create queue items. - * @return void */ - private function setNotificationFactory(FactoryInterface $factory) + private function setNotificationFactory(FactoryInterface $factory): void { $this->notificationFactory = $factory; } @@ -306,16 +300,15 @@ private function setNotificationFactory(FactoryInterface $factory) /** * @return FactoryInterface */ - private function notificationFactory() + private function notificationFactory(): ?\Charcoal\Factory\FactoryInterface { return $this->notificationFactory; } /** * @param FactoryInterface $factory The factory used to create queue items. - * @return void */ - private function setRevisionFactory(FactoryInterface $factory) + private function setRevisionFactory(FactoryInterface $factory): void { $this->revisionFactory = $factory; } diff --git a/packages/admin/src/Charcoal/Admin/Script/Notification/ProcessDailyScript.php b/packages/admin/src/Charcoal/Admin/Script/Notification/ProcessDailyScript.php index ad5987bcc..495887fae 100644 --- a/packages/admin/src/Charcoal/Admin/Script/Notification/ProcessDailyScript.php +++ b/packages/admin/src/Charcoal/Admin/Script/Notification/ProcessDailyScript.php @@ -1,5 +1,7 @@ setTime(0, 0, 0); @@ -35,9 +34,8 @@ protected function startDate() /** * Retrieve the "maximal" date that the revisions should have been made for this script. - * @return DateTime */ - protected function endDate() + protected function endDate(): \DateTime { $d = new DateTime('today'); $d->setTime(0, 0, 0); @@ -47,9 +45,8 @@ protected function endDate() /** * @param Notification $notification The notification object. * @param array $objects The objects that were modified. - * @return array */ - protected function emailData(Notification $notification, array $objects) + protected function emailData(Notification $notification, array $objects): array { unset($notification, $objects); diff --git a/packages/admin/src/Charcoal/Admin/Script/Notification/ProcessHourlyScript.php b/packages/admin/src/Charcoal/Admin/Script/Notification/ProcessHourlyScript.php index 97e6f4be6..17d7d1bfa 100644 --- a/packages/admin/src/Charcoal/Admin/Script/Notification/ProcessHourlyScript.php +++ b/packages/admin/src/Charcoal/Admin/Script/Notification/ProcessHourlyScript.php @@ -14,19 +14,16 @@ class ProcessHourlyScript extends AbstractNotificationScript { /** * Get the frequency type of this script. - * - * @return string */ - protected function frequency() + protected function frequency(): string { return 'hourly'; } /** - * Retrieve the "minimal" date that the revisions should have been made for this script. - * @return DateTime - */ - protected function startDate() + * Retrieve the "minimal" date that the revisions should have been made for this script. + */ + protected function startDate(): \DateTime { $d = new DateTime('1 hour ago'); $d->setTime($d->format('H'), 0, 0); @@ -35,9 +32,8 @@ protected function startDate() /** * Retrieve the "maximal" date that the revisions should have been made for this script. - * @return DateTime */ - protected function endDate() + protected function endDate(): \DateTime { $d = new DateTime('now'); $d->setTime($d->format('H'), 0, 0); @@ -47,9 +43,8 @@ protected function endDate() /** * @param Notification $notification The notification object. * @param array $objects The objects that were modified. - * @return array */ - protected function emailData(Notification $notification, array $objects) + protected function emailData(Notification $notification, array $objects): array { unset($notification, $objects); diff --git a/packages/admin/src/Charcoal/Admin/Script/Notification/ProcessMinuteScript.php b/packages/admin/src/Charcoal/Admin/Script/Notification/ProcessMinuteScript.php index b250536ec..7063820ca 100644 --- a/packages/admin/src/Charcoal/Admin/Script/Notification/ProcessMinuteScript.php +++ b/packages/admin/src/Charcoal/Admin/Script/Notification/ProcessMinuteScript.php @@ -14,19 +14,16 @@ class ProcessMinuteScript extends AbstractNotificationScript { /** * Get the frequency type of this script. - * - * @return string */ - protected function frequency() + protected function frequency(): string { return 'minute'; } /** * Retrieve the "minimal" date that the revisions should have been made for this script. - * @return DateTime */ - protected function startDate() + protected function startDate(): \DateTime { $d = new DateTime('1 minute ago'); $d->setTime(0, 0, 0); @@ -35,20 +32,17 @@ protected function startDate() /** * Retrieve the "maximal" date that the revisions should have been made for this script. - * @return DateTime */ - protected function endDate() + protected function endDate(): \DateTime { - $d = new DateTime($this->starDate() . ' +1 minute'); - return $d; + return new DateTime($this->starDate() . ' +1 minute'); } /** * @param Notification $notification The notification object. * @param array $objects The objects that were modified. - * @return array */ - protected function emailData(Notification $notification, array $objects) + protected function emailData(Notification $notification, array $objects): array { unset($notification, $objects); diff --git a/packages/admin/src/Charcoal/Admin/Script/Notification/ProcessMonthlyScript.php b/packages/admin/src/Charcoal/Admin/Script/Notification/ProcessMonthlyScript.php index 12ceaf6bd..62c4ee275 100644 --- a/packages/admin/src/Charcoal/Admin/Script/Notification/ProcessMonthlyScript.php +++ b/packages/admin/src/Charcoal/Admin/Script/Notification/ProcessMonthlyScript.php @@ -1,5 +1,7 @@ setTime(0, 0, 0); @@ -35,9 +34,8 @@ protected function startDate() /** * Retrieve the "minimal" date that the revisions should have been made for this script. - * @return DateTime */ - protected function endDate() + protected function endDate(): \DateTime { $d = new DateTime('first day of this month'); $d->setTime(0, 0, 0); @@ -47,9 +45,8 @@ protected function endDate() /** * @param Notification $notification The notification object. * @param array $objects The objects that were modified. - * @return array */ - protected function emailData(Notification $notification, array $objects) + protected function emailData(Notification $notification, array $objects): array { unset($notification, $objects); diff --git a/packages/admin/src/Charcoal/Admin/Script/Notification/ProcessWeeklyScript.php b/packages/admin/src/Charcoal/Admin/Script/Notification/ProcessWeeklyScript.php index 47f95d6be..eafe5fa14 100644 --- a/packages/admin/src/Charcoal/Admin/Script/Notification/ProcessWeeklyScript.php +++ b/packages/admin/src/Charcoal/Admin/Script/Notification/ProcessWeeklyScript.php @@ -1,5 +1,7 @@ setTime(0, 0, 0); @@ -35,9 +34,8 @@ protected function startDate() /** * Retrieve the "maximal" date that the revisions should have been made for this script. - * @return DateTime */ - protected function endDate() + protected function endDate(): \DateTime { $d = new DateTime('last monday'); $d->setTime(0, 0, 0); @@ -47,9 +45,8 @@ protected function endDate() /** * @param Notification $notification The notification object. * @param array $objects The objects that were modified. - * @return array */ - protected function emailData(Notification $notification, array $objects) + protected function emailData(Notification $notification, array $objects): array { unset($notification, $objects); diff --git a/packages/admin/src/Charcoal/Admin/Script/Object/CreateScript.php b/packages/admin/src/Charcoal/Admin/Script/Object/CreateScript.php index 56e5a7a66..0a38cbc72 100644 --- a/packages/admin/src/Charcoal/Admin/Script/Object/CreateScript.php +++ b/packages/admin/src/Charcoal/Admin/Script/Object/CreateScript.php @@ -13,10 +13,8 @@ */ class CreateScript extends AdminScript { - /** - * @return array - */ - public function defaultArguments() + #[\Override] + public function defaultArguments(): array { $arguments = [ 'obj-type' => [ @@ -30,17 +28,14 @@ public function defaultArguments() 'defaultValue' => '' ] ]; - - $arguments = array_merge(parent::defaultArguments(), $arguments); - return $arguments; + return array_merge(parent::defaultArguments(), $arguments); } /** * @param RequestInterface $request A PSR-7 compatible Request instance. * @param ResponseInterface $response A PSR-7 compatible Response instance. - * @return ResponseInterface */ - public function run(RequestInterface $request, ResponseInterface $response) + public function run(RequestInterface $request, ResponseInterface $response): ResponseInterface { unset($request); diff --git a/packages/admin/src/Charcoal/Admin/Script/Object/ProcessSchedulesScript.php b/packages/admin/src/Charcoal/Admin/Script/Object/ProcessSchedulesScript.php index e2740c42b..3402f9447 100644 --- a/packages/admin/src/Charcoal/Admin/Script/Object/ProcessSchedulesScript.php +++ b/packages/admin/src/Charcoal/Admin/Script/Object/ProcessSchedulesScript.php @@ -26,15 +26,10 @@ class ProcessSchedulesScript extends AdminScript implements CronScriptInterface { use CronScriptTrait; - /** - * @var FactoryInterface $scheduleFactory - */ - private $scheduleFactory; + private ?\Charcoal\Factory\FactoryInterface $scheduleFactory = null; - /** - * @return array - */ - public function defaultArguments() + #[\Override] + public function defaultArguments(): array { $arguments = [ 'obj-type' => [ @@ -49,17 +44,14 @@ public function defaultArguments() 'defaultValue' => '' ] ]; - - $arguments = array_merge(parent::defaultArguments(), $arguments); - return $arguments; + return array_merge(parent::defaultArguments(), $arguments); } /** * @param RequestInterface $request A PSR-7 compatible Request instance. * @param ResponseInterface $response A PSR-7 compatible Response instance. - * @return ResponseInterface */ - public function run(RequestInterface $request, ResponseInterface $response) + public function run(RequestInterface $request, ResponseInterface $response): ResponseInterface { unset($request); @@ -72,17 +64,17 @@ public function run(RequestInterface $request, ResponseInterface $response) $scheduled = $this->loadSchedules($objType, $objId); - $callback = function ($obj) use ($climate) { + $callback = function ($obj): void { // No default callback }; - $successCallback = function ($obj) use ($climate) { + $successCallback = function ($obj) use ($climate): void { $climate->green()->out( sprintf('Object %s : %s schedule was successfully ran.', $obj->targetType(), $obj->targetId()) ); }; - $failureCallback = function ($obj) use ($climate) { + $failureCallback = function ($obj) use ($climate): void { $climate->red()->out( sprintf('Object %s : %s schedule could not be ran.', $obj->targetType(), $obj->targetId()) ); @@ -102,6 +94,7 @@ public function run(RequestInterface $request, ResponseInterface $response) * @param Container $container Pimple DI container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -112,16 +105,15 @@ protected function setDependencies(Container $container) /** * @return FactoryInterface */ - protected function scheduleFactory() + protected function scheduleFactory(): ?\Charcoal\Factory\FactoryInterface { return $this->scheduleFactory; } /** * @param FactoryInterface $factory The factory used to create queue items. - * @return void */ - private function setScheduleFactory(FactoryInterface $factory) + private function setScheduleFactory(FactoryInterface $factory): void { $this->scheduleFactory = $factory; } @@ -139,7 +131,7 @@ private function scheduleProto() * @param string $objId Optional object id to loader. * @return \Charcoal\Model\Collection|array */ - private function loadSchedules($objType = null, $objId = null) + private function loadSchedules($objType = null, $objId = null): \ArrayAccess|array { $loader = new CollectionLoader([ 'logger' => $this->logger, @@ -173,7 +165,6 @@ private function loadSchedules($objType = null, $objId = null) 'property' => 'scheduled_date', 'mode' => 'asc' ]); - $schedules = $loader->load(); - return $schedules; + return $loader->load(); } } diff --git a/packages/admin/src/Charcoal/Admin/Script/Object/Table/AlterPrimaryKeyScript.php b/packages/admin/src/Charcoal/Admin/Script/Object/Table/AlterPrimaryKeyScript.php index e80af22d9..4baf2c194 100644 --- a/packages/admin/src/Charcoal/Admin/Script/Object/Table/AlterPrimaryKeyScript.php +++ b/packages/admin/src/Charcoal/Admin/Script/Object/Table/AlterPrimaryKeyScript.php @@ -78,6 +78,7 @@ class AlterPrimaryKeyScript extends AdminScript /** * @return void */ + #[\Override] protected function init() { parent::init(); @@ -93,9 +94,8 @@ protected function init() * * @param RequestInterface $request A PSR-7 compatible Request instance. * @param ResponseInterface $response A PSR-7 compatible Response instance. - * @return ResponseInterface */ - public function run(RequestInterface $request, ResponseInterface $response) + public function run(RequestInterface $request, ResponseInterface $response): ResponseInterface { unset($request); @@ -110,10 +110,8 @@ public function run(RequestInterface $request, ResponseInterface $response) /** * Execute the prime directive. - * - * @return self */ - public function start() + public function start(): static { $cli = $this->climate(); @@ -235,7 +233,7 @@ public function start() * @throws RuntimeException If the $oldKey does not exist. * @return IdProperty[] */ - protected function prepareProperties($oldKey, $newKey, &$oldProp = null, &$newProp = null) + protected function prepareProperties($oldKey, $newKey, &$oldProp = null, &$newProp = null): array { $model = $this->targetModel(); $source = $model->source(); @@ -282,11 +280,11 @@ protected function prepareProperties($oldKey, $newKey, &$oldProp = null, &$newPr ); } - if (preg_match('~\bINT\(?(?:$|\b)~i', $col['Type'])) { + if (preg_match('~\bINT\(?(?:$|\b)~i', (string) $col['Type'])) { $oldProp->setMode(IdProperty::MODE_AUTO_INCREMENT); - } elseif (preg_match('~(?:^|\b)(?:VAR)?CHAR\(13\)(?:$|\b)~i', $col['Type'])) { + } elseif (preg_match('~(?:^|\b)(?:VAR)?CHAR\(13\)(?:$|\b)~i', (string) $col['Type'])) { $oldProp->setMode(IdProperty::MODE_UNIQID); - } elseif (preg_match('~(?:^|\b)(?:VAR)?CHAR\(36\)(?:$|\b)~i', $col['Type'])) { + } elseif (preg_match('~(?:^|\b)(?:VAR)?CHAR\(36\)(?:$|\b)~i', (string) $col['Type'])) { $oldProp->setMode(IdProperty::MODE_UUID); } else { $oldProp->setMode(IdProperty::MODE_CUSTOM); @@ -299,7 +297,7 @@ protected function prepareProperties($oldKey, $newKey, &$oldProp = null, &$newPr throw new RuntimeException( sprintf( 'The model [%1$s] does not have the target field [%2$s]', - get_class($model), + $model::class, $oldKey ) ); @@ -316,41 +314,30 @@ protected function prepareProperties($oldKey, $newKey, &$oldProp = null, &$newPr * * @param string|IdProperty $mode The mode or property to resolve. * @throws UnexpectedValueException If the ID mode is invalid. - * @return string */ - protected function labelFromMode($mode) + protected function labelFromMode($mode): string { if ($mode instanceof IdProperty) { $mode = $mode->getMode(); } - - switch ($mode) { - case IdProperty::MODE_AUTO_INCREMENT: - return 'auto-increment'; - - case IdProperty::MODE_UNIQID: - return 'uniqid()'; - - case IdProperty::MODE_UUID: - return 'RFC-4122 UUID'; - - case IdProperty::MODE_CUSTOM: - return 'custom'; - } - - throw new UnexpectedValueException(sprintf( - 'The ID mode was not recognized: %s', - is_object($mode) ? get_class($mode) : gettype($mode) - )); + return match ($mode) { + IdProperty::MODE_AUTO_INCREMENT => 'auto-increment', + IdProperty::MODE_UNIQID => 'uniqid()', + IdProperty::MODE_UUID => 'RFC-4122 UUID', + IdProperty::MODE_CUSTOM => 'custom', + default => throw new UnexpectedValueException(sprintf( + 'The ID mode was not recognized: %s', + get_debug_type($mode) + )), + }; } /** * Retrieve a label for the property. * * @param IdProperty $prop The new ID property to analyse. - * @return string|null */ - protected function labelFromProp(IdProperty $prop) + protected function labelFromProp(IdProperty $prop): ?string { $mode = $prop->getMode(); switch ($mode) { @@ -362,7 +349,7 @@ protected function labelFromProp(IdProperty $prop) default: $label = $this->labelFromMode($mode); - if ($label) { + if ($label !== '' && $label !== '0') { return sprintf('auto-generated ID (%s)', $label); } else { return 'auto-generated ID'; @@ -377,11 +364,10 @@ protected function labelFromProp(IdProperty $prop) * * @param IdProperty $newProp The new ID property to analyse. * @param IdProperty $oldProp The previous ID property to analyse. - * @return self */ - protected function describeConversion(IdProperty $newProp, IdProperty $oldProp = null) + protected function describeConversion(IdProperty $newProp, ?IdProperty $oldProp = null): static { - if ($oldProp) { + if ($oldProp instanceof \Charcoal\Property\IdProperty) { $new = $this->labelFromProp($newProp); $old = $this->labelFromProp($oldProp); $desc = sprintf('Converting to %s from %s.', $new, $old); @@ -401,7 +387,7 @@ protected function describeConversion(IdProperty $newProp, IdProperty $oldProp = * @param IdProperty $prop The property to retrieve the field from. * @return PropertyField */ - protected function propertyField(IdProperty $prop) + protected function propertyField(IdProperty $prop): \Charcoal\Property\PropertyField|false { $fields = $prop->fields(); @@ -447,12 +433,7 @@ private function isPrimaryKeyDifferent() private function oldPrimaryKey() { if ($this->oldPrimaryKey === null) { - if ($this->isPrimaryKeyDifferent()) { - $oldKey = $this->climate()->arguments->get('old_key'); - } else { - $oldKey = $this->targetModel()->key(); - } - + $oldKey = $this->isPrimaryKeyDifferent() ? $this->climate()->arguments->get('old_key') : $this->targetModel()->key(); $this->oldPrimaryKey = $oldKey; } @@ -469,11 +450,7 @@ private function newPrimaryKey() if ($this->newPrimaryKey === null) { $model = $this->targetModel(); - if ($this->isPrimaryKeyDifferent()) { - $newKey = $model->key(); - } else { - $newKey = sprintf('%s_new', $model->key()); - } + $newKey = $this->isPrimaryKeyDifferent() ? $model->key() : sprintf('%s_new', $model->key()); $this->newPrimaryKey = $newKey; } @@ -486,9 +463,8 @@ private function newPrimaryKey() * * @param array|Traversable $rows The target model's existing rows. * @throws InvalidArgumentException If the given argument is not iterable. - * @return boolean */ - private function describeCount($rows = null) + private function describeCount($rows = null): bool { if ($rows === null) { $rows = $this->fetchTargetRows(); @@ -498,7 +474,7 @@ private function describeCount($rows = null) throw new InvalidArgumentException( sprintf( 'The rows must be iterable; received %s', - is_object($rows) ? get_class($rows) : gettype($rows) + get_debug_type($rows) ) ); } @@ -506,7 +482,7 @@ private function describeCount($rows = null) $cli = $this->climate(); $model = $this->targetModel(); - if (is_array($rows) || $rows instanceof Countable) { + if (is_countable($rows)) { $count = count($rows); } elseif ($rows instanceof PDOStatement) { $count = $rows->rowCount(); @@ -525,12 +501,10 @@ private function describeCount($rows = null) if (!$this->quiet()) { $cli->comment('The object table has 1 row.'); } - } else { - if (!$this->quiet()) { - $cli->comment( - sprintf('The object table has %s rows.', $count) - ); - } + } elseif (!$this->quiet()) { + $cli->comment( + sprintf('The object table has %s rows.', $count) + ); } return true; @@ -541,9 +515,8 @@ private function describeCount($rows = null) * * @param PropertyField $field The new ID field. * @param IdProperty $prop The new ID property. - * @return self */ - private function insertNewField(PropertyField $field, IdProperty $prop) + private function insertNewField(PropertyField $field, IdProperty $prop): static { unset($prop); @@ -588,9 +561,8 @@ private function insertNewField(PropertyField $field, IdProperty $prop) * * @param PropertyField $field The previous ID field. * @param IdProperty $prop The previous ID property. - * @return self */ - private function dropPrimaryKey(PropertyField $field, IdProperty $prop) + private function dropPrimaryKey(PropertyField $field, IdProperty $prop): static { $keepId = $this->climate()->arguments->defined('keep_id'); $model = $this->targetModel(); @@ -628,9 +600,8 @@ private function dropPrimaryKey(PropertyField $field, IdProperty $prop) * * @param PropertyField $field The new ID field. * @param IdProperty $prop The new ID property. - * @return self */ - private function applyPrimaryKey(PropertyField $field, IdProperty $prop) + private function applyPrimaryKey(PropertyField $field, IdProperty $prop): static { unset($prop); @@ -655,9 +626,8 @@ private function applyPrimaryKey(PropertyField $field, IdProperty $prop) * @param PropertyField $field The field to rename. * @param string $from The original field key. * @param string $to The new field key. - * @return self */ - private function renameColumn(PropertyField $field, $from, $to) + private function renameColumn(PropertyField $field, $from, $to): static { $model = $this->targetModel(); $source = $model->source(); @@ -681,9 +651,8 @@ private function renameColumn(PropertyField $field, $from, $to) * Remove the given field. * * @param PropertyField $field The field to remove. - * @return self */ - private function removeColumn(PropertyField $field) + private function removeColumn(PropertyField $field): static { $source = $this->targetModel()->source(); @@ -707,21 +676,20 @@ private function removeColumn(PropertyField $field) * @param IdProperty $oldProp The previous ID property. * @param PropertyField $oldField The previous ID field. * @throws InvalidArgumentException If the new property does not implement the proper mode. - * @return self */ protected function convertIdField( IdProperty $newProp, PropertyField $newField, IdProperty $oldProp, PropertyField $oldField - ) { + ): static { $cli = $this->climate(); $keepId = $cli->arguments->defined('keep_id'); $model = $this->targetModel(); $source = $model->source(); $table = $source->table(); - $dbh = $source->db(); + $source->db(); $newKey = $newProp->getIdent(); $oldKey = $oldProp->getIdent(); @@ -739,7 +707,7 @@ protected function convertIdField( switch ($mode) { case IdProperty::MODE_AUTO_INCREMENT: $pool = 0; - $ids = function () use (&$pool) { + $ids = function () use (&$pool): int { return ++$pool; }; break; @@ -750,7 +718,7 @@ protected function convertIdField( $generator = $this->idGenerator(); $pool = []; - $ids = function () use (&$pool, $model, $generator) { + $ids = function () use (&$pool, $generator) { $id = $generator(); while (in_array($id, $pool)) { $id = $generator(); @@ -764,7 +732,7 @@ protected function convertIdField( default: $pool = []; - $ids = function () use (&$pool, $newProp) { + $ids = function () use (&$pool, $newProp): ?string { $id = $newProp->autoGenerate(); while (in_array($id, $pool)) { $id = $newProp->autoGenerate(); @@ -830,14 +798,13 @@ protected function convertIdField( * @param IdProperty $oldProp The previous ID property. * @param PropertyField $oldField The previous ID field. * @throws InvalidArgumentException If the new property does not implement the proper mode. - * @return self */ protected function syncRelatedFields( IdProperty $newProp, PropertyField $newField, IdProperty $oldProp, PropertyField $oldField - ) { + ): static { unset($newProp, $oldProp, $oldField); $cli = $this->climate(); @@ -907,21 +874,18 @@ protected function syncRelatedFields( * * @return array */ + #[\Override] public function defaultArguments() { static $arguments; if ($arguments === null) { - $validateFieldName = function ($response) { - return is_string($response) && strlen($response) > 0; - }; + $validateFieldName = (fn($response): bool => is_string($response) && $response !== ''); - $validateCallback = function ($response) { - return is_string($response) && (strpos($callable, '::') > 1 || function_exists($response)); - }; + $validateCallback = (fn($response): bool => is_string($response) && (strpos($callable, '::') > 1 || function_exists($response))); - $validateModel = function ($response) { - if (strlen($response) === 0) { + $validateModel = function ($response): bool { + if ((string) $response === '') { return false; } @@ -936,8 +900,8 @@ public function defaultArguments() return true; }; - $validateModels = function ($response) { - if (strlen($response) === 0) { + $validateModels = function ($response): bool { + if ((string) $response === '') { return false; } @@ -1018,9 +982,8 @@ public function parentArguments() * * @param mixed $callable A function or method. * @throws InvalidArgumentException If the given argument is not a callable function. - * @return self */ - public function setIdGenerator($callable) + public function setIdGenerator($callable): static { $this->idGenerator = $this->parseIdGenerator($callable); @@ -1047,16 +1010,16 @@ public function parseIdGenerator($callable) $bail = false; if (is_array($callable) && count($callable) === 2) { - list($class, $func) = $callable; + [$class, $func] = $callable; $isMethod = ($class && $func); } elseif (is_string($callable) && strpos($callable, '::') > 1) { - list($class, $func) = explode('::', $callable); + [$class, $func] = explode('::', $callable); $isMethod = ($class && $func); } if ($isMethod) { $model = $this->targetModel(); - $isModel = is_a($model, $class); + $isModel = $model instanceof $class; $method = new ReflectionMethod($class, $func); if ($isModel && $method->isPublic()) { @@ -1073,7 +1036,7 @@ public function parseIdGenerator($callable) sprintf( 'The ID generator must be callable, received: %s', is_object($callable) - ? get_class($callable) + ? $callable::class : (is_string($callable) ? $callable : gettype($callable) @@ -1093,7 +1056,7 @@ public function parseIdGenerator($callable) */ public function idGenerator() { - if (!isset($this->idGenerator)) { + if ($this->idGenerator === null) { throw new RuntimeException('A function to generate a unique ID must be provided.'); } @@ -1105,9 +1068,8 @@ public function idGenerator() * * @param string|ModelInterface $model An object model. * @throws InvalidArgumentException If the given argument is not a model. - * @return self */ - public function setTargetModel($model) + public function setTargetModel($model): static { if (is_string($model)) { $model = $this->modelFactory()->get($model); @@ -1135,7 +1097,7 @@ public function setTargetModel($model) */ public function targetModel() { - if (!isset($this->targetModel)) { + if ($this->targetModel === null) { throw new RuntimeException('A model must be targeted.'); } @@ -1147,14 +1109,13 @@ public function targetModel() * * @param string|array $models One or more object models. * @throws InvalidArgumentException If the given argument is not a model. - * @return self */ - public function setRelatedModels($models) + public function setRelatedModels($models): static { $models = $this->parseAsArray($models); foreach ($models as $i => $model) { if (is_string($model)) { - list($model, $prop) = $this->resolveRelatedModel($model); + [$model, $prop] = $this->resolveRelatedModel($model); $models[$i] = $model; $this->relatedProperties[$model->objType()] = $prop; } elseif ($model instanceof ModelInterface) { @@ -1162,7 +1123,7 @@ public function setRelatedModels($models) throw new InvalidArgumentException( sprintf( 'The related model [%s] requires a target property', - get_class($model) + $model::class ) ); } @@ -1189,16 +1150,16 @@ public function setRelatedModels($models) * @throws InvalidArgumentException If the identifier is invalid. * @return array Returns an array containing a ModelInterface and a property identifier. */ - protected function resolveRelatedModel($pattern) + protected function resolveRelatedModel($pattern): array { - list($class, $prop) = array_pad($this->parseAsArray($pattern, ':'), 2, null); + [$class, $prop] = array_pad($this->parseAsArray($pattern, ':'), 2, null); $model = $this->modelFactory()->get($class); if (!$prop) { throw new InvalidArgumentException( sprintf( 'The related model [%s] requires a target property', - get_class($model) + $model::class ) ); } diff --git a/packages/admin/src/Charcoal/Admin/Script/Object/Table/AlterScript.php b/packages/admin/src/Charcoal/Admin/Script/Object/Table/AlterScript.php index 37df8a738..e510413b6 100644 --- a/packages/admin/src/Charcoal/Admin/Script/Object/Table/AlterScript.php +++ b/packages/admin/src/Charcoal/Admin/Script/Object/Table/AlterScript.php @@ -13,10 +13,8 @@ */ class AlterScript extends AdminScript { - /** - * @return array - */ - public function defaultArguments() + #[\Override] + public function defaultArguments(): array { $arguments = [ 'obj-type' => [ @@ -25,17 +23,14 @@ public function defaultArguments() 'defaultValue' => '' ] ]; - - $arguments = array_merge(parent::defaultArguments(), $arguments); - return $arguments; + return array_merge(parent::defaultArguments(), $arguments); } /** * @param RequestInterface $request A PSR-7 compatible Request instance. * @param ResponseInterface $response A PSR-7 compatible Response instance. - * @return ResponseInterface */ - public function run(RequestInterface $request, ResponseInterface $response) + public function run(RequestInterface $request, ResponseInterface $response): ResponseInterface { unset($request); @@ -86,7 +81,7 @@ public function run(RequestInterface $request, ResponseInterface $response) return $response; } - $ret = $source->alterTable(); + $source->alterTable(); $climate->green()->out( "\n" . 'Success!' diff --git a/packages/admin/src/Charcoal/Admin/Script/Object/Table/CreateScript.php b/packages/admin/src/Charcoal/Admin/Script/Object/Table/CreateScript.php index 0cc9ecb37..b06cf7726 100644 --- a/packages/admin/src/Charcoal/Admin/Script/Object/Table/CreateScript.php +++ b/packages/admin/src/Charcoal/Admin/Script/Object/Table/CreateScript.php @@ -13,10 +13,8 @@ */ class CreateScript extends AdminScript { - /** - * @return array - */ - public function defaultArguments() + #[\Override] + public function defaultArguments(): array { $arguments = [ 'obj-type' => [ @@ -25,17 +23,14 @@ public function defaultArguments() 'defaultValue' => '' ] ]; - - $arguments = array_merge(parent::defaultArguments(), $arguments); - return $arguments; + return array_merge(parent::defaultArguments(), $arguments); } /** * @param RequestInterface $request A PSR-7 compatible Request instance. * @param ResponseInterface $response A PSR-7 compatible Response instance. - * @return ResponseInterface */ - public function run(RequestInterface $request, ResponseInterface $response) + public function run(RequestInterface $request, ResponseInterface $response): ResponseInterface { unset($request); @@ -86,7 +81,7 @@ public function run(RequestInterface $request, ResponseInterface $response) return $response; } - $ret = $source->createTable(); + $source->createTable(); $climate->green()->out( "\n" . 'Success!' diff --git a/packages/admin/src/Charcoal/Admin/Script/ObjectsScript.php b/packages/admin/src/Charcoal/Admin/Script/ObjectsScript.php index 4591678ae..5b1d606d3 100644 --- a/packages/admin/src/Charcoal/Admin/Script/ObjectsScript.php +++ b/packages/admin/src/Charcoal/Admin/Script/ObjectsScript.php @@ -28,10 +28,8 @@ class ObjectsScript extends AdminScript implements CollectionContainerInterface { use CollectionContainerTrait; - /** - * @return array - */ - public function defaultArguments() + #[\Override] + public function defaultArguments(): array { $arguments = [ 'obj-type' => [ @@ -54,17 +52,14 @@ public function defaultArguments() 'castTo' => 'int' ] ]; - - $arguments = array_merge(parent::defaultArguments(), $arguments); - return $arguments; + return array_merge(parent::defaultArguments(), $arguments); } /** * @param RequestInterface $request A PSR-7 compatible Request instance. * @param ResponseInterface $response A PSR-7 compatible Response instance. - * @return ResponseInterface */ - public function run(RequestInterface $request, ResponseInterface $response) + public function run(RequestInterface $request, ResponseInterface $response): ResponseInterface { unset($request); @@ -94,7 +89,7 @@ public function run(RequestInterface $request, ResponseInterface $response) $collection = $this->collection(); $table = []; - $rows = $this->objectRows(); + $this->objectRows(); foreach ($collection as $c) { $obj = []; diff --git a/packages/admin/src/Charcoal/Admin/Script/Tools/CheckLinksScript.php b/packages/admin/src/Charcoal/Admin/Script/Tools/CheckLinksScript.php index f86325828..e9b05c170 100644 --- a/packages/admin/src/Charcoal/Admin/Script/Tools/CheckLinksScript.php +++ b/packages/admin/src/Charcoal/Admin/Script/Tools/CheckLinksScript.php @@ -16,10 +16,7 @@ */ class CheckLinksScript extends AdminScript { - /** - * @var string - */ - private $startUrl; + private ?string $startUrl = null; /** * @var array @@ -36,10 +33,7 @@ class CheckLinksScript extends AdminScript */ private $processedUrls = []; - /** - * @var GuzzleClient - */ - private $guzzleClient; + private readonly \GuzzleHttp\Client $guzzleClient; /** * @var GoutteClient @@ -58,10 +52,8 @@ public function __construct($data = null) $this->goutteClient->setClient($this->guzzleClient); } - /** - * @return array - */ - public function defaultArguments() + #[\Override] + public function defaultArguments(): array { $arguments = [ 'url' => [ @@ -81,17 +73,14 @@ public function defaultArguments() 'defaultValue' => true ] ]; - - $arguments = array_merge(parent::defaultArguments(), $arguments); - return $arguments; + return array_merge(parent::defaultArguments(), $arguments); } /** * @param RequestInterface $request PSR-7 request. * @param ResponseInterface $response PSR-7 response. - * @return ResponseInterface */ - public function run(RequestInterface $request, ResponseInterface $response) + public function run(RequestInterface $request, ResponseInterface $response): ResponseInterface { unset($request); @@ -114,9 +103,8 @@ public function run(RequestInterface $request, ResponseInterface $response) /** * @param string $url The URL to check. - * @return void */ - private function checkUrl($url) + private function checkUrl($url): void { $rawUrl = $url; $this->processedUrls[] = $url; @@ -132,7 +120,7 @@ private function checkUrl($url) try { $response = $this->guzzleClient->request('GET', $url, [ 'http_errors' => false, - 'on_stats' => function (TransferStats $stats) { + 'on_stats' => function (TransferStats $stats): void { if ($stats->hasResponse()) { $code = $stats->getResponse()->getStatusCode(); $transferTime = (1000 * $stats->getTransferTime()); @@ -162,7 +150,7 @@ private function checkUrl($url) } } ]); - } catch (Exception $e) { + } catch (Exception) { // Do nothing $this->climate()->error('-- Error retrieving ' . $url); } @@ -174,13 +162,12 @@ private function checkUrl($url) /** * @param string $url The URL to retrieve links from. * @param integer $level The current level. - * @return void */ - private function retrieveLinks($url, $level) + private function retrieveLinks($url, int|float $level): void { $crawler = $this->goutteClient->request('GET', $url); - $crawler->filter('a')->each(function ($item) use ($level) { + $crawler->filter('a')->each(function ($item) use ($level): void { $href = $item->attr('href'); if (in_array($href, $this->processedUrls)) { return; @@ -202,19 +189,15 @@ private function isInternalLink($url) if (!isset($parsed['host'])) { return true; } - if ($parsed['host'] === $this->parsedStartUrl['host']) { - return true; - } - return false; + return $parsed['host'] === $this->parsedStartUrl['host']; } /** * @param string $url The URL to convert to absolute. - * @return string */ - private function absoluteLink($url) + private function absoluteLink($url): string { - if (strstr($url, 'http') === false) { + if (!str_contains($url, 'http')) { return $this->startUrl . ltrim($url, '/'); } else { return $url; @@ -223,16 +206,10 @@ private function absoluteLink($url) /** * @param string $url The URL to valdate. - * @return boolean */ - private function validateLink($url) + private function validateLink($url): bool { $parsed = parse_url($url); - if (isset($parsed['scheme'])) { - if (!in_array($parsed['scheme'], ['http', 'https'])) { - return false; - } - } - return true; + return !(isset($parsed['scheme']) && !in_array($parsed['scheme'], ['http', 'https'])); } } diff --git a/packages/admin/src/Charcoal/Admin/Script/Tools/CopyAssetsScript.php b/packages/admin/src/Charcoal/Admin/Script/Tools/CopyAssetsScript.php index 3b55aaa07..e8b3fca24 100644 --- a/packages/admin/src/Charcoal/Admin/Script/Tools/CopyAssetsScript.php +++ b/packages/admin/src/Charcoal/Admin/Script/Tools/CopyAssetsScript.php @@ -15,15 +15,14 @@ */ class CopyAssetsScript extends AdminScript { + public $basePath; /** * @var string */ private $dir; - /** - * @return array - */ - public function defaultArguments() + #[\Override] + public function defaultArguments(): array { $arguments = [ 'dir' => [ @@ -32,17 +31,14 @@ public function defaultArguments() 'defaultValue' => 'www/assets/admin/' ] ]; - - $arguments = array_merge(parent::defaultArguments(), $arguments); - return $arguments; + return array_merge(parent::defaultArguments(), $arguments); } /** * @param RequestInterface $request PSR-7 request. * @param ResponseInterface $response PSR-7 response. - * @return ResponseInterface */ - public function run(RequestInterface $request, ResponseInterface $response) + public function run(RequestInterface $request, ResponseInterface $response): ResponseInterface { unset($request); @@ -91,6 +87,7 @@ public function run(RequestInterface $request, ResponseInterface $response) * @param Container $container Pimple DI Container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -109,7 +106,7 @@ protected function setDependencies(Container $container) * @param integer $permissions New folder creation permissions. * @return boolean Returns true on success, false on failure. */ - private function copy($source, $dest, $permissions = 0755) + private function copy(string $source, string $dest, $permissions = 0755) { // Check for symlinks if (is_link($source)) { @@ -130,7 +127,7 @@ private function copy($source, $dest, $permissions = 0755) $dir = dir($source); while (false !== $entry = $dir->read()) { // Skip pointers - if ($entry == '.' || $entry == '..') { + if ($entry === '.' || $entry === '..') { continue; } diff --git a/packages/admin/src/Charcoal/Admin/Script/Tools/OptimizeImagesScript.php b/packages/admin/src/Charcoal/Admin/Script/Tools/OptimizeImagesScript.php index c7147c730..8f1570250 100644 --- a/packages/admin/src/Charcoal/Admin/Script/Tools/OptimizeImagesScript.php +++ b/packages/admin/src/Charcoal/Admin/Script/Tools/OptimizeImagesScript.php @@ -32,10 +32,8 @@ class OptimizeImagesScript extends AdminScript */ private $dir; - /** - * @return array - */ - public function defaultArguments() + #[\Override] + public function defaultArguments(): array { $arguments = [ 'jpg' => [ @@ -54,17 +52,14 @@ public function defaultArguments() 'defaultValue' => 'www/uploads/' ] ]; - - $arguments = array_merge(parent::defaultArguments(), $arguments); - return $arguments; + return array_merge(parent::defaultArguments(), $arguments); } /** * @param RequestInterface $request PSR-7 request. * @param ResponseInterface $response PSR-7 response. - * @return ResponseInterface */ - public function run(RequestInterface $request, ResponseInterface $response) + public function run(RequestInterface $request, ResponseInterface $response): ResponseInterface { unset($request); @@ -117,6 +112,7 @@ public function run(RequestInterface $request, ResponseInterface $response) * @param Container $container Pimple DI Container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -142,9 +138,8 @@ private function getOptipngCmd() /** * @param string $cmd The jpegoptim command. - * @return void */ - private function runJpegoptim($cmd) + private function runJpegoptim($cmd): void { $cmdName = sprintf( 'cd %s && \ @@ -172,9 +167,8 @@ private function runJpegoptim($cmd) /** * @param string $cmd The jpegoptim command. - * @return void */ - private function runOptipng($cmd) + private function runOptipng($cmd): void { $cmdName = sprintf( 'cd %s && \ @@ -197,11 +191,11 @@ private function runOptipng($cmd) * @param string $cmdName The binary name to search. * @return string */ - private function findCmd($cmdName) + private function findCmd(string $cmdName) { $cmd = exec('type -p ' . $cmdName); $cmd = str_replace($cmdName . ' is ', '', $cmd); - if (!$cmd) { + if ($cmd === '' || $cmd === '0') { $cmd = exec('where ' . $cmdName); } if (!$cmd) { diff --git a/packages/admin/src/Charcoal/Admin/Script/Tools/ResizeImagesScript.php b/packages/admin/src/Charcoal/Admin/Script/Tools/ResizeImagesScript.php index 1f2ecf868..3c03935d6 100644 --- a/packages/admin/src/Charcoal/Admin/Script/Tools/ResizeImagesScript.php +++ b/packages/admin/src/Charcoal/Admin/Script/Tools/ResizeImagesScript.php @@ -32,10 +32,8 @@ class ResizeImagesScript extends AdminScript */ private $dir; - /** - * @return array - */ - public function defaultArguments() + #[\Override] + public function defaultArguments(): array { $arguments = [ 'width' => [ @@ -54,17 +52,14 @@ public function defaultArguments() 'defaultValue' => 'www/uploads/' ] ]; - - $arguments = array_merge(parent::defaultArguments(), $arguments); - return $arguments; + return array_merge(parent::defaultArguments(), $arguments); } /** * @param RequestInterface $request PSR-7 request. * @param ResponseInterface $response PSR-7 response. - * @return ResponseInterface */ - public function run(RequestInterface $request, ResponseInterface $response) + public function run(RequestInterface $request, ResponseInterface $response): ResponseInterface { unset($request); @@ -175,6 +170,7 @@ public function run(RequestInterface $request, ResponseInterface $response) * @param Container $container Pimple DI Container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -186,11 +182,11 @@ protected function setDependencies(Container $container) * @param string $cmdName The binary name to search. * @return string */ - private function findCmd($cmdName) + private function findCmd(string $cmdName) { $cmd = exec('type -p ' . $cmdName); $cmd = str_replace($cmdName . ' is ', '', $cmd); - if (!$cmd) { + if ($cmd === '' || $cmd === '0') { $cmd = exec('where ' . $cmdName); } if (!$cmd) { diff --git a/packages/admin/src/Charcoal/Admin/Script/Tools/StaticWebsite/CrawlScript.php b/packages/admin/src/Charcoal/Admin/Script/Tools/StaticWebsite/CrawlScript.php index 6a00bbfa3..09eecc502 100644 --- a/packages/admin/src/Charcoal/Admin/Script/Tools/StaticWebsite/CrawlScript.php +++ b/packages/admin/src/Charcoal/Admin/Script/Tools/StaticWebsite/CrawlScript.php @@ -20,25 +20,16 @@ */ class CrawlScript extends AdminScript { - /** - * @var string - */ - private $startUrl; + private ?string $startUrl = null; - /** - * @var array - */ - private $parsedStartUrl; + private array|bool|null $parsedStartUrl = null; /** * @var string */ private $basePath; - /** - * @var string - */ - private $outputDir; + private ?string $outputDir = null; /** * @var integer @@ -50,10 +41,7 @@ class CrawlScript extends AdminScript */ private $processedUrls = []; - /** - * @var GuzzleClient - */ - private $guzzleClient; + private readonly \GuzzleHttp\Client $guzzleClient; /** * @var GoutteClient @@ -72,10 +60,8 @@ public function __construct($data = null) $this->goutteClient->setClient($this->guzzleClient); } - /** - * @return array - */ - public function defaultArguments() + #[\Override] + public function defaultArguments(): array { $arguments = [ 'url' => [ @@ -94,17 +80,14 @@ public function defaultArguments() 'defaultValue' => 2 ] ]; - - $arguments = array_merge(parent::defaultArguments(), $arguments); - return $arguments; + return array_merge(parent::defaultArguments(), $arguments); } /** * @param RequestInterface $request PSR-7 Request. * @param ResponseInterface $response PSR-7 Response. - * @return ResponseInterface */ - public function run(RequestInterface $request, ResponseInterface $response) + public function run(RequestInterface $request, ResponseInterface $response): ResponseInterface { unset($request); @@ -132,6 +115,7 @@ public function run(RequestInterface $request, ResponseInterface $response) * @param Container $container Pimple DI Container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -141,9 +125,8 @@ protected function setDependencies(Container $container) /** * @param string $url The URL to cache. The base (start) URL will be prefixed to relative URLs. - * @return void */ - private function cacheUrl($url) + private function cacheUrl(string|array $url): void { if (in_array($url, $this->processedUrls)) { return; @@ -169,7 +152,7 @@ private function cacheUrl($url) return; } - if (strstr($headers['Content-Type'][0], 'text/html') !== false) { + if (str_contains($headers['Content-Type'][0], 'text/html')) { $outputFile = $outputDir . '/index.html'; $prefix = ''; } else { @@ -191,12 +174,11 @@ private function cacheUrl($url) /** * @param string $url The URL to retrieve links from. * @param integer $level Current level. - * @return void */ - private function retrieveLinks($url, $level) + private function retrieveLinks(string|null|array $url, int|float $level): void { $crawler = $this->goutteClient->request('GET', $url); - $crawler->filter('a')->each(function ($item) use ($level) { + $crawler->filter('a')->each(function ($item) use ($level): void { $href = $item->attr('href'); $parsedHref = parse_url($href); if (isset($parsedHref['host']) && ($parsedHref['host'] !== $this->parsedStartUrl['host'])) { @@ -217,9 +199,8 @@ private function retrieveLinks($url, $level) /** * @param string $dir The directory to recursively delete. * @throws InvalidArgumentException If the argument is empty. - * @return mixed */ - private function recursiveDelete($dir) + private function recursiveDelete(string $dir): bool { if (!is_string($dir)) { throw new InvalidArgumentException( diff --git a/packages/admin/src/Charcoal/Admin/Script/Tools/StaticWebsite/UpdateScript.php b/packages/admin/src/Charcoal/Admin/Script/Tools/StaticWebsite/UpdateScript.php index b7c2b46dd..be1a7a6e8 100644 --- a/packages/admin/src/Charcoal/Admin/Script/Tools/StaticWebsite/UpdateScript.php +++ b/packages/admin/src/Charcoal/Admin/Script/Tools/StaticWebsite/UpdateScript.php @@ -24,15 +24,10 @@ class UpdateScript extends AdminScript */ private $basePath; - /** - * @var \GuzzleHttp\Client - */ - private $guzzleClient; + private ?\GuzzleHttp\Client $guzzleClient = null; - /** - * @return array - */ - public function defaultArguments() + #[\Override] + public function defaultArguments(): array { $arguments = [ 'url' => [ @@ -46,17 +41,14 @@ public function defaultArguments() 'noValue' => true ], ]; - - $arguments = array_merge(parent::defaultArguments(), $arguments); - return $arguments; + return array_merge(parent::defaultArguments(), $arguments); } /** * @param RequestInterface $request PSR-7 Request. * @param ResponseInterface $response PSR-7 Response. - * @return ResponseInterface */ - public function run(RequestInterface $request, ResponseInterface $response) + public function run(RequestInterface $request, ResponseInterface $response): ResponseInterface { unset($request); $this->guzzleClient = new GuzzleClient(); @@ -96,6 +88,7 @@ public function run(RequestInterface $request, ResponseInterface $response) * @param Container $container Pimple DI Container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -106,9 +99,8 @@ protected function setDependencies(Container $container) /** * @param string $url The URL to cache. The base (start) URL will be prefixed to relative URLs. * @param string $outputDir The output directory. - * @return void */ - private function cacheUrl($url, $outputDir) + private function cacheUrl(string $url, string $outputDir): void { $relativeUrl = str_replace($this->baseUrl(), '', $url); $url = $this->baseUrl() . $relativeUrl; @@ -130,7 +122,7 @@ private function cacheUrl($url, $outputDir) return; } - if (strstr($headers['Content-Type'][0], 'text/html') !== false) { + if (str_contains($headers['Content-Type'][0], 'text/html')) { $outputFile = $outputDir . '/index.html'; $prefix = ''; } else { @@ -154,7 +146,7 @@ private function cacheUrl($url, $outputDir) * @param integer $flags Glob flags. * @return array */ - private function globRecursive($dir, $pattern, $flags = 0) + private function globRecursive(string $dir, string $pattern, $flags = 0): array|false { $files = glob($dir . '/' . $pattern, $flags); foreach (glob($dir . '/*', (GLOB_ONLYDIR | GLOB_NOSORT)) as $dir) { diff --git a/packages/admin/src/Charcoal/Admin/Script/Translation/TranslateScript.php b/packages/admin/src/Charcoal/Admin/Script/Translation/TranslateScript.php index 3d43c86fb..652b4d0ca 100644 --- a/packages/admin/src/Charcoal/Admin/Script/Translation/TranslateScript.php +++ b/packages/admin/src/Charcoal/Admin/Script/Translation/TranslateScript.php @@ -37,10 +37,9 @@ class TranslateScript extends AdminScript * Valid arguments: * - path : path/to/files * - type : mustache | php - * - * @return array */ - public function defaultArguments() + #[\Override] + public function defaultArguments(): array { $arguments = [ 'path' => [ @@ -59,17 +58,14 @@ public function defaultArguments() 'defaultValue' => '' ] ]; - - $arguments = array_merge(parent::defaultArguments(), $arguments); - return $arguments; + return array_merge(parent::defaultArguments(), $arguments); } /** * @param RequestInterface $request A PSR-7 compatible Request instance. * @param ResponseInterface $response A PSR-7 compatible Response instance. - * @return ResponseInterface */ - public function run(RequestInterface $request, ResponseInterface $response) + public function run(RequestInterface $request, ResponseInterface $response): ResponseInterface { // Unused unset($request); @@ -85,6 +81,7 @@ public function run(RequestInterface $request, ResponseInterface $response) switch ($type) { case 'mustache': + default: $regex = '/{{\s*#\s*_t\s*}}((.|\n|\r|\n\r)*?){{\s*\/\s*_t\s*}}/i'; $file = '*.mustache'; $index = 1; @@ -94,11 +91,6 @@ public function run(RequestInterface $request, ResponseInterface $response) $index = 'text'; $file = '*.php'; break; - default: - $regex = '/{{\s*#\s*_t\s*}}((.|\n|\r|\n\r)*?){{\s*\/\s*_t\s*}}/i'; - $file = '*.mustache'; - $index = 1; - break; } // Remove vendor/charcoal/app @@ -123,7 +115,7 @@ public function run(RequestInterface $request, ResponseInterface $response) } // Loop files to get original text. - foreach ($glob as $k => $f) { + foreach ($glob as $f) { $text = file_get_contents($f); if (preg_match($regex, $text)) { preg_match_all($regex, $text, $array); @@ -156,7 +148,7 @@ public function run(RequestInterface $request, ResponseInterface $response) * @return array * @see http://in.php.net/manual/en/function.glob.php#106595 */ - public function globRecursive($pattern, $flags = 0) + public function globRecursive($pattern, $flags = 0): array|false { $max = $this->maxRecursiveLevel(); $i = 1; @@ -174,9 +166,8 @@ public function globRecursive($pattern, $flags = 0) /** * BASE URL * Realpath - * @return string */ - public function base() + public function base(): string { return realpath($this->app()->config()->get('base_path') . DIRECTORY_SEPARATOR . '../../../') . '/'; } @@ -185,7 +176,7 @@ public function base() * ARGUMENTS * @return TranslateScript Chainable */ - public function getPath() + public function getPath(): static { $path = $this->argOrInput('path'); $this->path = $path; @@ -206,7 +197,7 @@ public function path() /** * @return TranslateScript Chainable */ - public function getFileType() + public function getFileType(): static { $type = $this->argOrInput('type'); $this->fileType = $type; @@ -240,10 +231,8 @@ public function file() /** * Returns associative array * 'original text' => [ 'translation' => 'translation text', 'context' => 'filename' ] - * - * @return array */ - public function fromCSV() + public function fromCSV(): array { $output = $this->file(); $base = $this->base(); @@ -255,10 +244,10 @@ public function fromCSV() $results = []; $row = 0; - while (($data = fgetcsv($file, 0, ',')) !== false) { + while (($data = fgetcsv($file, 0, ',', escape: '\\')) !== false) { $row++; // Skip column names - if ($row == 1) { + if ($row === 1) { continue; } /** @@ -267,7 +256,7 @@ public function fromCSV() * data[2] = CONTEXT */ $translation = $this->translateCSV($data); - if (!empty($translation)) { + if ($translation !== []) { $results[$translation[0]] = $translation[1]; } } @@ -279,7 +268,7 @@ public function fromCSV() * @param array $translations The translations to save in CSV. * @return TranslateScript Chainable */ - public function toCSV(array $translations) + public function toCSV(array $translations): static { $base = $this->base(); $output = $this->file(); @@ -298,11 +287,11 @@ public function toCSV(array $translations) // Wtf happened? return $this; } - fputcsv($file, $columns, $separator, $enclosure); + fputcsv($file, $columns, $separator, $enclosure, escape: '\\'); foreach ($translations as $orig => $translation) { $data = [ $orig, $translation['translation'], $translation['context'] ]; - fputcsv($file, $data, $separator, $enclosure); + fputcsv($file, $data, $separator, $enclosure, escape: '\\'); } fclose($file); @@ -312,34 +301,31 @@ public function toCSV(array $translations) /** * @param array $data The translation data. - * @return array * @todo multiple langs * data[0] = ORIGINAL * data[1] = TRANSLATION * data[2] = CONTEXT */ - public function translateCSV(array $data) + public function translateCSV(array $data): array { if (count($data) < 3) { return []; } - $output = [ + return [ $data[0], [ 'translation' => $data[1], 'context' => $data[2] ] ]; - - return $output; } /** * @todo make this optional * @return string lang ident */ - public function origLanguage() + public function origLanguage(): string { return 'fr'; } @@ -348,10 +334,11 @@ public function origLanguage() * Get opposite languages from DATABASE * * @return [type] [description] + * @return mixed[] */ - public function oppositeLanguages() + public function oppositeLanguages(): array { - $cfg = $this->app()->config(); + $this->app()->config(); $locales = $this->locales(); $languages = $locales['languages']; @@ -379,11 +366,11 @@ public function locales() } $cfg = $this->app()->config(); - $locales = isset($cfg['locales']) ? $cfg['locales'] : []; - $languages = isset($locales['languages']) ? $locales['languages'] : []; - $file = isset($locales['file']) ? $locales['file'] : $this->argOrInput('output'); + $locales = $cfg['locales'] ?? []; + $languages = $locales['languages'] ?? []; + $file = $locales['file'] ?? $this->argOrInput('output'); // Default to FR - $default = isset($locales['default_language']) ? $locales['default_language'] : 'fr'; + $default = $locales['default_language'] ?? 'fr'; $this->locales = [ 'languages' => $languages, @@ -396,10 +383,8 @@ public function locales() /** * Columns of CSV file * This is already built to take multiple languages - * - * @return array */ - public function columns() + public function columns(): array { $orig = $this->origLanguage(); $opposites = $this->oppositeLanguages(); @@ -416,26 +401,17 @@ public function columns() return $columns; } - /** - * @return string - */ - public function enclosure() + public function enclosure(): string { return '"'; } - /** - * @return string - */ - public function separator() + public function separator(): string { return ','; } - /** - * @return integer - */ - public function maxRecursiveLevel() + public function maxRecursiveLevel(): int { return 4; } diff --git a/packages/admin/src/Charcoal/Admin/Script/User/AclRole/CreateScript.php b/packages/admin/src/Charcoal/Admin/Script/User/AclRole/CreateScript.php index ce397f0ee..08261ab64 100644 --- a/packages/admin/src/Charcoal/Admin/Script/User/AclRole/CreateScript.php +++ b/packages/admin/src/Charcoal/Admin/Script/User/AclRole/CreateScript.php @@ -18,9 +18,8 @@ class CreateScript extends AdminScript /** * @param RequestInterface $request A PSR-7 compatible Request instance. * @param ResponseInterface $response A PSR-7 compatible Response instance. - * @return ResponseInterface */ - public function run(RequestInterface $request, ResponseInterface $response) + public function run(RequestInterface $request, ResponseInterface $response): ResponseInterface { unset($request); @@ -72,10 +71,7 @@ public function run(RequestInterface $request, ResponseInterface $response) return $response; } - /** - * @return boolean - */ - public function authRequired() + public function authRequired(): bool { return false; } diff --git a/packages/admin/src/Charcoal/Admin/Script/User/CreateScript.php b/packages/admin/src/Charcoal/Admin/Script/User/CreateScript.php index 67872af55..e732e054c 100644 --- a/packages/admin/src/Charcoal/Admin/Script/User/CreateScript.php +++ b/packages/admin/src/Charcoal/Admin/Script/User/CreateScript.php @@ -40,6 +40,7 @@ public function __construct($data = null) * @param Container $container Pimple DI container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -52,10 +53,9 @@ protected function setDependencies(Container $container) * Retrieve the available default arguments of this action. * * @link http://climate.thephpleague.com/arguments/ For descriptions of the options for CLImate. - * - * @return array */ - public function defaultArguments() + #[\Override] + public function defaultArguments(): array { $arguments = [ 'email' => [ @@ -81,17 +81,14 @@ public function defaultArguments() ] ]; - $arguments = array_merge(parent::defaultArguments(), $arguments); - - return $arguments; + return array_merge(parent::defaultArguments(), $arguments); } /** * @param RequestInterface $request A PSR-7 compatible Request instance. * @param ResponseInterface $response A PSR-7 compatible Response instance. - * @return ResponseInterface */ - public function run(RequestInterface $request, ResponseInterface $response) + public function run(RequestInterface $request, ResponseInterface $response): ResponseInterface { unset($request); @@ -107,9 +104,8 @@ public function run(RequestInterface $request, ResponseInterface $response) /** * Create a new user in the database - * @return void */ - private function createUser() + private function createUser(): void { $authenticator = $this->authenticator(); @@ -148,11 +144,7 @@ private function createUser() $prompt = $prompts[$prop->ident()]; - if ($prompt['property']) { - $v = $prompt['property']; - } else { - $v = $this->promptProperty($prop, $prompt['label']); - } + $v = $prompt['property'] ?: $this->promptProperty($prop, $prompt['label']); if (isset($prompt['validation'])) { call_user_func($prompt['validation'], $v); } @@ -175,10 +167,7 @@ private function createUser() } } - /** - * @return array - */ - private function userPrompts() + private function userPrompts(): array { $translator = $this->translator(); $climate = $this->climate(); @@ -187,12 +176,12 @@ private function userPrompts() 'email' => [ 'label' => $translator->translate('Please enter email: '), 'property' => $climate->arguments->get('email'), - 'validation' => [ $this, 'validateEmail' ], + 'validation' => $this->validateEmail(...), ], 'password' => [ 'label' => $translator->translate('Please enter password: '), 'property' => $climate->arguments->get('password'), - 'validation' => [ $this, 'validatePassword' ], + 'validation' => $this->validatePassword(...), ], 'roles' => [ 'label' => $translator->translate('Please enter role(s) [ex: admin], comma separated: '), @@ -227,9 +216,8 @@ private function promptProperty($prop, $label) * @param string $email The email, from input. * @throws Exception If the email is empty or invalid (validated with php's filters) * or already exists in the database. - * @return void */ - private function validateEmail($email) + private function validateEmail($email): void { if (!$email) { throw new Exception( @@ -256,9 +244,8 @@ private function validateEmail($email) /** * @param string $password The password, from input. * @throws Exception If the password is empty or too small. - * @return void */ - private function validatePassword($password) + private function validatePassword($password): void { if (!$password) { throw new Exception( diff --git a/packages/admin/src/Charcoal/Admin/Script/User/ResetPasswordScript.php b/packages/admin/src/Charcoal/Admin/Script/User/ResetPasswordScript.php index 3b59f2e4d..e6c25cf94 100644 --- a/packages/admin/src/Charcoal/Admin/Script/User/ResetPasswordScript.php +++ b/packages/admin/src/Charcoal/Admin/Script/User/ResetPasswordScript.php @@ -37,6 +37,7 @@ public function __construct($data = null) * @param Container $container Pimple DI container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -49,10 +50,9 @@ protected function setDependencies(Container $container) * Retrieve the available default arguments of this action. * * @link http://climate.thephpleague.com/arguments/ For descriptions of the options for CLImate. - * - * @return array */ - public function defaultArguments() + #[\Override] + public function defaultArguments(): array { $arguments = [ 'email' => [ @@ -74,17 +74,14 @@ public function defaultArguments() ] ]; - $arguments = array_merge(parent::defaultArguments(), $arguments); - - return $arguments; + return array_merge(parent::defaultArguments(), $arguments); } /** * @param RequestInterface $request A PSR-7 compatible Request instance. * @param ResponseInterface $response A PSR-7 compatible Response instance. - * @return ResponseInterface */ - public function run(RequestInterface $request, ResponseInterface $response) + public function run(RequestInterface $request, ResponseInterface $response): ResponseInterface { unset($request); @@ -114,7 +111,7 @@ public function run(RequestInterface $request, ResponseInterface $response) $authenticator->changeUserPassword($user, $password); if ($climate->arguments->get('sendEmail')) { - $this->sendResetPasswordEmail($email, $password); + $this->sendResetPasswordEmail(); } $climate->red()->out( @@ -125,12 +122,10 @@ public function run(RequestInterface $request, ResponseInterface $response) } /** - * @param string $email The user email. - * @param string $password The new, plain-text password. * @return void * @todo Implement reset password email dispatch. */ - private function sendResetPasswordEmail($email, $password) + private function sendResetPasswordEmail() { } } diff --git a/packages/admin/src/Charcoal/Admin/Service/AssetsBuilder.php b/packages/admin/src/Charcoal/Admin/Service/AssetsBuilder.php index f6b7aacab..64edad4c8 100644 --- a/packages/admin/src/Charcoal/Admin/Service/AssetsBuilder.php +++ b/packages/admin/src/Charcoal/Admin/Service/AssetsBuilder.php @@ -19,23 +19,13 @@ */ final class AssetsBuilder { - /** - * @var AssetManager|null - */ - private $assetManager = null; - - /** - * @var string|null - */ - private $basePath = null; + private ?\Assetic\AssetManager $assetManager = null; /** * @param string|null $basePath The assets base path. - * @return void */ - public function __construct($basePath = null) + public function __construct(private $basePath = null) { - $this->basePath = $basePath; } /** @@ -44,7 +34,7 @@ public function __construct($basePath = null) * @param AssetsConfig $config The assets management config. * @return AssetManager */ - public function __invoke(AssetsConfig $config) + public function __invoke(AssetsConfig $config): ?\Assetic\AssetManager { return $this->build($config); } @@ -53,7 +43,7 @@ public function __invoke(AssetsConfig $config) * @param AssetsConfig $config The assets management config. * @return AssetManager */ - public function build(AssetsConfig $config) + public function build(AssetsConfig $config): ?\Assetic\AssetManager { $this->assetManager = new AssetManager(); $this->parseCollections($config->collections()); @@ -63,14 +53,13 @@ public function build(AssetsConfig $config) /** * @param array $collections Assets collections. - * @return void */ - private function parseCollections(array $collections) + private function parseCollections(array $collections): void { foreach ($collections as $collectionIdent => $actions) { $files = ($actions['files'] ?? []); // Parse scoped files. Solves merging issues. - array_walk($actions, function ($scope) use (&$files) { + array_walk($actions, function (array $scope) use (&$files): void { if (isset($scope['files']) && !empty($scope['files'])) { $files = array_merge($files, $scope['files']); } @@ -88,7 +77,7 @@ private function parseCollections(array $collections) * @param string[] $files Files to convert to Collection assets. * @return AssetInterface[] */ - private function extractFiles(array $files = []) + private function extractFiles(array $files = []): array { $collection = []; @@ -107,7 +96,7 @@ private function extractFiles(array $files = []) } // Files with asterisks should be treated as glob. - if (strpos($file, '*') !== false) { + if (str_contains($file, '*')) { $collection[] = new GlobAsset($file); continue; } @@ -128,7 +117,7 @@ private function extractFiles(array $files = []) * @param string $file A file path. * @return boolean Returns TRUE if the given path is absolute. Otherwise, returns FALSE. */ - private function isAbsolutePath($file) + private function isAbsolutePath($file): bool { $file = (string)$file; diff --git a/packages/admin/src/Charcoal/Admin/Service/Exporter.php b/packages/admin/src/Charcoal/Admin/Service/Exporter.php index f9411be72..c5902648f 100644 --- a/packages/admin/src/Charcoal/Admin/Service/Exporter.php +++ b/packages/admin/src/Charcoal/Admin/Service/Exporter.php @@ -26,6 +26,7 @@ */ class Exporter { + public $logger; use TranslatorAwareTrait; /** @@ -43,16 +44,14 @@ class Exporter /** * Options * Booleans - * @var boolean $convertBrToNewlines */ - private $convertBrToNewlines; + private ?bool $convertBrToNewlines = null; /** * Options * Booleans - * @var boolean $stripTags */ - private $stripTags; + private ?bool $stripTags = null; /** * Export ident for metadata @@ -62,21 +61,18 @@ class Exporter /** * Output properties - * @var array $properties */ - private $properties = []; + private array $properties = []; /** * CollectionConfig - * @var array $collectionConfig */ - private $collectionConfig; + private ?array $collectionConfig = null; /** * Model factory - * @var FactoryInterface $modelFactory */ - private $modelFactory; + private \Charcoal\Factory\FactoryInterface $modelFactory; /** * Property factory used @@ -134,7 +130,7 @@ public function __construct(array $data) * Init function * @return Exporter Chainable. */ - public function process() + public function process(): static { $this->prepareOptions(); $this->export(); @@ -143,9 +139,8 @@ public function process() /** * Export to CSV - * @return void */ - public function export() + public function export(): void { $headers = $this->fileHeaders(); $rows = $this->rows(); @@ -177,7 +172,7 @@ public function collection() if (!$this->collectionConfig()) { throw new RuntimeException(sprintf( 'Collection Config required for "%s"', - get_class($this) + static::class )); } @@ -230,7 +225,7 @@ private function proto() */ private function metadata() { - $proto = $this->proto(); + $this->proto(); return $this->proto()->metadata(); } @@ -241,7 +236,7 @@ private function metadata() * @throws InvalidArgumentException If no properties are defined. * @return Exporter Chainable. */ - private function prepareOptions() + private function prepareOptions(): static { $metadata = $this->metadata(); @@ -252,7 +247,7 @@ private function prepareOptions() throw new InvalidArgumentException(sprintf( 'No export ident defined for "%s" in %s', $this->objType(), - get_class($this) + static::class )); } @@ -265,7 +260,7 @@ private function prepareOptions() 'No export data defined for "%s" at "%s" in %s', $this->objType(), $this->exportIdent(), - get_class($this) + static::class )); } @@ -275,7 +270,7 @@ private function prepareOptions() throw new InvalidArgumentException(sprintf( 'No export data defined for "%s" in %s', $this->objType(), - get_class($this) + static::class )); } } @@ -284,7 +279,7 @@ private function prepareOptions() throw new InvalidArgumentException(sprintf( 'No properties defined to export "%s" in %s', $this->objType(), - get_class($this) + static::class )); } @@ -326,7 +321,7 @@ private function prepareOptions() /** * @return array File headers. */ - private function fileHeaders() + private function fileHeaders(): array { $metadata = $this->metadata(); $properties = $this->properties(); @@ -339,11 +334,7 @@ private function fileHeaders() continue; } - if (isset($prop['label'])) { - $label = $this->translator()->translation($prop['label']); - } else { - $label = ucfirst($p); - } + $label = isset($prop['label']) ? $this->translator()->translation($prop['label']) : ucfirst((string) $p); $out[] = $label; } @@ -394,7 +385,7 @@ private function rows() * @param string $filename Output filename. * @return Exporter (chainable). */ - private function setFilename($filename) + private function setFilename($filename): static { $this->filename = $filename; return $this; @@ -404,7 +395,7 @@ private function setFilename($filename) * @param string $objType Object to be exported. * @return Exporter (chainable). */ - private function setObjType($objType) + private function setObjType($objType): static { $this->objType = $objType; return $this; @@ -415,9 +406,9 @@ private function setObjType($objType) * @param boolean $bool Convert br to newline. * @return Exporter (chainable) */ - private function setConvertBrToNewlines($bool) + private function setConvertBrToNewlines($bool): static { - $this->convertBrToNewlines = !!$bool; + $this->convertBrToNewlines = (bool) $bool; return $this; } @@ -426,9 +417,9 @@ private function setConvertBrToNewlines($bool) * @param boolean $bool Strip tags. * @return Exporter (chainable) */ - private function setStripTags($bool) + private function setStripTags($bool): static { - $this->stripTags = !!$bool; + $this->stripTags = (bool) $bool; return $this; } @@ -439,7 +430,7 @@ private function setStripTags($bool) * @param string $ident Config ident. * @return Exporter (chainable) */ - public function setExportIdent($ident) + public function setExportIdent($ident): static { $this->exportIdent = $ident; return $this; @@ -451,14 +442,14 @@ public function setExportIdent($ident) * @throws InvalidArgumentException If the array is not a list of strings. * @return Exporter Chainable. */ - private function setProperties(array $properties) + private function setProperties(array $properties): static { $p = reset($properties); if (!is_string($p)) { throw new InvalidArgumentException(sprintf( 'Invalid properties to export "%s" in %s', $this->objType(), - get_class($this) + static::class )); } @@ -471,7 +462,7 @@ private function setProperties(array $properties) * @param array $cfg Collection config. * @return Exporter Chainable. */ - private function setCollectionConfig(array $cfg) + private function setCollectionConfig(array $cfg): static { $this->collectionConfig = $cfg; return $this; @@ -482,7 +473,7 @@ private function setCollectionConfig(array $cfg) * @param FactoryInterface $factory Model factory. * @return Exporter (chainable) */ - private function setModelFactory(FactoryInterface $factory) + private function setModelFactory(FactoryInterface $factory): static { $this->modelFactory = $factory; return $this; @@ -492,7 +483,7 @@ private function setModelFactory(FactoryInterface $factory) * @param FactoryInterface $factory The property factory, to create properties. * @return TableWidget Chainable */ - private function setPropertyFactory(FactoryInterface $factory) + private function setPropertyFactory(FactoryInterface $factory): static { $this->propertyFactory = $factory; return $this; @@ -521,7 +512,7 @@ private function objType() /** * @return boolean Convert to newlines. */ - private function convertBrToNewlines() + private function convertBrToNewlines(): ?bool { return $this->convertBrToNewlines; } @@ -529,7 +520,7 @@ private function convertBrToNewlines() /** * @return boolean Striptags. */ - private function stripTags() + private function stripTags(): ?bool { return $this->stripTags; } @@ -545,7 +536,7 @@ private function exportIdent() /** * @return array Properties. */ - private function properties() + private function properties(): array { return $this->properties; } @@ -553,7 +544,7 @@ private function properties() /** * @return array CollectionConfig. */ - private function collectionConfig() + private function collectionConfig(): ?array { return $this->collectionConfig; } @@ -561,7 +552,7 @@ private function collectionConfig() /** * @return ModelFactory Model factory. */ - private function modelFactory() + private function modelFactory(): \Charcoal\Factory\FactoryInterface { return $this->modelFactory; } @@ -588,11 +579,10 @@ private function propertyFactory() * @param string $text Text. * @return string Text with newlines. */ - private function brToNewline($text) + private function brToNewline($text): string { $breaks = [ '
', '
', '
' ]; - $text = str_ireplace($breaks, "\r\n", $text); - return $text; + return str_ireplace($breaks, "\r\n", $text); } /** diff --git a/packages/admin/src/Charcoal/Admin/Service/SelectizeRenderer.php b/packages/admin/src/Charcoal/Admin/Service/SelectizeRenderer.php index 43e369d42..3727a3276 100644 --- a/packages/admin/src/Charcoal/Admin/Service/SelectizeRenderer.php +++ b/packages/admin/src/Charcoal/Admin/Service/SelectizeRenderer.php @@ -64,15 +64,14 @@ public function __construct(array $data) * @param ModelInterface|array|null $context The context as Model or array. * @param string|null $controllerIdent The ControllerIdent string to override Object context. * @throws \InvalidArgumentException If the callable id not callable. - * @return string */ - public function renderTemplate($templateIdent, $context, $controllerIdent = null) + public function renderTemplate(string $templateIdent, $context, $controllerIdent = null): string { $template = null; if ($controllerIdent && is_string($controllerIdent)) { $controllerIdent = explode('::', $controllerIdent); - $controllerCallable = isset($controllerIdent[1]) ? $controllerIdent[1] : null; + $controllerCallable = $controllerIdent[1] ?? null; $controllerIdent = $controllerIdent[0]; $template = $this->templateFactory->create($controllerIdent); @@ -84,7 +83,7 @@ public function renderTemplate($templateIdent, $context, $controllerIdent = null '%s::%s supplied in %s::%s is not a callable method.', $controllerIdent, $controllerCallable, - __CLASS__, + self::class, __FUNCTION__ )); } @@ -103,8 +102,8 @@ public function renderTemplate($templateIdent, $context, $controllerIdent = null } else { throw new \InvalidArgumentException(sprintf( '%s supplied in %s::%s is not callable.', - get_class($template), - __CLASS__, + $template::class, + self::class, __FUNCTION__ )); } diff --git a/packages/admin/src/Charcoal/Admin/ServiceProvider/AclServiceProvider.php b/packages/admin/src/Charcoal/Admin/ServiceProvider/AclServiceProvider.php index 7e3aebc59..c15a395a5 100644 --- a/packages/admin/src/Charcoal/Admin/ServiceProvider/AclServiceProvider.php +++ b/packages/admin/src/Charcoal/Admin/ServiceProvider/AclServiceProvider.php @@ -31,9 +31,8 @@ class AclServiceProvider implements ServiceProviderInterface { /** * @param Container $container Pimple DI Container. - * @return void */ - public function register(Container $container) + public function register(Container $container): void { /** * Use an AclManager to load default permissions from config and database. @@ -41,7 +40,7 @@ public function register(Container $container) * @param Container $container Pimple DI container * @return Acl */ - $container['admin/acl'] = function (Container $container) { + $container['admin/acl'] = function (Container $container): \Laminas\Permissions\Acl\Acl { $adminConfig = $container['admin/config']; @@ -75,8 +74,6 @@ public function register(Container $container) * @todo Do this right! * @return Acl */ - $container['authorizer/acl'] = function () { - return $container['admin/acl']; - }; + $container['authorizer/acl'] = (fn(): \Closure => $container['admin/acl']); } } diff --git a/packages/admin/src/Charcoal/Admin/ServiceProvider/AdminServiceProvider.php b/packages/admin/src/Charcoal/Admin/ServiceProvider/AdminServiceProvider.php index fb236ce02..c120a7d3c 100644 --- a/packages/admin/src/Charcoal/Admin/ServiceProvider/AdminServiceProvider.php +++ b/packages/admin/src/Charcoal/Admin/ServiceProvider/AdminServiceProvider.php @@ -66,9 +66,8 @@ class AdminServiceProvider implements ServiceProviderInterface * It should not get services. * * @param Container $container The Pimple DI container. - * @return void */ - public function register(Container $container) + public function register(Container $container): void { // Ensure dependencies are set $container->register(new EmailServiceProvider()); @@ -102,7 +101,7 @@ protected function registerAdminServices(Container $container) * @param Container $container The Pimple DI Container. * @return AdminConfig */ - $container['admin/config'] = function (Container $container) { + $container['admin/config'] = function (Container $container): \Charcoal\Admin\Config { $appConfig = $container['config']; $extraConfigs = []; @@ -124,12 +123,10 @@ protected function registerAdminServices(Container $container) array_push($extraConfigs, ...$appAdminConfigs); } - if (!empty($extraConfigs)) { - foreach ($extraConfigs as $path) { - $configPath = $appConfig['base_path'] . DIRECTORY_SEPARATOR . ltrim($path, '/'); + foreach ($extraConfigs as $path) { + $configPath = $appConfig['base_path'] . DIRECTORY_SEPARATOR . ltrim($path, '/'); - $appConfig->addFile($configPath); - } + $appConfig->addFile($configPath); } $adminConfig = $appConfig['admin']; @@ -157,7 +154,7 @@ protected function registerAdminServices(Container $container) $adminUrl = clone $container['base-url']; if ($adminConfig['base_path']) { $basePath = rtrim($adminUrl->getBasePath(), '/'); - $adminPath = ltrim($adminConfig['base_path'], '/'); + $adminPath = ltrim((string) $adminConfig['base_path'], '/'); $adminUrl = $adminUrl->withBasePath($basePath . '/' . $adminPath); } } @@ -181,11 +178,9 @@ protected function registerAdminServices(Container $container) * @param Container $container A container instance. * @return ViewInterface */ - $container->extend('view', function (GenericView $view, Container $container): ViewInterface { - return new GenericView([ - 'engine' => $container['view/engine/mustache'] - ]); - }); + $container->extend('view', fn(GenericView $view, Container $container): ViewInterface => new GenericView([ + 'engine' => $container['view/engine/mustache'] + ])); /** * Extend view/config. @@ -216,11 +211,10 @@ protected function registerMetadataExtensions(Container $container) /** * @return MetadataConfig */ - $container['metadata/config'] = function (Container $container) { + $container['metadata/config'] = function (Container $container): \Charcoal\Model\Service\MetadataConfig { $settings = $container['admin/config']['metadata']; - $metaConfig = new MetadataConfig($settings); - return $metaConfig; + return new MetadataConfig($settings); }; } else { /** @@ -232,9 +226,9 @@ protected function registerMetadataExtensions(Container $container) * @param Container $container The Pimple DI container. * @return MetadataConfig */ - $container->extend('metadata/config', function (MetadataConfig $metaConfig, Container $container) { + $container->extend('metadata/config', function (MetadataConfig $metaConfig, Container $container): \Charcoal\Model\Service\MetadataConfig { $settings = $container['admin/config']['metadata']; - if (is_array($settings) && !empty($settings)) { + if (is_array($settings) && $settings !== []) { $metaConfig->merge($settings); } @@ -276,16 +270,16 @@ protected function registerMetadataExtensions(Container $container) * @param Container $container The Pimple DI container. * @return MetadataConfig */ - $container->extend('metadata/config', function (MetadataConfig $metaConfig, Container $container) { + $container->extend('metadata/config', function (MetadataConfig $metaConfig, Container $container): \Charcoal\Model\Service\MetadataConfig { $adminConfig = $container['admin/config']; - $adminDir = '/' . trim($adminConfig['base_path'], '/'); + $adminDir = '/' . trim((string) $adminConfig['base_path'], '/'); $metaPaths = $metaConfig->paths(); $parsedPaths = []; foreach ($metaPaths as $basePath) { $adminPath = rtrim($basePath, '/') . $adminDir; - - array_push($parsedPaths, $adminPath, $basePath); + $parsedPaths[] = $adminPath; + $parsedPaths[] = $basePath; } $metaConfig->setPaths($parsedPaths); @@ -306,15 +300,13 @@ protected function registerAuthExtensions(Container $container) * @param Container $container The Pimple DI Container. * @return Authenticator */ - $container['admin/authenticator'] = function (Container $container) { - return new Authenticator([ - 'logger' => $container['logger'], - 'user_type' => User::class, - 'user_factory' => $container['model/factory'], - 'token_type' => AuthToken::class, - 'token_factory' => $container['model/factory'] - ]); - }; + $container['admin/authenticator'] = (fn(Container $container): \Charcoal\User\Authenticator => new Authenticator([ + 'logger' => $container['logger'], + 'user_type' => User::class, + 'user_factory' => $container['model/factory'], + 'token_type' => AuthToken::class, + 'token_factory' => $container['model/factory'] + ])); /** * Replace default Authenticator ('charcoal-ui') with the Admin Authenticator. @@ -323,21 +315,17 @@ protected function registerAuthExtensions(Container $container) * @param Container $container The Pimple DI Container. * @return Authenticator */ - $container['authenticator'] = function (Container $container) { - return $container['admin/authenticator']; - }; + $container['authenticator'] = (fn(Container $container): mixed => $container['admin/authenticator']); /** * @param Container $container The Pimple DI container. * @return Authorizer */ - $container['admin/authorizer'] = function (Container $container) { - return new Authorizer([ - 'logger' => $container['logger'], - 'acl' => $container['admin/acl'], - 'resource' => 'admin' - ]); - }; + $container['admin/authorizer'] = (fn(Container $container): \Charcoal\User\Authorizer => new Authorizer([ + 'logger' => $container['logger'], + 'acl' => $container['admin/acl'], + 'resource' => 'admin' + ])); /** * Replace default Authorizer ('charcoal-ui') with the Admin Authorizer. @@ -346,9 +334,7 @@ protected function registerAuthExtensions(Container $container) * @param Container $container The Pimple DI Container. * @return Authorizer */ - $container['authorizer'] = function (Container $container) { - return $container['admin/authorizer']; - }; + $container['authorizer'] = (fn(Container $container): mixed => $container['admin/authorizer']); } /** @@ -360,9 +346,7 @@ protected function registerAuthExtensions(Container $container) protected function registerViewExtensions(Container $container) { if (!isset($container['view/mustache/helpers'])) { - $container['view/mustache/helpers'] = function () { - return []; - }; + $container['view/mustache/helpers'] = (fn(): array => []); } /** @@ -370,7 +354,7 @@ protected function registerViewExtensions(Container $container) * * @return array */ - $container->extend('view/mustache/helpers', function (array $helpers, Container $container) { + $container->extend('view/mustache/helpers', function (array $helpers, Container $container): array { $adminUrl = $container['admin/base-url']; $urls = [ @@ -387,8 +371,8 @@ protected function registerViewExtensions(Container $container) * @param string $uri A URI path to wrap. * @return UriInterface|null */ - 'withAdminUrl' => function ($uri, LambdaHelper $helper = null) use ($adminUrl) { - if ($helper) { + 'withAdminUrl' => function ($uri, ?LambdaHelper $helper = null) use ($adminUrl) { + if ($helper instanceof \Mustache_LambdaHelper) { $uri = $helper->render($uri); } @@ -397,16 +381,13 @@ protected function registerViewExtensions(Container $container) $uri = $adminUrl->withPath(''); } else { $parts = parse_url($uri); - if (!isset($parts['scheme'])) { - if (!in_array($uri[0], ['/', '#', '?'])) { - $path = isset($parts['path']) ? ltrim($parts['path'], '/') : ''; - $query = isset($parts['query']) ? $parts['query'] : ''; - $hash = isset($parts['fragment']) ? $parts['fragment'] : ''; - - return $adminUrl->withPath($path) - ->withQuery($query) - ->withFragment($hash); - } + if (!isset($parts['scheme']) && !in_array($uri[0], ['/', '#', '?'])) { + $path = isset($parts['path']) ? ltrim($parts['path'], '/') : ''; + $query = $parts['query'] ?? ''; + $hash = $parts['fragment'] ?? ''; + return $adminUrl->withPath($path) + ->withQuery($query) + ->withFragment($hash); } } @@ -432,7 +413,7 @@ protected function registerElfinderServices(Container $container) * @param AdminConfig $adminConfig The admin configset. * @return AdminConfig */ - $container->extend('admin/config', function (AdminConfig $adminConfig) { + $container->extend('admin/config', function (AdminConfig $adminConfig): \Charcoal\Admin\Config { $adminConfig['elfinder'] = new Config($adminConfig['elfinder']); return $adminConfig; @@ -444,9 +425,7 @@ protected function registerElfinderServices(Container $container) * @param Container $container The Pimple DI Container. * @return ConfigInterface */ - $container['elfinder/config'] = function (Container $container) { - return $container['admin/config']['elfinder']; - }; + $container['elfinder/config'] = (fn(Container $container) => $container['admin/config']['elfinder']); } /** @@ -463,14 +442,12 @@ protected function registerSelectizeServices(Container $container) * @param Container $container The Pimple DI container. * @return SelectizeRenderer */ - $container['selectize/renderer'] = function (Container $container) { - return new SelectizeRenderer([ - 'logger' => $container['logger'], - 'translator' => $container['translator'], - 'template_factory' => $container['template/factory'], - 'view' => $container['view'] - ]); - }; + $container['selectize/renderer'] = (fn(Container $container): \Charcoal\Admin\Service\SelectizeRenderer => new SelectizeRenderer([ + 'logger' => $container['logger'], + 'translator' => $container['translator'], + 'template_factory' => $container['template/factory'], + 'view' => $container['view'] + ])); } /** @@ -479,7 +456,7 @@ protected function registerSelectizeServices(Container $container) */ protected function registerAssetsManager(Container $container) { - $container['assets/config'] = function (Container $container) { + $container['assets/config'] = function (Container $container): \Charcoal\Admin\AssetsConfig { $config = $container['admin/config']->get('assets'); return new AssetsConfig($config); @@ -498,54 +475,48 @@ protected function registerFactoryServices(Container $container) * @param Container $container The Pimple DI container. * @return FactoryInterface */ - $container['property/input/factory'] = function (Container $container) { - return new Factory([ - 'base_class' => PropertyInputInterface::class, - 'arguments' => [[ - 'container' => $container, - 'logger' => $container['logger'] - ]], - 'resolver_options' => [ - 'suffix' => 'Input' - ] - ]); - }; + $container['property/input/factory'] = (fn(Container $container): \Charcoal\Factory\GenericFactory => new Factory([ + 'base_class' => PropertyInputInterface::class, + 'arguments' => [[ + 'container' => $container, + 'logger' => $container['logger'] + ]], + 'resolver_options' => [ + 'suffix' => 'Input' + ] + ])); /** * @param Container $container The Pimple DI container. * @return FactoryInterface */ - $container['property/display/factory'] = function (Container $container) { - return new Factory([ - 'base_class' => PropertyDisplayInterface::class, - 'arguments' => [[ - 'container' => $container, - 'logger' => $container['logger'] - ]], - 'resolver_options' => [ - 'suffix' => 'Display' - ] - ]); - }; + $container['property/display/factory'] = (fn(Container $container): \Charcoal\Factory\GenericFactory => new Factory([ + 'base_class' => PropertyDisplayInterface::class, + 'arguments' => [[ + 'container' => $container, + 'logger' => $container['logger'] + ]], + 'resolver_options' => [ + 'suffix' => 'Display' + ] + ])); /** * @param Container $container A Pimple DI container. * @return FactoryInterface */ - $container['secondary-menu/group/factory'] = function (Container $container) { - return new Factory([ - 'base_class' => SecondaryMenuGroupInterface::class, - 'default_class' => GenericSecondaryMenuGroup::class, - 'arguments' => [[ - 'container' => $container, - 'logger' => $container['logger'], - 'view' => $container['view'], - 'layout_builder' => $container['layout/builder'] - ]], - 'resolver_options' => [ - 'suffix' => 'SecondaryMenuGroup' - ] - ]); - }; + $container['secondary-menu/group/factory'] = (fn(Container $container): \Charcoal\Factory\GenericFactory => new Factory([ + 'base_class' => SecondaryMenuGroupInterface::class, + 'default_class' => GenericSecondaryMenuGroup::class, + 'arguments' => [[ + 'container' => $container, + 'logger' => $container['logger'], + 'view' => $container['view'], + 'layout_builder' => $container['layout/builder'] + ]], + 'resolver_options' => [ + 'suffix' => 'SecondaryMenuGroup' + ] + ])); } } diff --git a/packages/admin/src/Charcoal/Admin/ServiceProvider/AssetsManagerServiceProvider.php b/packages/admin/src/Charcoal/Admin/ServiceProvider/AssetsManagerServiceProvider.php index a57520303..3132925d8 100644 --- a/packages/admin/src/Charcoal/Admin/ServiceProvider/AssetsManagerServiceProvider.php +++ b/packages/admin/src/Charcoal/Admin/ServiceProvider/AssetsManagerServiceProvider.php @@ -21,9 +21,8 @@ class AssetsManagerServiceProvider implements ServiceProviderInterface * It should not get services. * * @param Container $container A container instance. - * @return void */ - public function register(Container $container) + public function register(Container $container): void { $this->registerAssetsManager($container); $this->registerMustacheHelpersServices($container); @@ -36,9 +35,7 @@ public function register(Container $container) protected function registerMustacheHelpersServices(Container $container) { if (!isset($container['view/mustache/helpers'])) { - $container['view/mustache/helpers'] = function () { - return []; - }; + $container['view/mustache/helpers'] = (fn(): array => []); } /** @@ -47,11 +44,9 @@ protected function registerMustacheHelpersServices(Container $container) * @param Container $container Pimple DI container. * @return AssetsHelpers */ - $container['view/mustache/helpers/assets-manager'] = function (Container $container) { - return new AssetsHelpers([ - 'assets' => $container['assets'] - ]); - }; + $container['view/mustache/helpers/assets-manager'] = (fn(Container $container): \Charcoal\Admin\Mustache\AssetsHelpers => new AssetsHelpers([ + 'assets' => $container['assets'] + ])); /** * Extend global helpers for the Mustache Engine. @@ -60,12 +55,10 @@ protected function registerMustacheHelpersServices(Container $container) * @param Container $container A container instance. * @return array */ - $container->extend('view/mustache/helpers', function (array $helpers, Container $container) { - return array_merge( - $helpers, - $container['view/mustache/helpers/assets-manager']->toArray() - ); - }); + $container->extend('view/mustache/helpers', fn(array $helpers, Container $container): array => array_merge( + $helpers, + $container['view/mustache/helpers/assets-manager']->toArray() + )); } /** @@ -76,13 +69,13 @@ protected function registerMustacheHelpersServices(Container $container) */ protected function registerAssetsManager(Container $container) { - $container['assets/config'] = function (Container $container) { + $container['assets/config'] = function (Container $container): \Charcoal\Admin\AssetsConfig { $config = $container['view/config']->get('assets'); return new AssetsConfig($config); }; - $container['assets/builder'] = function (Container $container) { + $container['assets/builder'] = function (Container $container): \Charcoal\Admin\Service\AssetsBuilder { $appConfig = $container['config']; return new AssetsBuilder($appConfig['base_path']); diff --git a/packages/admin/src/Charcoal/Admin/Support/AdminTrait.php b/packages/admin/src/Charcoal/Admin/Support/AdminTrait.php index 33227c31e..d46b7c273 100644 --- a/packages/admin/src/Charcoal/Admin/Support/AdminTrait.php +++ b/packages/admin/src/Charcoal/Admin/Support/AdminTrait.php @@ -49,12 +49,10 @@ protected function adminConfig($key = null, $default = null) if ($key) { if (isset($this->adminConfig[$key])) { return $this->adminConfig[$key]; + } elseif (!is_string($default) && is_callable($default)) { + return $default(); } else { - if (!is_string($default) && is_callable($default)) { - return $default(); - } else { - return $default; - } + return $default; } } @@ -84,12 +82,10 @@ protected function appConfig($key = null, $default = null) if ($key) { if (isset($this->appConfig[$key])) { return $this->appConfig[$key]; + } elseif (!is_string($default) && is_callable($default)) { + return $default(); } else { - if (!is_string($default) && is_callable($default)) { - return $default(); - } else { - return $default; - } + return $default; } } diff --git a/packages/admin/src/Charcoal/Admin/Support/BaseUrlTrait.php b/packages/admin/src/Charcoal/Admin/Support/BaseUrlTrait.php index b66c8c610..dc6bdcb85 100644 --- a/packages/admin/src/Charcoal/Admin/Support/BaseUrlTrait.php +++ b/packages/admin/src/Charcoal/Admin/Support/BaseUrlTrait.php @@ -50,7 +50,7 @@ public function baseUrl($targetPath = null) if (!isset($this->baseUrl)) { throw new RuntimeException(sprintf( 'The base URI is not defined for [%s]', - get_class($this) + $this::class )); } @@ -86,7 +86,7 @@ public function adminUrl($targetPath = null) if (!isset($this->adminUrl)) { throw new RuntimeException(sprintf( 'The Admin URI is not defined for [%s]', - get_class($this) + $this::class )); } @@ -101,17 +101,10 @@ public function adminUrl($targetPath = null) * Determine if the given URI is relative. * * @param string $uri A URI path to test. - * @return boolean */ - protected function isRelativeUri($uri) + protected function isRelativeUri($uri): bool { - if ($uri && !parse_url($uri, PHP_URL_SCHEME)) { - if (!in_array($uri[0], [ '/', '#', '?' ])) { - return true; - } - } - - return false; + return $uri && !parse_url($uri, PHP_URL_SCHEME) && !in_array($uri[0], [ '/', '#', '?' ]); } /** @@ -126,14 +119,12 @@ protected function createAbsoluteUrl(UriInterface $basePath, $targetPath) $targetPath = strval($targetPath); if ($targetPath === '') { return $basePath->withPath(''); - } else { - if ($this->isRelativeUri($targetPath)) { - $parts = parse_url($targetPath); - $path = isset($parts['path']) ? ltrim($parts['path'], '/') : ''; - $query = isset($parts['query']) ? $parts['query'] : ''; - $hash = isset($parts['fragment']) ? $parts['fragment'] : ''; - $targetPath = $basePath->withPath($path)->withQuery($query)->withFragment($hash); - } + } elseif ($this->isRelativeUri($targetPath)) { + $parts = parse_url($targetPath); + $path = isset($parts['path']) ? ltrim($parts['path'], '/') : ''; + $query = $parts['query'] ?? ''; + $hash = $parts['fragment'] ?? ''; + $targetPath = $basePath->withPath($path)->withQuery($query)->withFragment($hash); } return $targetPath; diff --git a/packages/admin/src/Charcoal/Admin/Support/HttpAwareTrait.php b/packages/admin/src/Charcoal/Admin/Support/HttpAwareTrait.php index 090f313af..24e2a324b 100644 --- a/packages/admin/src/Charcoal/Admin/Support/HttpAwareTrait.php +++ b/packages/admin/src/Charcoal/Admin/Support/HttpAwareTrait.php @@ -38,7 +38,7 @@ public function httpRequest() if ($this->httpRequest === null) { throw new RuntimeException(sprintf( 'PSR-7 HTTP Request is not defined for "%s"', - get_class($this) + $this::class )); } @@ -47,10 +47,8 @@ public function httpRequest() /** * Determine if a HTTP request object is set. - * - * @return boolean */ - public function hasHttpRequest() + public function hasHttpRequest(): bool { return $this->httpRequest instanceof RequestInterface; } @@ -77,7 +75,7 @@ public function httpResponse() if ($this->httpResponse === null) { throw new RuntimeException(sprintf( 'PSR-7 HTTP Response is not defined for "%s"', - get_class($this) + $this::class )); } @@ -86,10 +84,8 @@ public function httpResponse() /** * Determine if a HTTP response object is set. - * - * @return boolean */ - public function hasHttpResponse() + public function hasHttpResponse(): bool { return $this->httpResponse instanceof ResponseInterface; } @@ -124,10 +120,8 @@ protected function updateHttpResponseStatus($code, $reasonPhrase = '') /** * Is this response successful? - * - * @return boolean */ - protected function isHttpResponseSuccessful() + protected function isHttpResponseSuccessful(): bool { $response = $this->httpResponse(); diff --git a/packages/admin/src/Charcoal/Admin/Support/SecurityTrait.php b/packages/admin/src/Charcoal/Admin/Support/SecurityTrait.php index 86712e71e..23dbb0209 100644 --- a/packages/admin/src/Charcoal/Admin/Support/SecurityTrait.php +++ b/packages/admin/src/Charcoal/Admin/Support/SecurityTrait.php @@ -15,10 +15,8 @@ trait SecurityTrait * * For example, the "Login" / "Reset Password" templates * should return `false`. - * - * @return boolean */ - protected function authRequired() + protected function authRequired(): bool { return true; } diff --git a/packages/admin/src/Charcoal/Admin/Support/Sorter.php b/packages/admin/src/Charcoal/Admin/Support/Sorter.php index c8c172b0d..5041793d8 100644 --- a/packages/admin/src/Charcoal/Admin/Support/Sorter.php +++ b/packages/admin/src/Charcoal/Admin/Support/Sorter.php @@ -1,5 +1,7 @@ $b; } } diff --git a/packages/admin/src/Charcoal/Admin/Template/Account/LostPasswordTemplate.php b/packages/admin/src/Charcoal/Admin/Template/Account/LostPasswordTemplate.php index 9cef74fe5..1eb08d053 100644 --- a/packages/admin/src/Charcoal/Admin/Template/Account/LostPasswordTemplate.php +++ b/packages/admin/src/Charcoal/Admin/Template/Account/LostPasswordTemplate.php @@ -21,9 +21,9 @@ class LostPasswordTemplate extends AdminTemplate * Determine if the password token is valid. * * @param RequestInterface $request The PSR-7 HTTP request. - * @return boolean */ - public function init(RequestInterface $request) + #[\Override] + public function init(RequestInterface $request): bool { $translator = $this->translator(); @@ -52,10 +52,8 @@ public function init(RequestInterface $request) return true; } - /** - * @return boolean - */ - public function authRequired() + #[\Override] + public function authRequired(): bool { return false; } @@ -73,6 +71,7 @@ public function urlLostPasswordAction() * * @return \Charcoal\Translator\Translation|string|null */ + #[\Override] public function title() { if ($this->title === null) { @@ -87,7 +86,8 @@ public function title() * * @return string[] */ - public function recaptchaParameters() + #[\Override] + public function recaptchaParameters(): array { $params = parent::recaptchaParameters(); $params['tabindex'] = 2; @@ -103,13 +103,11 @@ public function recaptchaParameters() // Templating // ========================================================================= - /** * Determine if main & secondary menu should appear as mobile in a desktop resolution. - * - * @return boolean */ - public function isFullscreenTemplate() + #[\Override] + public function isFullscreenTemplate(): bool { return true; } diff --git a/packages/admin/src/Charcoal/Admin/Template/Account/ResetPasswordTemplate.php b/packages/admin/src/Charcoal/Admin/Template/Account/ResetPasswordTemplate.php index d46c70b53..73044b1a4 100644 --- a/packages/admin/src/Charcoal/Admin/Template/Account/ResetPasswordTemplate.php +++ b/packages/admin/src/Charcoal/Admin/Template/Account/ResetPasswordTemplate.php @@ -30,18 +30,14 @@ class ResetPasswordTemplate extends AdminTemplate * Determine if the password token is valid. * * @param RequestInterface $request The PSR-7 HTTP request. - * @return boolean */ - public function init(RequestInterface $request) + #[\Override] + public function init(RequestInterface $request): bool { // Undocumented Slim 3 feature: The route attributes are stored in routeInfo[2]. $routeInfo = $request->getAttribute('routeInfo'); - if (isset($routeInfo[2]['token'])) { - $this->lostPasswordToken = $routeInfo[2]['token']; - } else { - $this->lostPasswordToken = $request->getParam('token'); - } + $this->lostPasswordToken = $routeInfo[2]['token'] ?? $request->getParam('token'); if ($this->lostPasswordToken && $this->validateToken($this->lostPasswordToken)) { return true; @@ -60,10 +56,8 @@ public function lostPasswordToken() return $this->lostPasswordToken; } - /** - * @return boolean - */ - public function authRequired() + #[\Override] + public function authRequired(): bool { return false; } @@ -86,9 +80,8 @@ public function urlResetPasswordAction() * * @see \Charcoal\Admin\Action\Account\ResetPasswordAction::validateToken() * @param string $token The token to validate. - * @return boolean */ - private function validateToken($token) + private function validateToken($token): bool { $obj = $this->modelFactory()->create(LostPasswordToken::class); $sql = strtr('SELECT * FROM `%table` WHERE `token` = :token AND `expiry` > NOW()', [ @@ -98,7 +91,7 @@ private function validateToken($token) 'token' => $token ]); - return !!$obj->token(); + return (bool) $obj->token(); } /** @@ -106,6 +99,7 @@ private function validateToken($token) * * @return \Charcoal\Translator\Translation|string|null */ + #[\Override] public function title() { if ($this->title === null) { @@ -120,7 +114,8 @@ public function title() * * @return string[] */ - public function recaptchaParameters() + #[\Override] + public function recaptchaParameters(): array { $params = parent::recaptchaParameters(); $params['tabindex'] = 5; @@ -136,13 +131,11 @@ public function recaptchaParameters() // Templating // ========================================================================= - /** * Determine if main & secondary menu should appear as mobile in a desktop resolution. - * - * @return boolean */ - public function isFullscreenTemplate() + #[\Override] + public function isFullscreenTemplate(): bool { return true; } diff --git a/packages/admin/src/Charcoal/Admin/Template/AuthTemplateTrait.php b/packages/admin/src/Charcoal/Admin/Template/AuthTemplateTrait.php index ab2c8b3fe..023686dda 100644 --- a/packages/admin/src/Charcoal/Admin/Template/AuthTemplateTrait.php +++ b/packages/admin/src/Charcoal/Admin/Template/AuthTemplateTrait.php @@ -65,7 +65,7 @@ public function urlResetPassword() * TRUE to use the default label, * or FALSE to disable the link. */ - public function returnToSiteLabel() + public function returnToSiteLabel(): false|string { $label = $this->adminConfig('login.visit_site'); if ($label === false) { diff --git a/packages/admin/src/Charcoal/Admin/Template/ElfinderTemplate.php b/packages/admin/src/Charcoal/Admin/Template/ElfinderTemplate.php index f578f0f48..b131566ea 100644 --- a/packages/admin/src/Charcoal/Admin/Template/ElfinderTemplate.php +++ b/packages/admin/src/Charcoal/Admin/Template/ElfinderTemplate.php @@ -44,45 +44,35 @@ class ElfinderTemplate extends AdminTemplate /** * The related object type. - * - * @var string */ - private $objType; + private string|bool|null $objType = null; /** * The related object ID. - * - * @var string */ - private $objId; + private string|bool|null $objId = null; /** * The related property identifier. - * - * @var string */ - private $propertyIdent; + private string|bool|null $propertyIdent = null; /** * Whether to output JS/CSS assets for initializing elFinder. - * - * @var boolean */ - private $showAssets = true; + private bool $showAssets = true; /** * Custom localization messages. * * @var array|null */ - private $localizations; + private \ArrayIterator|array|null $localizations = null; /** * The related JS callback ID. - * - * @var string */ - private $callbackIdent = ''; + private string|bool $callbackIdent = ''; /** * URL for the elFinder connector. @@ -97,7 +87,8 @@ class ElfinderTemplate extends AdminTemplate * @param RequestInterface $request A PSR-7 compatible Request instance. * @return self */ - protected function setDataFromRequest(RequestInterface $request) + #[\Override] + protected function setDataFromRequest(RequestInterface $request): bool { $keys = $this->validDataFromRequest(); $data = $request->getParams($keys); @@ -115,7 +106,7 @@ protected function setDataFromRequest(RequestInterface $request) } if (isset($data['assets'])) { - $this->showAssets = !!$data['assets']; + $this->showAssets = (bool) $data['assets']; } if (isset($data['callback'])) { @@ -137,7 +128,8 @@ protected function setDataFromRequest(RequestInterface $request) * * @return string[] */ - protected function validDataFromRequest() + #[\Override] + protected function validDataFromRequest(): array { return array_merge([ // Current object @@ -152,6 +144,7 @@ protected function validDataFromRequest() * * @return Translation */ + #[\Override] public function title() { if ($this->title === null) { @@ -165,9 +158,8 @@ public function title() * Set the custom localization messages. * * @param array $localizations An associative array of localizations. - * @return self */ - public function setLocalizations(array $localizations) + public function setLocalizations(array $localizations): static { $this->localizations = new ArrayIterator(); @@ -184,14 +176,13 @@ public function setLocalizations(array $localizations) * @param string $ident The message ID. * @param mixed $translations The message translations. * @throws InvalidArgumentException If the message ID is not a string or the translations are invalid. - * @return self */ - public function addLocalization($ident, $translations) + public function addLocalization($ident, $translations): static { if (!is_string($ident)) { throw new InvalidArgumentException(sprintf( 'Translation key must be a string, received %s', - (is_object($ident) ? get_class($ident) : gettype($ident)) + (get_debug_type($ident)) )); } @@ -205,14 +196,13 @@ public function addLocalization($ident, $translations) * * @param string $ident The message ID to remove. * @throws InvalidArgumentException If the message ID is not a string. - * @return self */ - public function removeLocalization($ident) + public function removeLocalization($ident): static { if (!is_string($ident)) { throw new InvalidArgumentException(sprintf( 'Translation key must be a string, received %s', - (is_object($ident) ? get_class($ident) : gettype($ident)) + (get_debug_type($ident)) )); } @@ -223,22 +213,18 @@ public function removeLocalization($ident) /** * Count the number of localizations. - * - * @return integer */ - public function numLocalizations() + public function numLocalizations(): int { return count($this->localizations()); } /** * Determine if there are any localizations. - * - * @return boolean */ - public function hasLocalizations() + public function hasLocalizations(): bool { - return !!$this->numLocalizations(); + return (bool) $this->numLocalizations(); } /** @@ -246,7 +232,7 @@ public function hasLocalizations() * * @return array */ - public function localizations() + public function localizations(): \ArrayIterator|array|null { if ($this->localizations === null) { $this->setLocalizations($this->defaultLocalizations()); @@ -267,15 +253,11 @@ public function localization($ident) if (!is_string($ident)) { throw new InvalidArgumentException(sprintf( 'Translation key must be a string, received %s', - (is_object($ident) ? get_class($ident) : gettype($ident)) + (get_debug_type($ident)) )); } - if (isset($this->localizations[$ident])) { - return $this->localizations[$ident]; - } - - return $ident; + return $this->localizations[$ident] ?? $ident; } /** @@ -283,7 +265,7 @@ public function localization($ident) * * @return array> */ - public function elfinderLocalizations() + public function elfinderLocalizations(): array { $i18n = []; @@ -310,7 +292,7 @@ public function elfinderLocalizationsAsJson() $options = (JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); if ($this->debug()) { - $options = ($options | JSON_PRETTY_PRINT); + $options |= JSON_PRETTY_PRINT; } return json_encode($this->elfinderLocalizations(), $options); @@ -321,7 +303,7 @@ public function elfinderLocalizationsAsJson() * * @return string Returns a stringified JSON object, protected from Mustache rendering. */ - final public function escapedElfinderLocalizationsAsJson() + final public function escapedElfinderLocalizationsAsJson(): string { return '{{=<% %>=}}' . $this->elfinderLocalizationsAsJson() . '<%={{ }}=%>'; } @@ -342,10 +324,7 @@ public function elfinderAssetsUrl() return $this->baseUrl(static::ELFINDER_ASSETS_REL_PATH); } - /** - * @return string - */ - public function elfinderAssets() + public function elfinderAssets(): bool { return $this->showAssets; } @@ -355,16 +334,15 @@ public function elfinderAssets() * * @return string|null */ - public function elfinderCallback() + public function elfinderCallback(): string|bool { return $this->callbackIdent; } /** * @param string $url The elFinder connector AJAX URL. - * @return self */ - public function setElfinderConnectorUrl($url) + public function setElfinderConnectorUrl($url): static { $this->elfinderConnectorUrl = $url; return $this; @@ -392,7 +370,7 @@ public function prepareElfinderConnectorUrl() { $uri = $this->getElfinderConnectorUrlTemplate(); - return function ($noop, LambdaHelper $helper) use ($uri) { + return function ($noop, LambdaHelper $helper) use ($uri): null { $uri = $helper->render($uri); $this->setElfinderConnectorUrl($uri); @@ -402,15 +380,12 @@ public function prepareElfinderConnectorUrl() /** * Retrieve the elFinder connector URL template for rendering. - * - * @return string */ - protected function getElfinderConnectorUrlTemplate() + protected function getElfinderConnectorUrlTemplate(): string { $uri = 'obj_type={{ objType }}&obj_id={{ objId }}&property={{ propertyIdent }}'; - $uri = '{{# withAdminUrl }}elfinder-connector?' . $uri . '{{/ withAdminUrl }}'; - return $uri; + return '{{# withAdminUrl }}elfinder-connector?' . $uri . '{{/ withAdminUrl }}'; } /** @@ -418,7 +393,7 @@ protected function getElfinderConnectorUrlTemplate() * * @return string|null */ - public function objType() + public function objType(): string|bool|null { return $this->objType; } @@ -428,7 +403,7 @@ public function objType() * * @return string|null */ - public function objId() + public function objId(): string|bool|null { return $this->objId; } @@ -438,7 +413,7 @@ public function objId() * * @return string|null */ - public function propertyIdent() + public function propertyIdent(): string|bool|null { return $this->propertyIdent; } @@ -476,11 +451,7 @@ public function formProperty() */ public function elfinderClientConfig() { - if (empty($this->elfinderConfig['client'])) { - $settings = []; - } else { - $settings = $this->elfinderConfig['client']; - } + $settings = empty($this->elfinderConfig['client']) ? [] : $this->elfinderConfig['client']; $settings['lang'] = $this->translator()->getLocale(); @@ -493,7 +464,7 @@ public function elfinderClientConfig() $mimeTypes = []; } elseif (!is_array($mimeTypes)) { $mimeTypes = explode(',', $mimeTypes); - $mimeTypes = array_filter($mimeTypes, 'strlen'); + $mimeTypes = array_filter($mimeTypes, strlen(...)); } $settings['onlyMimes'] = $mimeTypes; @@ -517,7 +488,7 @@ public function elfinderClientConfigAsJson() $options = (JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); if ($this->debug()) { - $options = ($options | JSON_PRETTY_PRINT); + $options |= JSON_PRETTY_PRINT; } return json_encode($this->elfinderClientConfig(), $options); @@ -528,7 +499,7 @@ public function elfinderClientConfigAsJson() * * @return string Returns a stringified JSON object, protected from Mustache rendering. */ - final public function escapedElfinderClientConfigAsJson() + final public function escapedElfinderClientConfigAsJson(): string { return '{{=<% %>=}}' . $this->elfinderClientConfigAsJson() . '<%={{ }}=%>'; } @@ -539,6 +510,7 @@ final public function escapedElfinderClientConfigAsJson() * @param Container $container A dependencies container instance. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -551,7 +523,7 @@ protected function setDependencies(Container $container) * * @return array */ - protected function defaultLocalizations() + protected function defaultLocalizations(): array { $t = $this->translator(); @@ -569,13 +541,11 @@ protected function defaultLocalizations() // Templating // ========================================================================= - /** * Determine if main & secondary menu should appear as mobile in a desktop resolution. - * - * @return boolean */ - public function isFullscreenTemplate() + #[\Override] + public function isFullscreenTemplate(): bool { return false; } diff --git a/packages/admin/src/Charcoal/Admin/Template/HandlerTemplate.php b/packages/admin/src/Charcoal/Admin/Template/HandlerTemplate.php index 235eea173..b87e87717 100644 --- a/packages/admin/src/Charcoal/Admin/Template/HandlerTemplate.php +++ b/packages/admin/src/Charcoal/Admin/Template/HandlerTemplate.php @@ -14,10 +14,8 @@ class HandlerTemplate extends AdminTemplate { use HandlerAwareTrait; - /** - * @return string - */ - public function ident() + #[\Override] + public function ident(): string { return 'error'; } @@ -27,6 +25,7 @@ public function ident() * * @return string|null */ + #[\Override] public function title() { return $this->appHandler()->getSummary(); @@ -34,10 +33,9 @@ public function title() /** * Error handler response is available to all users, no login required. - * - * @return boolean */ - protected function authRequired() + #[\Override] + protected function authRequired(): bool { return false; } diff --git a/packages/admin/src/Charcoal/Admin/Template/HelpTemplate.php b/packages/admin/src/Charcoal/Admin/Template/HelpTemplate.php index f1d62f01a..1e49057ac 100644 --- a/packages/admin/src/Charcoal/Admin/Template/HelpTemplate.php +++ b/packages/admin/src/Charcoal/Admin/Template/HelpTemplate.php @@ -1,5 +1,7 @@ title === null) { diff --git a/packages/admin/src/Charcoal/Admin/Template/HomeTemplate.php b/packages/admin/src/Charcoal/Admin/Template/HomeTemplate.php index 6737361a1..ea9fe663a 100644 --- a/packages/admin/src/Charcoal/Admin/Template/HomeTemplate.php +++ b/packages/admin/src/Charcoal/Admin/Template/HomeTemplate.php @@ -1,5 +1,7 @@ translator(); @@ -51,11 +51,10 @@ public function init(RequestInterface $request) /** * @todo Implement using PSR Request object - * @return boolean */ - private function isHttps() + private function isHttps(): bool { - if (isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) === 'on') { + if (isset($_SERVER['HTTPS']) && strtolower((string) $_SERVER['HTTPS']) === 'on') { return true; } elseif (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') { return true; @@ -86,10 +85,9 @@ public function rememberMeEnabled() /** * Authentication is obviously never required for the login page. - * - * @return boolean */ - protected function authRequired() + #[\Override] + protected function authRequired(): bool { return false; } @@ -107,6 +105,7 @@ public function urlLoginAction() * * @return \Charcoal\Translator\Translation|string|null */ + #[\Override] public function title() { if ($this->title === null) { @@ -121,7 +120,8 @@ public function title() * * @return string[] */ - public function recaptchaParameters() + #[\Override] + public function recaptchaParameters(): array { $params = parent::recaptchaParameters(); $params['tabindex'] = 4; @@ -137,13 +137,11 @@ public function recaptchaParameters() // Templating // ========================================================================= - /** * Determine if main & secondary menu should appear as mobile in a desktop resolution. - * - * @return boolean */ - public function isFullscreenTemplate() + #[\Override] + public function isFullscreenTemplate(): bool { return true; } diff --git a/packages/admin/src/Charcoal/Admin/Template/LogoutTemplate.php b/packages/admin/src/Charcoal/Admin/Template/LogoutTemplate.php index 66a8f2da7..dc2801fbd 100644 --- a/packages/admin/src/Charcoal/Admin/Template/LogoutTemplate.php +++ b/packages/admin/src/Charcoal/Admin/Template/LogoutTemplate.php @@ -1,5 +1,7 @@ authenticator(); @@ -34,10 +37,9 @@ public function init(RequestInterface $request) /** * Authentication is obviously never required for the login page. - * - * @return boolean */ - protected function authRequired() + #[\Override] + protected function authRequired(): bool { return false; } @@ -62,6 +64,7 @@ public function avatarImage() * * @return \Charcoal\Translator\Translation|string|null */ + #[\Override] public function title() { if ($this->title === null) { @@ -75,13 +78,11 @@ public function title() // Templating // ========================================================================= - /** * Determine if main & secondary menu should appear as mobile in a desktop resolution. - * - * @return boolean */ - public function isFullscreenTemplate() + #[\Override] + public function isFullscreenTemplate(): bool { return true; } diff --git a/packages/admin/src/Charcoal/Admin/Template/Object/CollectionTemplate.php b/packages/admin/src/Charcoal/Admin/Template/Object/CollectionTemplate.php index d7e213814..c39856e77 100644 --- a/packages/admin/src/Charcoal/Admin/Template/Object/CollectionTemplate.php +++ b/packages/admin/src/Charcoal/Admin/Template/Object/CollectionTemplate.php @@ -42,9 +42,9 @@ class CollectionTemplate extends AdminTemplate implements /** * @param RequestInterface $request PSR-7 request. - * @return boolean */ - public function init(RequestInterface $request) + #[\Override] + public function init(RequestInterface $request): bool { parent::init($request); $this->createObjTable(); @@ -57,7 +57,8 @@ public function init(RequestInterface $request) * * @return string[] */ - protected function validDataFromRequest() + #[\Override] + protected function validDataFromRequest(): array { return array_merge([ 'obj_type' @@ -105,11 +106,7 @@ protected function createSearchWidget() $widgetData = []; } - if (isset($widgetData['type'])) { - $widgetType = $widgetData['type']; - } else { - $widgetType = SearchWidget::class; - } + $widgetType = $widgetData['type'] ?? SearchWidget::class; $widget = $this->widgetFactory()->create($widgetType); $widget->setObjType($this->objType()); @@ -127,9 +124,10 @@ protected function createSearchWidget() * * @return \Charcoal\Translator\Translation */ + #[\Override] public function title() { - if (isset($this->title)) { + if ($this->title !== null) { return $this->title; } @@ -151,7 +149,7 @@ public function title() $metadata = $model->metadata(); $objLabel = null; - if (!$objLabel && isset($metadata['admin']['lists'])) { + if (isset($metadata['admin']['lists'])) { $adminMetadata = $metadata['admin']; $listIdent = filter_input(INPUT_GET, 'collection_ident', FILTER_SANITIZE_STRING); @@ -190,11 +188,7 @@ public function title() } } - if ($hasView) { - $this->title = $model->render((string)$objLabel, $model); - } else { - $this->title = (string)$objLabel; - } + $this->title = $hasView ? $model->render((string)$objLabel, $model) : (string)$objLabel; return $this->title; } @@ -203,6 +197,7 @@ public function title() * @param Container $container DI Container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -234,15 +229,10 @@ protected function createDashboardConfig() ); } - $dashboardConfig = $adminMetadata['dashboards'][$dashboardIdent]; - - return $dashboardConfig; + return $adminMetadata['dashboards'][$dashboardIdent]; } - /** - * @return void - */ - private function createObjTable() + private function createObjTable(): void { $obj = $this->proto(); if (!$obj) { @@ -304,7 +294,7 @@ private function metadataDashboardIdent() // You've reached error. throw new Exception(sprintf( 'No default collection dashboard defined in admin metadata for %s.', - get_class($this->proto()) + $this->proto()::class )); } @@ -315,7 +305,6 @@ private function metadataDashboardIdent() protected function objAdminMetadata() { $objMetadata = $this->proto()->metadata(); - $adminMetadata = isset($objMetadata['admin']) ? $objMetadata['admin'] : []; - return $adminMetadata; + return $objMetadata['admin'] ?? []; } } diff --git a/packages/admin/src/Charcoal/Admin/Template/Object/CreateTemplate.php b/packages/admin/src/Charcoal/Admin/Template/Object/CreateTemplate.php index 4fa387bcf..4f3d8101f 100644 --- a/packages/admin/src/Charcoal/Admin/Template/Object/CreateTemplate.php +++ b/packages/admin/src/Charcoal/Admin/Template/Object/CreateTemplate.php @@ -28,13 +28,14 @@ class CreateTemplate extends AdminTemplate implements * @param RequestInterface $request PSR-7 HTTP Server Request. * @return boolean */ + #[\Override] public function init(RequestInterface $request) { $ret = parent::init($request); if ($this->obj()->id()) { $path = str_replace('object/create', 'object/edit', $request->getUri()->getPath()); - header('Location: ' . (string)$request->getUri()->withPath($path)); + header('Location: ' . $request->getUri()->withPath($path)); die(); } return $ret; @@ -45,7 +46,8 @@ public function init(RequestInterface $request) * * @return string[] */ - protected function validDataFromRequest() + #[\Override] + protected function validDataFromRequest(): array { return array_merge([ 'obj_type', @@ -58,6 +60,7 @@ protected function validDataFromRequest() * * @return \Charcoal\Translator\Translation */ + #[\Override] public function title() { if ($this->title === null) { @@ -79,7 +82,7 @@ public function title() $objType = $this->objType(); $metadata = $obj->metadata(); - if (!$title && isset($metadata['admin']['forms'])) { + if (isset($metadata['admin']['forms'])) { $adminMetadata = $metadata['admin']; $formIdent = filter_input(INPUT_GET, 'form_ident', FILTER_SANITIZE_STRING); @@ -140,6 +143,7 @@ public function title() * * @return Translation|string|null */ + #[\Override] public function subtitle() { if ($this->subtitle === null) { @@ -150,11 +154,7 @@ public function subtitle() $config = []; } - if (isset($config['subtitle'])) { - $title = $this->translator()->translation($config['subtitle']); - } else { - $title = ''; - } + $title = isset($config['subtitle']) ? $this->translator()->translation($config['subtitle']) : ''; $this->subtitle = $title; } @@ -166,6 +166,7 @@ public function subtitle() * @param Container $container DI container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -194,7 +195,7 @@ protected function createDashboardConfig() } else { throw new Exception(sprintf( 'Can not show object creation dashboard: No default create dashboard defined in admin metadata for %s', - get_class($this->obj()) + $this->obj()::class )); } } @@ -205,9 +206,7 @@ protected function createDashboardConfig() ); } - $dashboardConfig = $adminMetadata['dashboards'][$dashboardIdent]; - - return $dashboardConfig; + return $adminMetadata['dashboards'][$dashboardIdent]; } @@ -221,11 +220,11 @@ protected function objAdminMetadata() $objMetadata = $obj->metadata(); - $adminMetadata = isset($objMetadata['admin']) ? $objMetadata['admin'] : null; + $adminMetadata = $objMetadata['admin'] ?? null; if ($adminMetadata === null) { throw new Exception(sprintf( 'The object %s does not have an admin metadata.', - get_class($obj) + $obj::class )); } diff --git a/packages/admin/src/Charcoal/Admin/Template/Object/EditTemplate.php b/packages/admin/src/Charcoal/Admin/Template/Object/EditTemplate.php index 9aedc36f5..93952d1f1 100644 --- a/packages/admin/src/Charcoal/Admin/Template/Object/EditTemplate.php +++ b/packages/admin/src/Charcoal/Admin/Template/Object/EditTemplate.php @@ -27,13 +27,14 @@ class EditTemplate extends AdminTemplate implements * @param RequestInterface $request PSR-7 HTTP Server Request. * @return boolean */ + #[\Override] public function init(RequestInterface $request) { $ret = parent::init($request); if (!$this->obj()->id()) { $path = str_replace('object/edit', 'object/create', $request->getUri()->getPath()); - header('Location: ' . (string)$request->getUri()->withPath($path)); + header('Location: ' . $request->getUri()->withPath($path)); die(); } return $ret; @@ -44,7 +45,8 @@ public function init(RequestInterface $request) * * @return string[] */ - protected function validDataFromRequest() + #[\Override] + protected function validDataFromRequest(): array { return array_merge([ 'obj_type', @@ -57,6 +59,7 @@ protected function validDataFromRequest() * * @return \Charcoal\Translator\Translation */ + #[\Override] public function title() { if ($this->title === null) { @@ -79,7 +82,7 @@ public function title() $objType = $this->objType(); $metadata = $obj->metadata(); - if (!$title && isset($metadata['admin']['forms'])) { + if (isset($metadata['admin']['forms'])) { $adminMetadata = $metadata['admin']; $formIdent = filter_input(INPUT_GET, 'form_ident', FILTER_SANITIZE_STRING); @@ -128,6 +131,7 @@ public function title() * * @return Translation|string|null */ + #[\Override] public function subtitle() { if ($this->subtitle === null) { @@ -138,11 +142,7 @@ public function subtitle() $config = []; } - if (isset($config['subtitle'])) { - $title = $this->translator()->translation($config['subtitle']); - } else { - $title = ''; - } + $title = isset($config['subtitle']) ? $this->translator()->translation($config['subtitle']) : ''; $this->subtitle = $this->renderTitle($title); } @@ -154,6 +154,7 @@ public function subtitle() * @param Container $container DI container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -186,7 +187,7 @@ protected function createDashboardConfig() } else { throw new Exception(sprintf( 'No default edit dashboard defined in admin metadata for %s', - get_class($this->obj()) + $this->obj()::class )); } } @@ -197,9 +198,7 @@ protected function createDashboardConfig() ); } - $dashboardConfig = $adminMetadata['dashboards'][$dashboardIdent]; - - return $dashboardConfig; + return $adminMetadata['dashboards'][$dashboardIdent]; } /** @@ -228,11 +227,11 @@ protected function objAdminMetadata() $objMetadata = $obj->metadata(); - $adminMetadata = isset($objMetadata['admin']) ? $objMetadata['admin'] : null; + $adminMetadata = $objMetadata['admin'] ?? null; if ($adminMetadata === null) { throw new Exception(sprintf( 'The object %s does not have an admin metadata.', - get_class($obj) + $obj::class )); } diff --git a/packages/admin/src/Charcoal/Admin/Template/System/ClearCacheTemplate.php b/packages/admin/src/Charcoal/Admin/Template/System/ClearCacheTemplate.php index f1f497b92..edd83e231 100644 --- a/packages/admin/src/Charcoal/Admin/Template/System/ClearCacheTemplate.php +++ b/packages/admin/src/Charcoal/Admin/Template/System/ClearCacheTemplate.php @@ -38,10 +38,8 @@ class ClearCacheTemplate extends AdminTemplate /** * Summary of cache. - * - * @var array */ - private $cacheInfo; + private ?array $cacheInfo = null; /** * Cache service config. @@ -59,10 +57,8 @@ class ClearCacheTemplate extends AdminTemplate /** * Regular expression pattern to match a Stash / APC cache key. - * - * @var string */ - private $apcCacheKeyPattern; + private ?string $apcCacheKeyPattern = null; /** * Mustache View Engine. @@ -83,6 +79,7 @@ class ClearCacheTemplate extends AdminTemplate * * @return \Charcoal\Translator\Translation|string|null */ + #[\Override] public function title() { if ($this->title === null) { @@ -97,10 +94,11 @@ public function title() * * @return \Charcoal\Admin\Widget\SecondaryMenuWidgetInterface|null */ + #[\Override] public function secondaryMenu() { if ($this->secondaryMenu === null) { - $this->secondaryMenu = $this->createSecondaryMenu('system'); + $this->secondaryMenu = $this->createSecondaryMenu(); } return $this->secondaryMenu; @@ -108,14 +106,13 @@ public function secondaryMenu() /** * @param boolean $force Whether to reload cache information. - * @return array */ - public function cacheInfo($force = false) + public function cacheInfo($force = false): array { if ($this->cacheInfo === null || $force === true) { $flip = array_flip($this->availableCacheDrivers); - $driver = get_class($this->cache->getDriver()); - $cacheType = isset($flip['\\' . $driver]) ? $flip['\\' . $driver] : $driver; + $driver = $this->cache->getDriver()::class; + $cacheType = $flip['\\' . $driver] ?? $driver; $globalItems = $this->globalCacheItems(); $this->cacheInfo = [ @@ -150,18 +147,12 @@ private function getApcNamespace() return $this->cacheConfig['prefix']; } - /** - * @return string - */ - private function getGlobalCacheKey() + private function getGlobalCacheKey(): string { return '/::' . $this->getCacheNamespace() . '::/'; } - /** - * @return array - */ - private function globalCacheInfo() + private function globalCacheInfo(): array { if ($this->isApc()) { $cacheKey = $this->getGlobalCacheKey(); @@ -190,10 +181,7 @@ private function globalCacheItems() } } - /** - * @return string - */ - private function getPagesCacheKey() + private function getPagesCacheKey(): string { return '/::' . $this->getCacheNamespace() . '::request::|::' . $this->getCacheNamespace() . '::template::/'; } @@ -224,10 +212,7 @@ public function getTwigEngine(): ?TwigEngine return null; } - /** - * @return array - */ - private function pagesCacheInfo() + private function pagesCacheInfo(): array { if ($this->isApc()) { $cacheKey = $this->getPagesCacheKey(); @@ -243,31 +228,12 @@ private function pagesCacheInfo() } } - /** - * @return array - */ - private function pagesCacheItems() - { - if ($this->isApc()) { - $cacheKey = $this->getPagesCacheKey(); - return $this->apcCacheItems($cacheKey); - } else { - return []; - } - } - - /** - * @return string - */ - private function getObjectsCacheKey() + private function getObjectsCacheKey(): string { return '/::' . $this->getCacheNamespace() . '::object::|::' . $this->getCacheNamespace() . '::metadata::/'; } - /** - * @return array - */ - private function objectsCacheInfo() + private function objectsCacheInfo(): array { if ($this->isApc()) { $cacheKey = $this->getObjectsCacheKey(); @@ -289,7 +255,7 @@ private function objectsCacheInfo() private function twigCacheInfo(): array { $engine = $this->getTwigEngine(); - if (!$engine) { + if (!$engine instanceof \Charcoal\View\Twig\TwigEngine) { return [ 'num_entries' => 0, 'total_size' => 0, @@ -298,9 +264,7 @@ private function twigCacheInfo(): array } $defaultCachePath = realpath($engine->cache()); - $cachePath = $defaultCachePath - ? $defaultCachePath - : realpath($this->appConfig['publicPath'] . DIRECTORY_SEPARATOR . $engine->cache()); + $cachePath = $defaultCachePath ?: realpath($this->appConfig['publicPath'] . DIRECTORY_SEPARATOR . $engine->cache()); if (!is_dir($cachePath)) { return [ @@ -323,7 +287,7 @@ private function twigCacheInfo(): array private function mustacheCacheInfo(): array { $engine = $this->getMustacheEngine(); - if (!$engine) { + if (!$engine instanceof \Charcoal\View\Mustache\MustacheEngine) { return [ 'num_entries' => 0, 'total_size' => 0, @@ -332,9 +296,7 @@ private function mustacheCacheInfo(): array } $defaultCachePath = realpath($engine->cache()); - $cachePath = $defaultCachePath - ? $defaultCachePath - : realpath($this->appConfig['publicPath'] . DIRECTORY_SEPARATOR . $engine->cache()); + $cachePath = $defaultCachePath ?: realpath($this->appConfig['publicPath'] . DIRECTORY_SEPARATOR . $engine->cache()); if (!is_dir($cachePath)) { return [ 'no_cache_folder' => true, @@ -362,24 +324,10 @@ private function dirSize(string $directory): int return $size; } - /** - * @return array - */ - private function objectsCacheItems() - { - if ($this->isApc()) { - $cacheKey = $this->getObjectsCacheKey(); - return $this->apcCacheItems($cacheKey); - } else { - return []; - } - } - /** * @param string $key The cache key to look at. - * @return array */ - private function apcCacheInfo($key) + private function apcCacheInfo(string $key): array { $iter = $this->createApcIterator($key); @@ -393,8 +341,8 @@ private function apcCacheInfo($key) $hitsTotal += $item['num_hits']; $ttlTotal += $item['ttl']; } - $sizeAvg = $numEntries ? ($sizeTotal / $numEntries) : 0; - $hitsAvg = $numEntries ? ($hitsTotal / $numEntries) : 0; + $sizeAvg = $numEntries !== 0 ? ($sizeTotal / $numEntries) : 0; + $hitsAvg = $numEntries !== 0 ? ($hitsTotal / $numEntries) : 0; return [ 'num_entries' => $numEntries, 'total_size' => $this->formatBytes($sizeTotal), @@ -408,7 +356,7 @@ private function apcCacheInfo($key) * @param string $key The cache key to look at. * @return array|\Generator */ - private function apcCacheItems($key) + private function apcCacheItems(string $key) { $iter = $this->createApcIterator($key); @@ -431,9 +379,8 @@ private function apcCacheItems($key) /** * @param string $key The cache item key to load. * @throws RuntimeException If the APC Iterator class is missing. - * @return \APCIterator|\APCUIterator|null */ - private function createApcIterator($key) + private function createApcIterator(string $key): \APCUIterator|\APCIterator { if (class_exists('\\APCUIterator', false)) { return new \APCUIterator($key); @@ -446,48 +393,40 @@ private function createApcIterator($key) /** * Determine if Charcoal has cache statistics. - * - * @return boolean */ - public function hasStats() + public function hasStats(): bool { return $this->isApc(); } /** * Determine if Charcoal is using the APC driver. - * - * @return boolean */ - public function isApc() + public function isApc(): bool { - return is_a($this->cache->getDriver(), Apc::class); + return $this->cache->getDriver() instanceof \Stash\Driver\Apc; } /** * Determine if Charcoal is using the Memcache driver. - * - * @return boolean */ - public function isMemcache() + public function isMemcache(): bool { - return is_a($this->cache->getDriver(), Memcache::class); + return $this->cache->getDriver() instanceof \Stash\Driver\Memcache; } /** * Determine if Charcoal is using the Ephemeral driver. - * - * @return boolean */ - public function isMemory() + public function isMemory(): bool { - return is_a($this->cache->getDriver(), Ephemeral::class); + return $this->cache->getDriver() instanceof \Stash\Driver\Ephemeral; } public function hasTwigCache(): bool { $engine = $this->getTwigEngine(); - if ($engine) { + if ($engine instanceof \Charcoal\View\Twig\TwigEngine) { return (bool)$engine->config()['useCache']; } @@ -513,10 +452,8 @@ public function hasViewCache(): bool * - `stashNS`: Stash Segment * - `poolNS`: Optional. Application Key * - `appKey`: Data Segment - * - * @return string */ - private function getApcCacheKeyPattern() + private function getApcCacheKeyPattern(): string { if ($this->apcCacheKeyPattern === null) { $pattern = '/^(?[a-f0-9]{32})::(?:(?'; @@ -556,7 +493,7 @@ private function formatApcCacheKey($key) * @param integer $bytes The number of bytes to format. * @return string */ - private function formatBytes($bytes) + private function formatBytes($bytes): int|string { if ($bytes === 0) { return 0; @@ -566,7 +503,7 @@ private function formatBytes($bytes) $base = log($bytes, 1024); $floor = floor($base); $unit = $units[$floor]; - $size = round(pow(1024, ($base - $floor)), 2); + $size = round(1024 ** ($base - $floor), 2); $locale = localeconv(); $size = number_format($size, 2, $locale['decimal_point'], $locale['thousands_sep']); @@ -585,9 +522,9 @@ private function formatBytes($bytes) * @param DateTimeInterface|null $date2 The datetime to compare against. * @return string */ - private function formatTimeDiff(DateTimeInterface $date1, DateTimeInterface $date2 = null) + private function formatTimeDiff(DateTimeInterface $date1, ?DateTimeInterface $date2 = null) { - $isNow = $date2 === null; + $isNow = !$date2 instanceof \DateTimeInterface; if ($isNow) { $date2 = new DateTime('now', $date1->getTimezone()); } @@ -626,15 +563,14 @@ private function formatTimeDiff(DateTimeInterface $date1, DateTimeInterface $dat break; } - $time = $translator->transChoice($unit, $count, [ '{{ count }}' => $count ]); - - return $time; + return $translator->transChoice($unit, $count, [ '{{ count }}' => $count ]); } /** * @param Container $container Pimple DI Container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -651,7 +587,7 @@ protected function setDependencies(Container $container) return null; }; $this->twigEngine = function () use ($container) { - if (class_exists('\Twig\Environment')) { + if (class_exists(\Twig\Environment::class)) { return $container['view/engine/twig']; } diff --git a/packages/admin/src/Charcoal/Admin/Template/System/Object/InfoTemplate.php b/packages/admin/src/Charcoal/Admin/Template/System/Object/InfoTemplate.php index a4a0f9988..05aef6464 100644 --- a/packages/admin/src/Charcoal/Admin/Template/System/Object/InfoTemplate.php +++ b/packages/admin/src/Charcoal/Admin/Template/System/Object/InfoTemplate.php @@ -23,26 +23,23 @@ class InfoTemplate extends AdminTemplate implements DashboardContainerInterface, ObjectContainerInterface { + public $metadataLoader; + public $collectionLoader; use DashboardContainerTrait; use ObjectContainerTrait; - /** - * @var array - */ - private $metadataFiles; + private ?array $metadataFiles = null; /** * @return \Charcoal\Admin\Translation|\Charcoal\Translator\Translation|string|null */ + #[\Override] public function title() { return $this->objType(); } - /** - * @return array - */ - public function objProperties() + public function objProperties(): array { $ret = []; $properties = $this->obj()->metadata()->properties(); @@ -72,10 +69,10 @@ public function objProperties() $ret[] = $property; } - usort($ret, function ($a, $b) { - $ret = strcmp($a['metadataSource'], $b['metadataSource']); + usort($ret, function (array $a, array $b): int { + $ret = strcmp((string) $a['metadataSource'], (string) $b['metadataSource']); if ($ret === 0) { - return strcmp($a['ident'], $b['ident']); + return strcmp((string) $a['ident'], (string) $b['ident']); } else { return $ret; } @@ -83,29 +80,19 @@ public function objProperties() return $ret; } - /** - * @return string - */ - public function className() + public function className(): string { - return get_class($this->obj()); + return $this->obj()::class; } - /** - * @return array - */ - public function classHierarchy() + public function classHierarchy(): array { $ret = []; $ret = array_merge($ret, array_keys(class_parents($this->obj()))); - $ret = array_reverse($ret); - return $ret; + return array_reverse($ret); } - /** - * @return array - */ - public function classTraits() + public function classTraits(): array { $traits = []; $hierarchy = $this->classHierarchy(); @@ -117,31 +104,23 @@ public function classTraits() return $traits; } - /** - * @return array - */ - public function classInterfaces() + public function classInterfaces(): array { - $reflection = new ReflectionClass(get_class($this->obj())); + $reflection = new ReflectionClass($this->obj()::class); $interfaces = array_keys($reflection->getInterfaces()); sort($interfaces); return $interfaces; } - /** - * @return array - */ - public function metadataFiles() + public function metadataFiles(): array { if ($this->metadataFiles === null) { $files = []; $reflector = new ReflectionObject($this->metadataLoader); $method = $reflector->getMethod('hierarchy'); - $method->setAccessible(true); $hierarchy = $method->invoke($this->metadataLoader, $this->objType()); $method2 = $reflector->getMethod('loadMetadataFromSource'); - $method2->setAccessible(true); foreach ($hierarchy as $source) { $ret = $method2->invoke($this->metadataLoader, $source); if (!empty($ret)) { @@ -177,7 +156,7 @@ public function sourceTable() */ public function sourceEntries() { - $this->collectionLoader->setModel(get_class($this->obj())); + $this->collectionLoader->setModel($this->obj()::class); return $this->collectionLoader->loadCount(); } @@ -186,7 +165,8 @@ public function sourceEntries() * * @return string[] */ - protected function validDataFromRequest() + #[\Override] + protected function validDataFromRequest(): array { return array_merge([ 'obj_type', 'obj_id' @@ -197,6 +177,7 @@ protected function validDataFromRequest() * @param Container $container DI container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -210,10 +191,7 @@ protected function setDependencies(Container $container) $this->collectionLoader = $container['model/collection/loader']; } - /** - * @return array - */ - protected function createDashboardConfig() + protected function createDashboardConfig(): array { return []; } @@ -234,9 +212,8 @@ private function getFirstFile($propertyIdent) /** * @param string $propertyIdent The property ident to retrieve. - * @return array */ - private function getAllFiles($propertyIdent) + private function getAllFiles($propertyIdent): array { $ret = []; $files = $this->metadataFiles(); diff --git a/packages/admin/src/Charcoal/Admin/Template/System/StaticWebsiteTemplate.php b/packages/admin/src/Charcoal/Admin/Template/System/StaticWebsiteTemplate.php index 9dadc2108..6a7920ef6 100644 --- a/packages/admin/src/Charcoal/Admin/Template/System/StaticWebsiteTemplate.php +++ b/packages/admin/src/Charcoal/Admin/Template/System/StaticWebsiteTemplate.php @@ -21,6 +21,7 @@ class StaticWebsiteTemplate extends AdminTemplate * * @return \Charcoal\Translator\Translation|string|null */ + #[\Override] public function title() { if ($this->title === null) { @@ -35,19 +36,17 @@ public function title() * * @return \Charcoal\Admin\Widget\SecondaryMenuWidgetInterface|null */ + #[\Override] public function secondaryMenu() { if ($this->secondaryMenu === null) { - $this->secondaryMenu = $this->createSecondaryMenu('system'); + $this->secondaryMenu = $this->createSecondaryMenu(); } return $this->secondaryMenu; } - /** - * @return boolean - */ - public function isStaticWebsiteEnabled() + public function isStaticWebsiteEnabled(): bool { return file_exists($this->basePath . DIRECTORY_SEPARATOR . '/www/static'); } @@ -65,7 +64,7 @@ public function staticWebsiteFiles() 'size' => $this->formatBytes(filesize($file)), 'mtime' => date(DATE_ATOM, filemtime($file)), 'generated' => date('Y-m-d H:i:s', filemtime($file)), - 'type' => pathinfo($file, PATHINFO_EXTENSION) + 'type' => pathinfo((string) $file, PATHINFO_EXTENSION) ]; } } @@ -74,6 +73,7 @@ public function staticWebsiteFiles() * @param Container $container Pimple DI Container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -84,9 +84,8 @@ protected function setDependencies(Container $container) * Human-readable bytes format. * * @param integer $size The number of bytes to format. - * @return boolean */ - private function formatBytes($size) + private function formatBytes(int|bool $size): int|string { if ($size === 0) { return 0; @@ -95,7 +94,7 @@ private function formatBytes($size) $suffixes = [ 'bytes', 'k', 'M', 'G', 'T' ]; $floor = floor($base); - return round(pow(1024, ($base - $floor)), 2) . ' ' . $suffixes[$floor]; + return round(1024 ** ($base - $floor), 2) . ' ' . $suffixes[$floor]; } /** @@ -104,7 +103,7 @@ private function formatBytes($size) * @param integer $flags Glob flags. * @return array */ - private function globRecursive($dir, $pattern, $flags = 0) + private function globRecursive(string $dir, string $pattern, $flags = 0): array|false { $files = glob($dir . '/' . $pattern, $flags); foreach (glob($dir . '/*', (GLOB_ONLYDIR | GLOB_NOSORT)) as $dir) { diff --git a/packages/admin/src/Charcoal/Admin/Template/System/UserPermissionsTemplate.php b/packages/admin/src/Charcoal/Admin/Template/System/UserPermissionsTemplate.php index 633ae4243..745d61a8c 100644 --- a/packages/admin/src/Charcoal/Admin/Template/System/UserPermissionsTemplate.php +++ b/packages/admin/src/Charcoal/Admin/Template/System/UserPermissionsTemplate.php @@ -26,9 +26,9 @@ class UserPermissionsTemplate extends AdminTemplate implements /** * @param RequestInterface $request PSR-7 request. - * @return boolean */ - public function init(RequestInterface $request) + #[\Override] + public function init(RequestInterface $request): bool { parent::init($request); @@ -42,17 +42,15 @@ public function init(RequestInterface $request) * * @return string[] */ - protected function validDataFromRequest() + #[\Override] + protected function validDataFromRequest(): array { return array_merge([ 'obj_type' ], parent::validDataFromRequest()); } - /** - * @return void - */ - private function createObjTable() + private function createObjTable(): void { $obj = $this->modelFactory()->create('charcoal/admin/user/permission'); if ($obj->source()->tableExists() === false) { @@ -70,15 +68,13 @@ private function createObjTable() /** * @return \Charcoal\Translator\Translation */ - public function title() + #[\Override] + public function title(): ?\Charcoal\Translator\Translation { return $this->translator()->translation('Administrator Permissions'); } - /** - * @return mixed - */ - public function createDashboardConfig() + public function createDashboardConfig(): array { return [ 'layout' => [ @@ -99,6 +95,7 @@ public function createDashboardConfig() * @param Container $container Pimple DI Container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); diff --git a/packages/admin/src/Charcoal/Admin/Template/System/UserRolesTemplate.php b/packages/admin/src/Charcoal/Admin/Template/System/UserRolesTemplate.php index cf457c235..adadb1a71 100644 --- a/packages/admin/src/Charcoal/Admin/Template/System/UserRolesTemplate.php +++ b/packages/admin/src/Charcoal/Admin/Template/System/UserRolesTemplate.php @@ -25,7 +25,8 @@ class UserRolesTemplate extends AdminTemplate implements * * @return string[] */ - protected function validDataFromRequest() + #[\Override] + protected function validDataFromRequest(): array { return array_merge([ 'obj_type' @@ -35,15 +36,13 @@ protected function validDataFromRequest() /** * @return \Charcoal\Translator\Translation */ - public function title() + #[\Override] + public function title(): ?\Charcoal\Translator\Translation { return $this->translator()->translation('Administrator Roles'); } - /** - * @return mixed - */ - public function createDashboardConfig() + public function createDashboardConfig(): array { return [ 'layout' => [ @@ -64,6 +63,7 @@ public function createDashboardConfig() * @param Container $container Pimple DI Container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); diff --git a/packages/admin/src/Charcoal/Admin/Template/System/UsersTemplate.php b/packages/admin/src/Charcoal/Admin/Template/System/UsersTemplate.php index 5e83f09d1..6395fd450 100644 --- a/packages/admin/src/Charcoal/Admin/Template/System/UsersTemplate.php +++ b/packages/admin/src/Charcoal/Admin/Template/System/UsersTemplate.php @@ -27,7 +27,8 @@ class UsersTemplate extends AdminTemplate implements * * @return string[] */ - protected function validDataFromRequest() + #[\Override] + protected function validDataFromRequest(): array { return array_merge([ 'obj_type' @@ -39,15 +40,13 @@ protected function validDataFromRequest() * * @return \Charcoal\Translator\Translation|string|null */ - public function title() + #[\Override] + public function title(): ?\Charcoal\Translator\Translation { return $this->translator()->translation('Administrators'); } - /** - * @return mixed - */ - public function createDashboardConfig() + public function createDashboardConfig(): array { return [ 'layout' => [ @@ -68,6 +67,7 @@ public function createDashboardConfig() * @param Container $container Pimple DI Container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); diff --git a/packages/admin/src/Charcoal/Admin/Ui/ActionContainerTrait.php b/packages/admin/src/Charcoal/Admin/Ui/ActionContainerTrait.php index b0fa09721..020794449 100644 --- a/packages/admin/src/Charcoal/Admin/Ui/ActionContainerTrait.php +++ b/packages/admin/src/Charcoal/Admin/Ui/ActionContainerTrait.php @@ -38,7 +38,7 @@ trait ActionContainerTrait * to determine if any renderables should be processed. * @return array Returns a collection of parsed actions. */ - protected function parseActions(array $actions, $renderer = false) + protected function parseActions(array $actions, $renderer = false): array { $this->actionsPriority = $this->defaultActionPriority(); @@ -63,7 +63,7 @@ protected function parseActions(array $actions, $renderer = false) } } - usort($parsedActions, [ 'Charcoal\Admin\Support\Sorter', 'sortByPriority' ]); + usort($parsedActions, \Charcoal\Admin\Support\Sorter::sortByPriority(...)); while (($first = reset($parsedActions)) && $first['isSeparator']) { array_shift($parsedActions); @@ -82,7 +82,7 @@ protected function parseActions(array $actions, $renderer = false) * @param array ...$params Variable list of actions to merge. * @return array Returns a collection of merged actions. */ - protected function mergeActions(array ...$params) + protected function mergeActions(array ...$params): array { $unique = []; foreach ($params as $actions) { @@ -96,15 +96,11 @@ protected function mergeActions(array ...$params) $action['ident'] = $ident; $hasActions = (isset($action['actions']) && is_array($action['actions'])); - if ($hasActions) { - $action['actions'] = $this->mergeActions($action['actions']); - } else { - $action['actions'] = []; - } + $action['actions'] = $hasActions ? $this->mergeActions($action['actions']) : []; if (isset($unique[$ident])) { if (static::compareActions($action, $unique[$ident])) { - if ($hasActions && !!$unique[$ident]['actions']) { + if ($hasActions && (bool) $unique[$ident]['actions']) { $action['actions'] = $this->mergeActions( $unique[$ident]['actions'], $action['actions'] @@ -113,7 +109,7 @@ protected function mergeActions(array ...$params) } $unique[$ident] = array_replace($unique[$ident], $action); } else { - if ($hasActions && !!$unique[$ident]['actions']) { + if ($hasActions && (bool) $unique[$ident]['actions']) { $unique[$ident]['actions'] = $this->mergeActions( $unique[$ident]['actions'], $action['actions'] @@ -138,13 +134,9 @@ protected function mergeActions(array ...$params) * @param mixed $action The action structure. * @return string Resolved action identifier. */ - protected function parseActionIdent($ident, $action) + protected function parseActionIdent($ident, array $action) { - if (isset($action['ident'])) { - return $action['ident']; - } - - return $ident; + return $action['ident'] ?? $ident; } /** @@ -176,11 +168,9 @@ protected function parseActionItem($action, $ident, $renderer = false) $action['ident'] = $ident; } - if (isset($action['buttonType'])) { - if (!in_array($action['buttonType'], $buttonTypes)) { - $action['actionType'] = $action['buttonType']; - $action['buttonType'] = 'button'; - } + if (isset($action['buttonType']) && !in_array($action['buttonType'], $buttonTypes)) { + $action['actionType'] = $action['buttonType']; + $action['buttonType'] = 'button'; } if (!isset($action['actionType'])) { @@ -232,21 +222,17 @@ protected function parseActionItem($action, $ident, $renderer = false) } if (isset($action['dataAttributes']) && is_array($action['dataAttributes'])) { - $action['dataAttributes'] = array_filter($action['dataAttributes'], function ($attribute) { - return !empty($attribute['key']) && - is_string($attribute['key']) && - !empty($attribute['value']) && - is_string($attribute['value']); - }); + $action['dataAttributes'] = array_filter($action['dataAttributes'], fn(array $attribute): bool => !empty($attribute['key']) && + is_string($attribute['key']) && + !empty($attribute['value']) && + is_string($attribute['value'])); } else { $action['dataAttributes'] = []; } if (isset($action['actions']) && is_array($action['actions'])) { $action['actions'] = $this->parseActions($action['actions']); - $action['hasActions'] = !!array_filter($action['actions'], function ($action) { - return $action['active']; - }); + $action['hasActions'] = (bool) array_filter($action['actions'], fn(array $action): mixed => $action['active']); } else { $action['actions'] = []; $action['hasActions'] = false; @@ -267,41 +253,25 @@ protected function parseActionItem($action, $ident, $renderer = false) * Resolve the action's type. * * @param mixed $action The action structure. - * @return string */ - protected function resolveActionType($action) + protected function resolveActionType(array $action): string { - switch ($action['ident']) { - case 'create': - case 'save': - case 'submit': - case 'update': - case 'edit': - return 'primary'; - - case 'reset': - return 'warning'; - - case 'delete': - return 'danger'; - - default: - return 'dark'; - } + return match ($action['ident']) { + 'create', 'save', 'submit', 'update', 'edit' => 'primary', + 'reset' => 'warning', + 'delete' => 'danger', + default => 'dark', + }; } /** * Fetch a viewable instance to process an action's renderables. - * - * @return ViewableInterface|null */ - protected function getActionRenderer() + protected function getActionRenderer(): ?\Charcoal\View\ViewableInterface { $obj = null; - if ($this instanceof FormSidebarInterface) { - if ($this->form()) { - $obj = $this->form()->obj(); - } + if ($this instanceof FormSidebarInterface && $this->form()) { + $obj = $this->form()->obj(); } if ($this instanceof ObjectContainerInterface) { @@ -309,7 +279,7 @@ protected function getActionRenderer() } if ($this instanceof CollectionContainerInterface) { - $obj = isset($this->currentObj) ? $this->currentObj : $this->proto(); + $obj = $this->currentObj ?? $this->proto(); } if (($obj instanceof ViewableInterface) && ($obj->view() instanceof ViewInterface)) { @@ -328,7 +298,7 @@ protected function getActionRenderer() * @throws RuntimeException If a renderer is unavailable. * @return array Resolved action structure. */ - protected function parseActionRenderables($action, $renderer) + protected function parseActionRenderables(array $action, $renderer): array { if ($renderer === false) { return $action; @@ -379,13 +349,13 @@ protected function parseActionCondition($condition, $action = null, $renderer = $result = null; if ($renderer && is_callable([ $renderer, $condition ])) { - $result = !!$renderer->{$condition}(); + $result = (bool) $renderer->{$condition}(); } elseif (is_callable([ $this, $condition ])) { - $result = !!$this->{$condition}(); + $result = (bool) $this->{$condition}(); } elseif (is_callable($condition)) { - $result = !!$condition(); + $result = (bool) $condition(); } elseif ($renderer) { - $result = !!$renderer->renderTemplate($condition); + $result = (bool) $renderer->renderTemplate($condition); } if ($result !== null) { @@ -418,7 +388,7 @@ protected function parseActionUrl($url, $action = null, $renderer = null) $url = trim($url); - if (empty($url) && !is_numeric($url)) { + if (($url === '' || $url === '0') && !is_numeric($url)) { return '#'; } @@ -432,10 +402,10 @@ protected function parseActionUrl($url, $action = null, $renderer = null) /** @todo Shame! Force `{{ type }}` to use "obj_type" GET parameter… */ $objType = filter_input(INPUT_GET, 'obj_type', FILTER_SANITIZE_STRING); if ($objType) { - $url = preg_replace('~\{\{\s*(obj_)?type\s*\}\}~', $objType, $url); + $url = preg_replace('~\{\{\s*(obj_)?type\s*\}\}~', $objType, (string) $url); } - if ($url && strpos($url, ':') === false && !in_array($url[0], [ '/', '#', '?' ])) { + if ($url && !str_contains($url, ':') && !in_array($url[0], [ '/', '#', '?' ])) { $url = $this->adminUrl() . $url; } @@ -443,7 +413,7 @@ protected function parseActionUrl($url, $action = null, $renderer = null) } elseif ($renderer instanceof ViewableInterface) { $url = $renderer->renderTemplate($url); - if ($url && strpos($url, ':') === false && !in_array($url[0], [ '/', '#', '?' ])) { + if ($url && !str_contains($url, ':') && !in_array($url[0], [ '/', '#', '?' ])) { $url = $this->adminUrl() . $url; } } @@ -479,10 +449,8 @@ protected function parseActionCssClasses($classes, $action = null, $renderer = n /** * Retrieve the default action structure. - * - * @return array */ - protected function defaultActionStruct() + protected function defaultActionStruct(): array { return [ 'ident' => null, @@ -533,10 +501,10 @@ protected function defaultActionPriority() * @param array $b Second action object to sort. * @return boolean Returns TRUE if $a has priority. Otherwise, FALSE for $b. */ - protected function compareActions(array $a, array $b) + protected function compareActions(array $a, array $b): bool { - $a = isset($a['priority']) ? $a['priority'] : 0; - $b = isset($b['priority']) ? $b['priority'] : 0; + $a = $a['priority'] ?? 0; + $b = $b['priority'] ?? 0; $c = isset($action['isSubmittable']) && $action['isSubmittable']; return ($c || ($a === 0) || ($a >= $b)); diff --git a/packages/admin/src/Charcoal/Admin/Ui/CollectionContainerInterface.php b/packages/admin/src/Charcoal/Admin/Ui/CollectionContainerInterface.php index b5a47967f..c6a4ad298 100644 --- a/packages/admin/src/Charcoal/Admin/Ui/CollectionContainerInterface.php +++ b/packages/admin/src/Charcoal/Admin/Ui/CollectionContainerInterface.php @@ -1,5 +1,7 @@ modelFactory === null) { throw new Exception(sprintf( 'Model Factory is not defined for "%s"', - get_class($this) + $this::class )); } @@ -216,7 +216,7 @@ private function propertyDisplayFactory() if ($this->propertyDisplayFactory === null) { throw new Exception(sprintf( 'Property display factory is not defined for "%s"', - get_class($this) + $this::class )); } @@ -251,10 +251,8 @@ protected function collectionLoader() /** * Create a collection loader. - * - * @return CollectionLoader */ - protected function createCollectionLoader() + protected function createCollectionLoader(): \Charcoal\Loader\CollectionLoader { return new CollectionLoader([ 'logger' => $this->logger, @@ -271,14 +269,14 @@ protected function createCollectionLoader() * @param array|null $data Optional collection data. * @return void */ - protected function configureCollectionLoader(CollectionLoader $loader, array $data = null) + protected function configureCollectionLoader(CollectionLoader $loader, ?array $data = null) { - $objType = $this->getObjTypeOrFail(); + $this->getObjTypeOrFail(); $loader->setModel($this->proto()); $config = $this->collectionConfig(); - if (is_array($config) && !empty($config)) { + if (is_array($config) && $config !== []) { unset($config['properties']); $loader->setData($config); } @@ -328,7 +326,7 @@ public function getObjTypeOrFail() if (!$objType) { throw new UnexpectedValueException(sprintf( '%1$s cannot create collection. Object type is not defined.', - get_class($this) + $this::class )); } @@ -475,13 +473,10 @@ public function mergeCollectionConfig(array $config) * Stub: Parse given parameters into the collection's config set. * * @param array $config New collection config values. - * @return array */ - protected function parseCollectionConfig(array $config) + protected function parseCollectionConfig(array $config): array { - return array_filter($config, function ($val) { - return !empty($val) || is_numeric($val); - }); + return array_filter($config, fn($val): bool => !empty($val) || is_numeric($val)); } /** @@ -510,10 +505,7 @@ protected function createCollectionConfig() return $this->collectionMetadata(); } - /** - * @return boolean - */ - public function hasPagination() + public function hasPagination(): bool { return ($this->pagination() instanceof Pagination); } @@ -549,10 +541,9 @@ public function setPagination($pagination) /** * @return PaginationInterface */ - protected function createPagination() + protected function createPagination(): \Charcoal\Source\Pagination { - $pagination = new Pagination(); - return $pagination; + return new Pagination(); } /** @@ -574,7 +565,7 @@ public function numPerPage() /** * @return integer */ - public function numPages() + public function numPages(): int|float { if ($this->numPerPage() === 0) { return 0; @@ -583,10 +574,7 @@ public function numPages() return ceil($this->numTotal() / $this->numPerPage()); } - /** - * @return boolean - */ - public function hasFilters() + public function hasFilters(): bool { return count($this->filters()) > 0; } @@ -630,16 +618,12 @@ public function setFilters($filters) /** * @return FilterInterface */ - protected function createFilter() + protected function createFilter(): \Charcoal\Source\Filter { - $filter = new Filter(); - return $filter; + return new Filter(); } - /** - * @return boolean - */ - public function hasOrders() + public function hasOrders(): bool { return count($this->orders()) > 0; } @@ -683,10 +667,9 @@ public function setOrders($orders) /** * @return OrderInterface */ - protected function createOrder() + protected function createOrder(): \Charcoal\Source\Order { - $order = new Order(); - return $order; + return new Order(); } /** @@ -717,15 +700,13 @@ public function collection() * @throws Exception If the object type of the colletion has not been set. * @return ModelInterface[] */ - public function createCollection(array $data = null) + public function createCollection(?array $data = null) { - $objType = $this->getObjTypeOrFail(); + $this->getObjTypeOrFail(); $loader = $this->collectionLoader(); $this->configureCollectionLoader($loader, $data); - - $collection = $loader->load(); - return $collection; + return $loader->load(); } /** @@ -764,10 +745,8 @@ public function setProperties($properties) * Prepares and returns the properties. * * This method should be overriden in the class implementing this trait. - * - * @return array */ - public function properties() + public function properties(): array { return []; } @@ -788,10 +767,8 @@ public function sortProperties() * Retrieve the property customizations for the collection. * * This method should be overriden in the class implementing this trait. - * - * @return array */ - public function propertiesOptions() + public function propertiesOptions(): array { return []; } @@ -811,10 +788,8 @@ public function objectRows() // Go through each object to generate an array of properties listed in object's list metadata foreach ($objects as $object) { - if (isset($object['requiredAclPermissions']) && !empty($object['requiredAclPermissions'])) { - if ($this->hasPermissions($object['requiredAclPermissions']) === false) { - continue; - } + if (isset($object['requiredAclPermissions']) && !empty($object['requiredAclPermissions']) && $this->hasPermissions($object['requiredAclPermissions']) === false) { + continue; } $objectProperties = []; @@ -891,13 +866,12 @@ protected function setupDisplayPropertyValue(ModelInterface $object, PropertyInt * @param ModelInterface $object The current row's object. * @param PropertyInterface $property The current property. * @param string $propertyValue The property $key's display value. - * @return array */ protected function parsePropertyCell( ModelInterface $object, PropertyInterface $property, $propertyValue - ) { + ): array { unset($object); return [ @@ -913,9 +887,8 @@ protected function parsePropertyCell( * * @param ModelInterface $object The current row's object. * @param array $objectProperties The $object's display properties. - * @return array */ - protected function parseObjectRow(ModelInterface $object, array $objectProperties) + protected function parseObjectRow(ModelInterface $object, array $objectProperties): array { return [ 'object' => $object, @@ -925,18 +898,12 @@ protected function parseObjectRow(ModelInterface $object, array $objectPropertie ]; } - /** - * @return boolean - */ - public function hasObjects() + public function hasObjects(): bool { return ($this->numObjects() > 0); } - /** - * @return integer - */ - public function numObjects() + public function numObjects(): int { return count($this->objects()); } @@ -978,7 +945,7 @@ public function objLabels() $objMetadata = $proto->metadata(); if (isset($objMetadata['labels']) && !empty($objMetadata['labels'])) { $objLabels = $objMetadata['labels']; - array_walk($objLabels, function (&$value) { + array_walk($objLabels, function (&$value): void { $value = $this->translator()->translation($value); }); $this->objLabels = $objLabels; @@ -1000,7 +967,7 @@ public function proto($reload = false) if ($objType === null) { throw new InvalidArgumentException(sprintf( '%s Can not create an object prototype: object type is null.', - get_class($this) + $this::class )); } $this->proto = $this->modelFactory()->create($objType); @@ -1042,7 +1009,7 @@ protected function isObjRenderable($obj, $toString = false) return false; } - $key = get_class($obj); + $key = $obj::class; if (isset(static::$objRenderableCache[$key])) { return static::$objRenderableCache[$key]; diff --git a/packages/admin/src/Charcoal/Admin/Ui/DashboardContainerInterface.php b/packages/admin/src/Charcoal/Admin/Ui/DashboardContainerInterface.php index eb5353d44..143e4f6f4 100644 --- a/packages/admin/src/Charcoal/Admin/Ui/DashboardContainerInterface.php +++ b/packages/admin/src/Charcoal/Admin/Ui/DashboardContainerInterface.php @@ -1,5 +1,7 @@ defaultDashboardType(); } - $dashboard = $this->dashboardBuilder->build($dashboardConfig); - - return $dashboard; + return $this->dashboardBuilder->build($dashboardConfig); } /** * Retrieve the default dashboard type class name. - * - * @return string */ - public function defaultDashboardType() + public function defaultDashboardType(): string { return DashboardWidget::class; } diff --git a/packages/admin/src/Charcoal/Admin/Ui/FeedbackContainerTrait.php b/packages/admin/src/Charcoal/Admin/Ui/FeedbackContainerTrait.php index 8eec4f6ae..469c928a6 100644 --- a/packages/admin/src/Charcoal/Admin/Ui/FeedbackContainerTrait.php +++ b/packages/admin/src/Charcoal/Admin/Ui/FeedbackContainerTrait.php @@ -33,20 +33,16 @@ public function clearFeedback() /** * Determine if there's feedback. - * - * @return boolean */ - public function hasFeedbacks() + public function hasFeedbacks(): bool { return ($this->numFeedbacks() > 0); } /** * Count feedback. - * - * @return integer */ - public function numFeedbacks() + public function numFeedbacks(): int { return count($this->feedbacks()); } @@ -95,7 +91,7 @@ public function addFeedback($level, $message = null) $entry = $level; } elseif (is_string($level) && is_array($message)) { $entry = $message; - $entry['level'] = (string)$level; + $entry['level'] = $level; } else { $entry = [ 'level' => (string)$level, @@ -184,7 +180,7 @@ public function addFeedbackFromValidatorResult(ValidatorResult $result) * * @return string[] */ - public function getSupportedValidatorLevelsForFeedback() + public function getSupportedValidatorLevelsForFeedback(): array { return [ ValidatorInterface::ERROR, @@ -229,7 +225,7 @@ final protected function parseFeedback(array $entry) * @throws InvalidArgumentException If the feedback entry is invalid. * @return array A parsed feedback entry. */ - protected function parseFeedbackEntry(array $entry) + protected function parseFeedbackEntry(array $entry): array { $entry['message'] = (string)$entry['message']; @@ -247,7 +243,7 @@ protected function parseFeedbackEntry(array $entry) * * @return string A unique feedback entry ID. */ - protected function generateFeedbackEntryId() + protected function generateFeedbackEntryId(): string { return uniqid(); } @@ -258,7 +254,7 @@ protected function generateFeedbackEntryId() * @param string $level The feedback level. * @return boolean Whether the level is dismissable (TRUE) or not (FALSE). */ - protected function isFeedbackDismissable($level) + protected function isFeedbackDismissable($level): bool { return in_array($level, [ 'log', 'debug', 'info', 'notice' ]); } @@ -271,25 +267,13 @@ protected function isFeedbackDismissable($level) */ protected function resolveFeedbackType($level) { - switch ($level) { - case 'emergency': - case 'alert': - case 'critical': - case 'error': - return 'danger'; - - case 'debug': - return 'warning'; - - case 'notice': - case 'log': - return 'info'; - - case 'done': - return 'success'; - } - - return $level; + return match ($level) { + 'emergency', 'alert', 'critical', 'error' => 'danger', + 'debug' => 'warning', + 'notice', 'log' => 'info', + 'done' => 'success', + default => $level, + }; } /** @@ -300,24 +284,12 @@ protected function resolveFeedbackType($level) */ protected function resolveFeedbackLevel($level) { - switch ($level) { - case 'emergency': - case 'alert': - case 'critical': - case 'danger': - return 'error'; - - case 'debug': - return 'warning'; - - case 'notice': - case 'log': - return 'info'; - - case 'done': - return 'success'; - } - - return $level; + return match ($level) { + 'emergency', 'alert', 'critical', 'danger' => 'error', + 'debug' => 'warning', + 'notice', 'log' => 'info', + 'done' => 'success', + default => $level, + }; } } diff --git a/packages/admin/src/Charcoal/Admin/Ui/FormGroupInterface.php b/packages/admin/src/Charcoal/Admin/Ui/FormGroupInterface.php index 0b917e79a..d7a424a3f 100644 --- a/packages/admin/src/Charcoal/Admin/Ui/FormGroupInterface.php +++ b/packages/admin/src/Charcoal/Admin/Ui/FormGroupInterface.php @@ -1,5 +1,7 @@ translator()->getLocale(); $locales = $this->translator()->locales(); $languages = []; - uasort($locales, [ 'Charcoal\Admin\Support\Sorter', 'sortByPriority' ]); + uasort($locales, \Charcoal\Admin\Support\Sorter::sortByPriority(...)); foreach ($locales as $locale => $localeStruct) { /** @@ -48,11 +47,7 @@ public function languages() $label = $this->translator()->translation($localeStruct['name']); } else { $trans = 'locale.' . $locale; - if ($trans === $this->translator()->trans($trans)) { - $label = strtoupper($locale); - } else { - $label = $this->translator()->translation($trans); - } + $label = $trans === $this->translator()->trans($trans) ? strtoupper((string) $locale) : $this->translator()->translation($trans); } $isCurrent = ($locale === $currentLocale); diff --git a/packages/admin/src/Charcoal/Admin/Ui/ImageAttributesTrait.php b/packages/admin/src/Charcoal/Admin/Ui/ImageAttributesTrait.php index e70410ff5..362acf3ee 100644 --- a/packages/admin/src/Charcoal/Admin/Ui/ImageAttributesTrait.php +++ b/packages/admin/src/Charcoal/Admin/Ui/ImageAttributesTrait.php @@ -90,7 +90,7 @@ public function styleAttr() * * @return string */ - protected function defaultStyleAttr() + protected function defaultStyleAttr(): array { return [ 'height' => $this->height(), @@ -104,12 +104,11 @@ protected function defaultStyleAttr() * Parse the CSS styling declarations from the property's display features. * * @param string[] $styles An associative array of CSS styles. - * @return string */ - protected function parseStyleAttr(array $styles) + protected function parseStyleAttr(array $styles): string { $inline = array_map( - function ($val, $key) { + function (int|string $val, int|string $key) { if (is_bool($val)) { return ($val) ? $key : ''; } elseif (isset($val)) { @@ -278,10 +277,9 @@ public function maxHeight() * Determine if the value is a {@see @see http://en.wikipedia.org/wiki/Data_URI_scheme Data URI}. * * @param string $val A path or URI to analyze. - * @return boolean */ - protected function isDataUri($val) + protected function isDataUri($val): bool { - return (0 === strpos($val, 'data:')); + return (str_starts_with($val, 'data:')); } } diff --git a/packages/admin/src/Charcoal/Admin/Ui/LanguageSwitcherAwareInterface.php b/packages/admin/src/Charcoal/Admin/Ui/LanguageSwitcherAwareInterface.php index 17878deb2..7d6989268 100644 --- a/packages/admin/src/Charcoal/Admin/Ui/LanguageSwitcherAwareInterface.php +++ b/packages/admin/src/Charcoal/Admin/Ui/LanguageSwitcherAwareInterface.php @@ -1,5 +1,7 @@ getWidget(); $this->setDynamicTemplate('widget_template', $widget->template()); @@ -183,12 +183,10 @@ public function widgetData($key = null, $default = null) if ($key) { if (isset($this->widgetData[$key])) { return $this->widgetData[$key]; + } elseif (!is_string($default) && is_callable($default)) { + return $default(); } else { - if (!is_string($default) && is_callable($default)) { - return $default(); - } else { - return $default; - } + return $default; } } @@ -197,10 +195,8 @@ public function widgetData($key = null, $default = null) /** * Retrieve the default nested widget options. - * - * @return array */ - public function defaultWidgetData() + public function defaultWidgetData(): array { return []; } @@ -246,7 +242,7 @@ public function setRenderableData(array $data) * @throws RuntimeException If the form doesn't have a model. * @return array|Traversable The rendered data. */ - protected function renderDataRecursive($data) + protected function renderDataRecursive($data): \Traversable|array { if (!is_array($data) && !($data instanceof Traversable)) { throw new InvalidArgumentException('The renderable data must be iterable.'); @@ -262,7 +258,7 @@ protected function renderDataRecursive($data) foreach ($data as $key => $val) { if (is_string($val)) { $data[$key] = $this->renderData($val); - } elseif (is_array($val) || ($val instanceof Traversable)) { + } elseif (is_iterable($val)) { $data[$key] = $this->renderDataRecursive($val); } else { continue; @@ -278,7 +274,7 @@ protected function renderDataRecursive($data) * @param string $data The data to render. * @return string The rendered data. */ - protected function renderData($data) + protected function renderData($data): string|array|null { $obj = $this->form()->obj(); @@ -300,14 +296,14 @@ protected function renderData($data) * @throws InvalidArgumentException If a route token is not a string. * @return string */ - private function parseDataToken($token) + private function parseDataToken($token): string|float|int { // Processes matches from a regular expression operation if (is_array($token) && isset($token[1])) { $token = $token[1]; } - $token = trim($token); + $token = trim((string) $token); $method = [ $this, $token ]; if (is_callable($method)) { @@ -324,8 +320,8 @@ private function parseDataToken($token) throw new InvalidArgumentException(sprintf( 'Data token "%1$s" must be a string with %2$s; received %3$s', $token, - get_called_class(), - (is_object($value) ? get_class($value) : gettype($value)) + static::class, + (get_debug_type($value)) )); } diff --git a/packages/admin/src/Charcoal/Admin/Ui/ObjectContainerInterface.php b/packages/admin/src/Charcoal/Admin/Ui/ObjectContainerInterface.php index 184e2ecf4..220a0dc09 100644 --- a/packages/admin/src/Charcoal/Admin/Ui/ObjectContainerInterface.php +++ b/packages/admin/src/Charcoal/Admin/Ui/ObjectContainerInterface.php @@ -1,5 +1,7 @@ modelFactory === null) { throw new Exception(sprintf( 'Model factory not set for %s', - get_class($this) + $this::class )); } return $this->modelFactory; @@ -94,7 +93,7 @@ public function setObjType($objType) if (!is_string($objType)) { throw new InvalidArgumentException(sprintf( 'Object type must be a string, received %s.', - (is_object($objType) ? get_class($objType) : gettype($objType)) + (get_debug_type($objType)) )); } @@ -125,7 +124,7 @@ public function setObjId($objId) if (!is_scalar($objId)) { throw new InvalidArgumentException(sprintf( 'Object ID must be a string or numerical value, received %s.', - (is_object($objId) ? get_class($objId) : gettype($objId)) + (get_debug_type($objId)) )); } @@ -151,7 +150,7 @@ public function objId() * * @return string Escaped ID. */ - public function objIdWithSlashes() + public function objIdWithSlashes(): string { return addslashes($this->objId()); } @@ -198,10 +197,8 @@ public function proto() /** * Determine if the class has a concrete object. - * - * @return boolean */ - public function hasObj() + public function hasObj(): bool { return ($this->obj() && $this->obj()->id()); } @@ -216,11 +213,7 @@ public function obj() if ($this->obj === null) { $this->obj = $this->createOrLoadObj(); - if ($this->obj instanceof ModelInterface) { - $this->objId = $this->obj->id(); - } else { - $this->objId = null; - } + $this->objId = $this->obj instanceof ModelInterface ? $this->obj->id() : null; } return $this->obj; @@ -255,7 +248,7 @@ protected function cloneObj() if (empty($cloneId)) { throw new Exception(sprintf( '%1$s cannot clone object. Clone ID missing from request.', - get_class($this) + $this::class )); } @@ -285,7 +278,7 @@ protected function createObjFromBluePrint() if (empty($bpId)) { throw new Exception(sprintf( '%1$s cannot create object from blueprint. Blueprint ID missing from request.', - get_class($this) + $this::class )); } @@ -320,30 +313,28 @@ protected function createObj() if ($objBaseClass) { $message = sprintf( '[%1$s] can not create object: Object type [%2$s] does not match [%3$s]', - get_class($this), + $this::class, $objType, $objBaseClass ); } else { $message = sprintf( '[%1$s] can not create object: Invalid object type [%2$s]', - get_class($this), + $this::class, $objType ); } } else { $message = sprintf( '[%1$s] can not create object: Missing object type', - get_class($this) + $this::class ); } throw new Exception($message); } - $obj = $this->modelFactory()->create($objType); - - return $obj; + return $this->modelFactory()->create($objType); } /** @@ -377,16 +368,15 @@ protected function validateObjType() } return $this->validateObjBaseClass($this->proto()); - } catch (Exception $e) { + } catch (Exception) { return false; } } /** * @param mixed $obj Object to validate. - * @return boolean */ - protected function validateObjBaseClass($obj) + protected function validateObjBaseClass($obj): bool { $objBaseClass = $this->objBaseClass(); if (!$objBaseClass) { @@ -396,7 +386,7 @@ protected function validateObjBaseClass($obj) try { return ($obj instanceof $objBaseClass); - } catch (Exception $e) { + } catch (Exception) { return false; } } @@ -409,7 +399,7 @@ protected function validateObjBaseClass($obj) */ protected function getSingularLabelFromObj(ModelInterface $obj) { - $key = get_class($obj); + $key = $obj::class; if (isset(static::$labelCache[$key])) { return static::$labelCache[$key]; @@ -425,11 +415,7 @@ protected function getSingularLabelFromObj(ModelInterface $obj) $label = null; } - if (is_array($label)) { - $label = reset($label); - } else { - $label = (new ReflectionClass($obj))->getShortName(); - } + $label = is_array($label) ? reset($label) : new ReflectionClass($obj)->getShortName(); static::$labelCache[$key] = $label; @@ -453,7 +439,7 @@ protected function isObjRenderable($obj, $toString = false) return false; } - $key = get_class($obj); + $key = $obj::class; if (isset(static::$objRenderableCache[$key])) { return static::$objRenderableCache[$key]; diff --git a/packages/admin/src/Charcoal/Admin/Ui/ObjectRevisionsInterface.php b/packages/admin/src/Charcoal/Admin/Ui/ObjectRevisionsInterface.php index 846efab13..25c7c1c1e 100644 --- a/packages/admin/src/Charcoal/Admin/Ui/ObjectRevisionsInterface.php +++ b/packages/admin/src/Charcoal/Admin/Ui/ObjectRevisionsInterface.php @@ -1,5 +1,7 @@ latestRevision(); $propLabel = '%2$s'; - $callback = function (ObjectRevisionInterface &$revision) use ($lastRevision, $obj, $propLabel) { + $callback = function (ObjectRevisionInterface &$revision) use ($lastRevision, $obj, $propLabel): void { $dataDiff = $revision['dataDiff']; $revision->revTsDisplay = $revision['revTs']->format('Y-m-d H:i:s'); $revision->revUserDisplay = $revision->p('revUser')->displayVal($revision['revUser']); diff --git a/packages/admin/src/Charcoal/Admin/Ui/SecondaryMenu/GenericSecondaryMenuGroup.php b/packages/admin/src/Charcoal/Admin/Ui/SecondaryMenu/GenericSecondaryMenuGroup.php index 9768c560e..c79df642c 100644 --- a/packages/admin/src/Charcoal/Admin/Ui/SecondaryMenu/GenericSecondaryMenuGroup.php +++ b/packages/admin/src/Charcoal/Admin/Ui/SecondaryMenu/GenericSecondaryMenuGroup.php @@ -1,5 +1,7 @@ hasPermissions($link['permissions']) === false) { - continue; - } + if (isset($link['permissions']) && $this->hasPermissions($link['permissions']) === false) { + continue; } yield $link; @@ -282,26 +280,22 @@ public function links() /** * Determine if the secondary menu has any links. - * - * @return boolean */ - public function hasLinks() + public function hasLinks(): bool { - return !!$this->numLinks(); + return (bool) $this->numLinks(); } /** * Count the number of secondary menu links. - * - * @return integer */ - public function numLinks() + public function numLinks(): int { if (!is_array($this->links) && !($this->links instanceof \Traversable)) { return 0; } - $links = array_filter($this->links, function ($link) { + $links = array_filter($this->links, function (array $link): bool { if (isset($link['active']) && !$link['active']) { return false; } @@ -310,14 +304,7 @@ public function numLinks() $link['permissions'] = $link['required_acl_permissions']; unset($link['required_acl_permissions']); } - - if (isset($link['permissions'])) { - if ($this->hasPermissions($link['permissions']) === false) { - return false; - } - } - - return true; + return !(isset($link['permissions']) && $this->hasPermissions($link['permissions']) === false); }); return count($links); @@ -332,7 +319,7 @@ public function numLinks() public function isSelected($flag = null) { if ($flag !== null) { - $this->isSelected = !!$flag; + $this->isSelected = (bool) $flag; $this->setCollapsed(!$flag); } @@ -342,20 +329,16 @@ public function isSelected($flag = null) /** * Determine if the secondary groups should be displayed as panels. - * - * @return boolean */ - public function displayAsPanel() + public function displayAsPanel(): bool { return in_array($this->displayType(), [ 'panel', 'collapsible' ]); } /** * Determine if the group is collapsible. - * - * @return boolean */ - public function collapsible() + public function collapsible(): bool { return ($this->displayType() === 'collapsible'); } @@ -368,7 +351,7 @@ public function collapsible() */ public function setCollapsed($flag) { - $this->collapsed = !!$flag; + $this->collapsed = (bool) $flag; return $this; } @@ -401,7 +384,7 @@ public function collapsed() */ public function setParented($flag) { - $this->parented = !!$flag; + $this->parented = (bool) $flag; return $this; } @@ -447,7 +430,7 @@ public function ident() */ public function setActive($active) { - $this->active = !!$active; + $this->active = (bool) $active; return $this; } diff --git a/packages/admin/src/Charcoal/Admin/Ui/StructureContainerInterface.php b/packages/admin/src/Charcoal/Admin/Ui/StructureContainerInterface.php index 184421c44..e068046ab 100644 --- a/packages/admin/src/Charcoal/Admin/Ui/StructureContainerInterface.php +++ b/packages/admin/src/Charcoal/Admin/Ui/StructureContainerInterface.php @@ -1,5 +1,7 @@ supportedDisplayLayouts(); $displays = []; @@ -93,10 +91,8 @@ public function displays() /** * Retrieve the supported display layouts. - * - * @return array */ - protected function supportedDisplayLayouts() + protected function supportedDisplayLayouts(): array { return [ self::GROUP_STRUCT_DISPLAY, @@ -123,7 +119,7 @@ protected function defaultDisplay() */ public function setShowEmpty($show) { - $this->showEmpty = !!$show; + $this->showEmpty = (bool) $show; return $this; } diff --git a/packages/admin/src/Charcoal/Admin/User.php b/packages/admin/src/Charcoal/Admin/User.php index 4e518a153..f5a20c453 100644 --- a/packages/admin/src/Charcoal/Admin/User.php +++ b/packages/admin/src/Charcoal/Admin/User.php @@ -1,5 +1,7 @@ 'charcoal_admin_login', ]); - return $defaults; } } diff --git a/packages/admin/src/Charcoal/Admin/User/LostPasswordToken.php b/packages/admin/src/Charcoal/Admin/User/LostPasswordToken.php index a2cc68e79..2b65109ab 100644 --- a/packages/admin/src/Charcoal/Admin/User/LostPasswordToken.php +++ b/packages/admin/src/Charcoal/Admin/User/LostPasswordToken.php @@ -1,5 +1,7 @@ token = $token; return $this; @@ -63,9 +59,8 @@ public function token() /** * @param string $user The user. - * @return self */ - public function setUser($user) + public function setUser($user): static { $this->user = $user; return $this; @@ -82,9 +77,8 @@ public function user() /** * @param DateTimeInterface|string|null $expiry The date/time at object's creation. * @throws InvalidArgumentException If the date/time is invalid. - * @return self */ - public function setExpiry($expiry) + public function setExpiry($expiry): static { if ($expiry === null) { $this->expiry = null; @@ -95,7 +89,7 @@ public function setExpiry($expiry) try { $expiry = new DateTime($expiry); } catch (Exception $e) { - throw new InvalidArgumentException($e->getMessage()); + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); } } @@ -110,10 +104,7 @@ public function setExpiry($expiry) return $this; } - /** - * @return DateTimeInterface|null - */ - public function expiry() + public function expiry(): ?\DateTimeInterface { return $this->expiry; } @@ -122,6 +113,7 @@ public function expiry() * @param Container $container Pimple DI Container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -132,9 +124,10 @@ protected function setDependencies(Container $container) * @see \Charcoal\Source\StorableTrait::preSave() For the "create" Event. * @return boolean */ + #[\Override] protected function preSave() { - if ($this->expiry === null) { + if (!$this->expiry instanceof \DateTimeInterface) { $this->setExpiry('now +' . $this->defaultExpiry); } diff --git a/packages/admin/src/Charcoal/Admin/User/Permission.php b/packages/admin/src/Charcoal/Admin/User/Permission.php index 665cac388..ae5c28817 100644 --- a/packages/admin/src/Charcoal/Admin/User/Permission.php +++ b/packages/admin/src/Charcoal/Admin/User/Permission.php @@ -1,5 +1,7 @@ numColumns = $numColumns; return $this; } - /** - * @return float|integer - */ - public function bsColRatio() + public function bsColRatio(): float|int { return abs(12 / ($this->numColumns() ?: 12)); } @@ -67,9 +63,8 @@ public function cardTemplate() /** * @param string $cardTemplate CardTemplate for CardCollectionWidget. - * @return self */ - public function setCardTemplate($cardTemplate) + public function setCardTemplate($cardTemplate): static { $this->cardTemplate = $cardTemplate; @@ -94,13 +89,13 @@ public function objectCardRow() * * @param ModelInterface $object The current row's object. * @param array $objectProperties The $object's display properties. - * @return array */ - protected function parseObjectRow(ModelInterface $object, array $objectProperties) + #[\Override] + protected function parseObjectRow(ModelInterface $object, array $objectProperties): array { $row = $this->parseCollectionObjectRow($object, $objectProperties); $objProps = $row['objectProperties']; - array_walk($objProps, function ($value) use (&$row) { + array_walk($objProps, function (array $value) use (&$row): void { $row['objectProperties'][$value['ident']] = $value['val']; if (!method_exists($row['object'], 'isChipSuccess')) { @@ -113,20 +108,17 @@ protected function parseObjectRow(ModelInterface $object, array $objectPropertie /** * Retrieve the widget's data options for JavaScript components. - * - * @return array */ - public function widgetDataForJs() + #[\Override] + public function widgetDataForJs(): array { - $data = array_merge_recursive( + return array_merge_recursive( parent::widgetDataForJs(), [ 'card_template' => $this->cardTemplate(), 'num_columns' => $this->numColumns() ] ); - - return $data; } /** @@ -161,9 +153,8 @@ public function showFooterChip() /** * @param boolean $showFooterChip ShowFooterChip for CardCollectionWidget. - * @return self */ - public function setShowFooterChip($showFooterChip) + public function setShowFooterChip($showFooterChip): static { $this->showFooterChip = $showFooterChip; @@ -192,9 +183,8 @@ private function defaultChipTitle() /** * @param Translation|string $chipTitle ChipTitle for CardCollectionWidget. - * @return self */ - public function setChipTitle($chipTitle) + public function setChipTitle($chipTitle): static { $this->chipTitle = $this->translator()->translation($chipTitle); diff --git a/packages/admin/src/Charcoal/Admin/Widget/CollectionMapWidget.php b/packages/admin/src/Charcoal/Admin/Widget/CollectionMapWidget.php index a8232e52f..23ee3fb37 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/CollectionMapWidget.php +++ b/packages/admin/src/Charcoal/Admin/Widget/CollectionMapWidget.php @@ -66,7 +66,8 @@ class CollectionMapWidget extends AdminWidget implements CollectionContainerInte * @param array $data The widget data. * @return TableWidget Chainable */ - public function setData(array $data) + #[\Override] + public function setData(array $data): static { parent::setData($data); @@ -82,9 +83,8 @@ public function setData(array $data) * Sets the API key for the mapping service. * * @param string $key An API key. - * @return self */ - public function setApiKey($key) + public function setApiKey($key): static { $this->apiKey = $key; @@ -105,7 +105,7 @@ public function apiKey() * @param string $p The latitude property ident. * @return MapWidget Chainable */ - public function setLatProperty($p) + public function setLatProperty($p): static { $this->latProperty = $p; return $this; @@ -123,7 +123,7 @@ public function latProperty() * @param string $p The longitude property ident. * @return MapWidget Chainable */ - public function setLonProperty($p) + public function setLonProperty($p): static { $this->lonProperty = $p; return $this; @@ -141,7 +141,7 @@ public function lonProperty() * @param string $p The polygon property ident. * @return MapWidget Chainable */ - public function setPolygonProperty($p) + public function setPolygonProperty($p): static { $this->polygonProperty = $p; return $this; @@ -159,7 +159,7 @@ public function polygonProperty() * @param string $p The path property ident. * @return MapWidget Chainable */ - public function setPathProperty($p) + public function setPathProperty($p): static { $this->pathProperty = $p; return $this; @@ -177,7 +177,7 @@ public function pathProperty() * @param string $template The infobox template ident. * @return CollectionMapWidget Chainable */ - public function setInfoboxTemplate($template) + public function setInfoboxTemplate($template): static { $this->infoboxTemplate = $template; return $this; @@ -204,7 +204,7 @@ public function mapObjects() if (!$objType) { throw new UnexpectedValueException(sprintf( '%1$s cannot create collection map. Object type is not defined.', - get_class($this) + static::class )); } @@ -212,12 +212,12 @@ public function mapObjects() $loader->setModel($this->proto()); $collectionConfig = $this->collectionConfig(); - if (is_array($collectionConfig) && !empty($collectionConfig)) { + if (is_array($collectionConfig) && $collectionConfig !== []) { unset($collectionConfig['properties']); $loader->setData($collectionConfig); } - $callback = function (&$obj) { + $callback = function (&$obj): void { $obj->mapInfoboxTemplate = $this->infoboxTemplate(); if ($this->latProperty() && $this->latProperty()) { @@ -273,10 +273,7 @@ public function mapObjects() } } - /** - * @return boolean - */ - public function showInfobox() + public function showInfobox(): bool { return ($this->infoboxTemplate != ''); } @@ -294,10 +291,8 @@ public function dataFromRequest() /** * Retrieve the accepted metadata from the current request. - * - * @return array */ - public function acceptedRequestData() + public function acceptedRequestData(): array { return [ 'obj_type', @@ -315,7 +310,7 @@ public function dataFromObject() { $proto = $this->proto(); $objMetadata = $proto->metadata(); - $adminMetadata = (isset($objMetadata['admin']) ? $objMetadata['admin'] : null); + $adminMetadata = ($objMetadata['admin'] ?? null); if (empty($adminMetadata['lists'])) { return []; @@ -334,11 +329,7 @@ public function dataFromObject() return []; } - if (isset($adminMetadata['lists'][$collectionIdent])) { - $objListData = $adminMetadata['lists'][$collectionIdent]; - } else { - $objListData = []; - } + $objListData = $adminMetadata['lists'][$collectionIdent] ?? []; $collectionConfig = []; @@ -368,7 +359,7 @@ public function dataFromObject() } } - if ($collectionConfig) { + if ($collectionConfig !== []) { $this->mergeCollectionConfig($collectionConfig); } @@ -381,6 +372,7 @@ public function dataFromObject() * @param Container $container A dependencies container instance. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -420,11 +412,7 @@ protected function getPropertyValue(ModelInterface $obj, $key) $data = $obj; foreach (explode('.', $key) as $segment) { $accessible = is_array($data) || $data instanceof ArrayAccess; - if ($data instanceof ArrayAccess) { - $exists = $data->offsetExists($segment); - } else { - $exists = array_key_exists($segment, $data); - } + $exists = $data instanceof ArrayAccess ? $data->offsetExists($segment) : array_key_exists($segment, $data); if ($accessible && $exists) { $data = $data[$segment]; @@ -446,6 +434,7 @@ protected function getPropertyValue(ModelInterface $obj, $key) * @param mixed $toResolve A callable used when merging data. * @return callable|null */ + #[\Override] protected function resolveDataSourceFilter($toResolve) { if (is_string($toResolve)) { diff --git a/packages/admin/src/Charcoal/Admin/Widget/DashboardWidget.php b/packages/admin/src/Charcoal/Admin/Widget/DashboardWidget.php index 0313fdb65..811b7efab 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/DashboardWidget.php +++ b/packages/admin/src/Charcoal/Admin/Widget/DashboardWidget.php @@ -28,6 +28,7 @@ class DashboardWidget extends AdminWidget implements * @param Container $container The DI container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); diff --git a/packages/admin/src/Charcoal/Admin/Widget/Dialog/ImportlistWidget.php b/packages/admin/src/Charcoal/Admin/Widget/Dialog/ImportlistWidget.php index b1c594c08..d0409a1b3 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/Dialog/ImportlistWidget.php +++ b/packages/admin/src/Charcoal/Admin/Widget/Dialog/ImportlistWidget.php @@ -1,5 +1,7 @@ objId()) { return $this->translator()->translation('Update'); @@ -106,7 +105,8 @@ public function defaultBackToObjectLabel() * @param array $data The widget data. * @return ObjectForm Chainable */ - public function setData(array $data) + #[\Override] + public function setData(array $data): static { parent::setData($data); @@ -121,6 +121,7 @@ public function setData(array $data) /** * @return FormSidebarInterface[]|\Generator */ + #[\Override] public function sidebars() { $objId = $this->obj()->id(); @@ -134,7 +135,7 @@ public function sidebars() $metadata = $this->obj()->metadata(); $objType = (isset($metadata['labels']['singular_name']) ? $translator->translate($metadata['labels']['singular_name']) - : (new ReflectionClass($obj))->getShortName()); + : new ReflectionClass($obj)->getShortName()); $label = $translator->translate('Back to {{name}} id: {{id}}'); $label = strtr($label, [ @@ -169,9 +170,8 @@ public function sidebars() * * @param string $formIdent The form identifier. * @throws InvalidArgumentException If the identifier is not a string. - * @return self */ - public function setFormIdent($formIdent) + public function setFormIdent($formIdent): static { if (!is_string($formIdent)) { throw new InvalidArgumentException( @@ -209,7 +209,7 @@ public function formIdentFallback() * * @return array */ - public function displayOptions() + public function displayOptions(): ?array { if (!$this->displayOptions) { $this->setDisplayOptions([]); @@ -223,9 +223,8 @@ public function displayOptions() * * @param array $options Display configuration. * @throws \RuntimeException If the display options are not an associative array. - * @return self */ - public function setDisplayOptions(array $options) + public function setDisplayOptions(array $options): static { if (!is_array($options)) { throw new \RuntimeException('The display options must be an associative array.'); @@ -238,10 +237,8 @@ public function setDisplayOptions(array $options) /** * Retrieve the default display options for the widget. - * - * @return array */ - public function defaultDisplayOptions() + public function defaultDisplayOptions(): array { return [ 'parented' => false, @@ -265,7 +262,7 @@ public function formIdent() * @throws InvalidArgumentException If argument is not a string. * @return ActionInterface Chainable */ - public function setNextUrl($url) + public function setNextUrl($url): static { if (!is_string($url)) { throw new InvalidArgumentException( @@ -287,6 +284,7 @@ public function setNextUrl($url) * * @return string Relative URL */ + #[\Override] public function action() { $action = parent::action(); @@ -311,14 +309,15 @@ public function action() * @throws UnexpectedValueException If a property data is invalid. * @return DocFormPropertyWidget[]|Generator */ - public function formProperties(array $group = null) + #[\Override] + public function formProperties(?array $group = null) { $obj = $this->obj(); $props = $obj->metadata()->properties(); // We need to sort form properties by form group property order if a group exists - if (!empty($group)) { - $group = array_map([ $this, 'camelize' ], $group); + if ($group !== null && $group !== []) { + $group = array_map($this->camelize(...), $group); $group = array_flip($group); $props = array_intersect_key($props, $group); $props = array_merge($group, $props); @@ -344,7 +343,7 @@ public function formProperties(array $group = null) throw new UnexpectedValueException(sprintf( 'Invalid property data for "%1$s", received %2$s', $propertyIdent, - (is_object($propertyMetadata) ? get_class($propertyMetadata) : gettype($propertyMetadata)) + (get_debug_type($propertyMetadata)) )); } @@ -386,7 +385,7 @@ public function formProperty($propertyIdent) throw new UnexpectedValueException(sprintf( 'Invalid property data for "%1$s", received %2$s', $propertyIdent, - (is_object($propertyMetadata) ? get_class($propertyMetadata) : gettype($propertyMetadata)) + (get_debug_type($propertyMetadata)) )); } @@ -407,19 +406,14 @@ public function formProperty($propertyIdent) * @param array $data Data. * @return ObjectFormWidget Chainable. */ - public function setFormData(array $data) + #[\Override] + public function setFormData(array $data): static { $objData = $this->objData(); $merged = array_replace_recursive($objData, $data); // Remove null values - $merged = array_filter($merged, function ($val) { - if ($val === null) { - return false; - } - - return true; - }); + $merged = array_filter($merged, fn($val): bool => $val !== null); $this->formData = $merged; $this->obj()->setData($merged); @@ -432,6 +426,7 @@ public function setFormData(array $data) * * @return array */ + #[\Override] public function formData() { if (!$this->formData) { @@ -460,9 +455,8 @@ public function showHeader() /** * @param boolean $showHeader Is the Header to be shown. - * @return self */ - public function setShowHeader($showHeader) + public function setShowHeader($showHeader): static { $this->showHeader = $showHeader; @@ -479,19 +473,16 @@ public function showTitle() /** * @param boolean $showTitle Is the title to be shown. - * @return self */ - public function setShowTitle($showTitle) + public function setShowTitle($showTitle): static { $this->showTitle = $showTitle; return $this; } - /** - * @return string - */ - public function defaultGroupType() + #[\Override] + public function defaultGroupType(): string { return 'charcoal/admin/docs/widget/form-group/doc'; } @@ -501,17 +492,17 @@ public function defaultGroupType() * * @return string[] */ - protected function defaultDataSources() + #[\Override] + protected function defaultDataSources(): array { return [ static::DATA_SOURCE_REQUEST, static::DATA_SOURCE_OBJECT ]; } /** * Retrieve the default data source filters (when setting data on an entity). - * - * @return array */ - protected function defaultDataSourceFilters() + #[\Override] + protected function defaultDataSourceFilters(): array { return [ 'request' => null, @@ -528,6 +519,7 @@ protected function defaultDataSourceFilters() * @param mixed $toResolve A callable used when merging data. * @return callable|null */ + #[\Override] protected function resolveDataSourceFilter($toResolve) { if (is_string($toResolve)) { @@ -554,10 +546,9 @@ protected function resolveDataSourceFilter($toResolve) /** * Retrieve the accepted metadata from the current request. - * - * @return array */ - protected function acceptedRequestData() + #[\Override] + protected function acceptedRequestData(): array { return array_merge( [ 'obj_type', 'obj_id', 'template' ], @@ -574,7 +565,7 @@ protected function dataFromObject() { $obj = $this->obj(); $objMetadata = $obj->metadata(); - $adminMetadata = (isset($objMetadata['admin']) ? $objMetadata['admin'] : null); + $adminMetadata = ($objMetadata['admin'] ?? null); $formIdent = $this->formIdent(); if (!$formIdent) { @@ -585,11 +576,7 @@ protected function dataFromObject() $formIdent = $obj->render($formIdent); } - if (isset($adminMetadata['forms'][$formIdent])) { - $objFormData = $adminMetadata['forms'][$formIdent]; - } else { - $objFormData = []; - } + $objFormData = $adminMetadata['forms'][$formIdent] ?? []; if (isset($objFormData['groups']) && isset($adminMetadata['form_groups'])) { $extraFormGroups = array_intersect( @@ -627,7 +614,8 @@ protected function dataFromObject() * @param array|null $data Optional. The form group data to set. * @return FormGroupInterface */ - protected function createFormGroup(array $data = null) + #[\Override] + protected function createFormGroup(?array $data = null) { $type = $this->defaultGroupType(); @@ -665,13 +653,13 @@ protected function createFormGroup(array $data = null) * @param FormGroupInterface $group The form group to update. * @param array|null $groupData Optional. The new group data to apply. * @param string|null $groupIdent Optional. The new group identifier. - * @return FormGroupInterface */ + #[\Override] protected function updateFormGroup( FormGroupInterface $group, - array $groupData = null, + ?array $groupData = null, $groupIdent = null - ) { + ): FormGroupInterface { $group->setForm($this); if ($groupIdent !== null) { diff --git a/packages/admin/src/Charcoal/Admin/Widget/FormGroup/AclPermissions.php b/packages/admin/src/Charcoal/Admin/Widget/FormGroup/AclPermissions.php index d0789e22a..bb871e13e 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/FormGroup/AclPermissions.php +++ b/packages/admin/src/Charcoal/Admin/Widget/FormGroup/AclPermissions.php @@ -30,20 +30,11 @@ class AclPermissions extends AdminWidget implements { use FormGroupTrait; - /** - * @var Acl $roleAcl - */ - private $roleAcl; + private ?\Laminas\Permissions\Acl\Acl $roleAcl = null; - /** - * @var array - */ - private $roleAllowed; + private ?array $roleAllowed = null; - /** - * @var array - */ - private $roleDenied; + private ?array $roleDenied = null; /** * Store the collection loader for the current class. @@ -71,15 +62,12 @@ class AclPermissions extends AdminWidget implements * * @return string */ - public function objId() + public function objId(): string|false|null { return filter_input(INPUT_GET, 'obj_id', FILTER_SANITIZE_STRING); } - /** - * @return array - */ - public function permissionCategories() + public function permissionCategories(): array { $loader = $this->collectionLoader(); $loader->setModel(PermissionCategory::class); @@ -103,6 +91,7 @@ public function permissionCategories() * @param Container $container A dependencies container instance. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -120,10 +109,10 @@ protected function setDependencies(Container $container) */ protected function db() { - if (!isset($this->database)) { + if ($this->database === null) { throw new RuntimeException(sprintf( 'Database Connection is not defined for "%s"', - get_class($this) + static::class )); } @@ -138,10 +127,10 @@ protected function db() */ protected function adminAcl() { - if (!isset($this->aclManager)) { + if ($this->aclManager === null) { throw new RuntimeException(sprintf( 'ACL Manager is not defined for "%s"', - get_class($this) + static::class )); } @@ -156,10 +145,10 @@ protected function adminAcl() */ protected function collectionLoader() { - if (!isset($this->collectionLoader)) { + if ($this->collectionLoader === null) { throw new RuntimeException(sprintf( 'Collection Loader is not defined for "%s"', - get_class($this) + static::class )); } @@ -168,12 +157,9 @@ protected function collectionLoader() - /** - * @return Acl - */ - protected function roleAcl() + protected function roleAcl(): \Laminas\Permissions\Acl\Acl { - if (!$this->roleAcl) { + if (!$this->roleAcl instanceof \Laminas\Permissions\Acl\Acl) { $id = $this->objId(); $this->roleAcl = new Acl(); @@ -195,8 +181,8 @@ protected function roleAcl() $sth->execute(); $permissions = $sth->fetch(PDO::FETCH_ASSOC); - $this->roleAllowed = explode(',', trim($permissions['allowed'])); - $this->roleDenied = explode(',', trim($permissions['denied'])); + $this->roleAllowed = explode(',', trim((string) $permissions['allowed'])); + $this->roleDenied = explode(',', trim((string) $permissions['denied'])); foreach ($this->roleAllowed as $allowed) { $this->roleAcl->allow($id, 'admin', $allowed); @@ -211,12 +197,11 @@ protected function roleAcl() /** * @param string $category The category ident to load permissions from. - * @return array */ - private function loadCategoryPermissions($category) + private function loadCategoryPermissions($category): array { $adminAcl = $this->adminAcl(); - $roleAcl = $this->roleAcl(); + $this->roleAcl(); $loader = $this->collectionLoader(); $loader->setModel(Permission::class); diff --git a/packages/admin/src/Charcoal/Admin/Widget/FormGroup/GenericFormGroup.php b/packages/admin/src/Charcoal/Admin/Widget/FormGroup/GenericFormGroup.php index e1a20645f..2b175b894 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/FormGroup/GenericFormGroup.php +++ b/packages/admin/src/Charcoal/Admin/Widget/FormGroup/GenericFormGroup.php @@ -1,5 +1,7 @@ widgetId = $widgetId; @@ -116,7 +109,8 @@ public function setWidgetId($widgetId) /** * @return Translation|string|null */ - public function description() + #[\Override] + public function description(): string { return $this->renderTemplate((string)parent::description()); } @@ -124,7 +118,8 @@ public function description() /** * @return Translation|string|null */ - public function notes() + #[\Override] + public function notes(): string { return $this->renderTemplate((string)parent::notes()); } @@ -135,6 +130,7 @@ public function notes() * @param boolean|string $show Whether to show or hide notes. * @return FormGroupWidget Chainable */ + #[\Override] public function setShowNotes($show) { $this->showNotesAbove = ($show === 'above'); @@ -142,10 +138,7 @@ public function setShowNotes($show) return parent::setShowNotes($show); } - /** - * @return boolean - */ - public function showNotesAbove() + public function showNotesAbove(): bool { return $this->showNotesAbove && $this->showNotes(); } @@ -154,6 +147,7 @@ public function showNotesAbove() * @param Container $container The DI container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -168,9 +162,8 @@ protected function setDependencies(Container $container) * Set the widget factory. * * @param FactoryInterface $factory The factory to create widgets. - * @return self */ - protected function setWidgetFactory(FactoryInterface $factory) + protected function setWidgetFactory(FactoryInterface $factory): static { $this->widgetFactory = $factory; @@ -181,14 +174,13 @@ protected function setWidgetFactory(FactoryInterface $factory) * Retrieve the widget factory. * * @throws RuntimeException If the widget factory was not previously set. - * @return FactoryInterface */ - protected function widgetFactory() + protected function widgetFactory(): \Charcoal\Factory\FactoryInterface { - if ($this->widgetFactory === null) { + if (!$this->widgetFactory instanceof \Charcoal\Factory\FactoryInterface) { throw new RuntimeException(sprintf( 'Widget Factory is not defined for "%s"', - get_class($this) + static::class )); } diff --git a/packages/admin/src/Charcoal/Admin/Widget/FormGroup/ObjectRevisionsFormGroup.php b/packages/admin/src/Charcoal/Admin/Widget/FormGroup/ObjectRevisionsFormGroup.php index ac241fa3a..a9f81723b 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/FormGroup/ObjectRevisionsFormGroup.php +++ b/packages/admin/src/Charcoal/Admin/Widget/FormGroup/ObjectRevisionsFormGroup.php @@ -40,9 +40,8 @@ class ObjectRevisionsFormGroup extends AbstractFormGroup implements /** * @param string $widgetId The widget identifier. - * @return self */ - public function setWidgetId($widgetId) + public function setWidgetId($widgetId): static { $this->widgetId = $widgetId; @@ -61,10 +60,8 @@ public function widgetId() return $this->widgetId; } - /** - * @return boolean - */ - public function active() + #[\Override] + public function active(): bool { return parent::active() && $this->objType() && $this->objId(); } @@ -105,6 +102,7 @@ public function objId() * @param Container $container A dependencies container instance. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); diff --git a/packages/admin/src/Charcoal/Admin/Widget/FormGroup/StructureFormGroup.php b/packages/admin/src/Charcoal/Admin/Widget/FormGroup/StructureFormGroup.php index f92b5ff71..261c7ad4f 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/FormGroup/StructureFormGroup.php +++ b/packages/admin/src/Charcoal/Admin/Widget/FormGroup/StructureFormGroup.php @@ -109,10 +109,8 @@ class StructureFormGroup extends FormGroupWidget implements /** * The form group the input belongs to. - * - * @var FormGroupInterface|null */ - private $formGroup; + private ?\Charcoal\Ui\FormGroup\FormGroupInterface $formGroup = null; /** * Whether the form is ready. @@ -136,19 +134,16 @@ class StructureFormGroup extends FormGroupWidget implements */ protected $rawData; - /** - * @return string - */ - public function type() + #[\Override] + public function type(): string { return 'charcoal/admin/widget/form-group/structure'; } /** * @param string $structId The structure entry identifier. - * @return self */ - public function setStructId($structId) + public function setStructId($structId): static { $this->structId = $structId; return $this; @@ -168,9 +163,9 @@ public function structId() /** * @param array $data Widget data. - * @return self */ - public function setData(array $data) + #[\Override] + public function setData(array $data): static { if ($this->rawData === null) { $this->rawData = $data; @@ -186,6 +181,7 @@ public function setData(array $data) * * @return boolean If TRUE or unset, check if there is a title. */ + #[\Override] public function showHeader() { if ($this->display() === self::SEAMLESS_STRUCT_DISPLAY) { @@ -200,6 +196,7 @@ public function showHeader() * * @return boolean If TRUE or unset, check if there are notes. */ + #[\Override] public function showFooter() { if ($this->display() === self::SEAMLESS_STRUCT_DISPLAY) { @@ -216,6 +213,7 @@ public function showFooter() * * @return string If unset, returns the UI item type. */ + #[\Override] public function template() { $this->setDynamicTemplate('structure_template', $this->displayTemplate()); @@ -225,10 +223,8 @@ public function template() /** * Retrieve the property's display layout template. - * - * @return string|null */ - public function displayTemplate() + public function displayTemplate(): string { $display = $this->display(); @@ -272,9 +268,8 @@ public function obj() * Set the form input's parent group. * * @param FormGroupInterface $formGroup The parent form group object. - * @return self */ - public function setFormGroup(FormGroupInterface $formGroup) + public function setFormGroup(FormGroupInterface $formGroup): static { $this->formGroup = $formGroup; @@ -283,20 +278,16 @@ public function setFormGroup(FormGroupInterface $formGroup) /** * Retrieve the input's parent group. - * - * @return FormGroupInterface|null */ - public function formGroup() + public function formGroup(): ?\Charcoal\Ui\FormGroup\FormGroupInterface { return $this->formGroup; } /** * Clear the group's parent group. - * - * @return self */ - public function clearFormGroup() + public function clearFormGroup(): static { $this->formGroup = null; @@ -312,9 +303,8 @@ public function clearFormGroup() * @param string|ModelStructureProperty $propertyIdent The property identifier—or instance—of a storage property. * @throws InvalidArgumentException If the property identifier is not a string. * @throws UnexpectedValueException If a property is invalid. - * @return self */ - public function setStorageProperty($propertyIdent) + public function setStorageProperty($propertyIdent): static { $property = null; if ($propertyIdent instanceof PropertyInterface) { @@ -331,11 +321,11 @@ public function setStorageProperty($propertyIdent) throw new UnexpectedValueException(sprintf( 'The "%1$s" property is not defined on [%2$s]', $propertyIdent, - get_class($obj) + $obj::class )); } - if ($property === null) { + if (!$property instanceof \Charcoal\Property\PropertyInterface) { $property = $obj->property($propertyIdent); } @@ -349,8 +339,8 @@ public function setStorageProperty($propertyIdent) throw new UnexpectedValueException(sprintf( '"%s" [%s] is not a model structure property on [%s].', $propertyIdent, - (is_object($property) ? get_class($property) : gettype($property)), - (is_object($obj) ? get_class($obj) : gettype($obj)) + (get_debug_type($property)), + (get_debug_type($obj)) )); } @@ -368,7 +358,7 @@ public function storageProperty() if ($this->storageProperty === null) { throw new RuntimeException(sprintf( 'Storage property owner is not defined for "%s"', - get_class($this) + static::class )); } @@ -406,11 +396,7 @@ protected function autoFormGroup() $property = $this->storageProperty(); $struct = $property->getStructureMetadata(); - if (isset($struct['admin']['auto_form_group'])) { - $this->autoFormGroup = $struct['admin']['auto_form_group']; - } else { - $this->autoFormGroup = true; - } + $this->autoFormGroup = $struct['admin']['auto_form_group'] ?? true; } return $this->autoFormGroup; @@ -445,8 +431,6 @@ protected function finalizeStructure($reload = false) protected function findStructureFormGroup(): ?array { $struct = $this->storageProperty()->getStructureMetadata(); - - $formGroup = null; if (isset($struct['admin']['form_group'])) { if (\is_string($struct['admin']['form_group'])) { $groupName = $struct['admin']['form_group']; @@ -476,6 +460,7 @@ protected function findStructureFormGroup(): ?array * * @return array */ + #[\Override] protected function parsedFormProperties() { if ($this->parsedFormProperties === null) { @@ -485,30 +470,28 @@ protected function parsedFormProperties() $availableProperties = $this->structProperties(); $structProperties = []; - if (!empty($groupProperties)) { - foreach ($groupProperties as $propertyIdent => $propertyMetadata) { - if (is_string($propertyMetadata)) { - $propertyIdent = $propertyMetadata; - $propertyMetadata = null; - } - - $propertyIdent = $this->camelize($propertyIdent); - - if (!isset($availableProperties[$propertyIdent])) { - continue; - } - - if (is_array($propertyMetadata)) { - $propertyMetadata = array_merge($propertyMetadata, $availableProperties[$propertyIdent]); - } else { - $propertyMetadata = $availableProperties[$propertyIdent]; - } - - $structProperties[$propertyIdent] = $propertyMetadata; + foreach ($groupProperties as $propertyIdent => $propertyMetadata) { + if (is_string($propertyMetadata)) { + $propertyIdent = $propertyMetadata; + $propertyMetadata = null; } + + $propertyIdent = $this->camelize($propertyIdent); + + if (!isset($availableProperties[$propertyIdent])) { + continue; + } + + if (is_array($propertyMetadata)) { + $propertyMetadata = array_merge($propertyMetadata, $availableProperties[$propertyIdent]); + } else { + $propertyMetadata = $availableProperties[$propertyIdent]; + } + + $structProperties[$propertyIdent] = $propertyMetadata; } - if (empty($structProperties) && $this->autoFormGroup() === true) { + if ($structProperties === [] && $this->autoFormGroup() === true) { $structProperties = $availableProperties; } @@ -526,12 +509,11 @@ protected function parsedFormProperties() * @throws UnexpectedValueException If a property data is invalid. * @return \Charcoal\Admin\Widget\FormPropertyWidget[]|\Generator */ + #[\Override] public function formProperties() { - if ($this instanceof ConditionalizableInterface) { - if ($this->condition() !== null && !$this->resolvedCondition()) { - return []; - } + if ($this->condition() !== null && !$this->resolvedCondition()) { + return []; } $this->finalizeStructure(); @@ -592,7 +574,7 @@ public function formProperties() throw new UnexpectedValueException(sprintf( 'Invalid property data for "%1$s", received %2$s', $propertyIdent, - (is_object($propertyMetadata) ? get_class($propertyMetadata) : gettype($propertyMetadata)) + (get_debug_type($propertyMetadata)) )); } diff --git a/packages/admin/src/Charcoal/Admin/Widget/FormGroup/TemplateOptionsFormGroup.php b/packages/admin/src/Charcoal/Admin/Widget/FormGroup/TemplateOptionsFormGroup.php index a8584dddd..a4d254b33 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/FormGroup/TemplateOptionsFormGroup.php +++ b/packages/admin/src/Charcoal/Admin/Widget/FormGroup/TemplateOptionsFormGroup.php @@ -80,14 +80,12 @@ class TemplateOptionsFormGroup extends StructureFormGroup * * @var TemplateableInterface|string|null */ - private $controllerIdent; + private ?string $controllerIdent = null; /** * Store the metadata loader instance. - * - * @var MetadataLoader */ - private $metadataLoader; + private ?\Charcoal\Model\Service\MetadataLoader $metadataLoader = null; /** * Set the form object's template controller identifier. @@ -95,7 +93,7 @@ class TemplateOptionsFormGroup extends StructureFormGroup * @param mixed $ident The template controller identifier. * @return TemplateableInterface Chainable */ - public function setControllerIdent($ident) + public function setControllerIdent(string $ident): static { if (class_exists($ident)) { $this->controllerIdent = $ident; @@ -103,7 +101,7 @@ public function setControllerIdent($ident) return $this; } - if (substr($ident, -9) !== '-template') { + if (!str_ends_with($ident, '-template')) { $ident .= '-template'; } @@ -114,10 +112,8 @@ public function setControllerIdent($ident) /** * Retrieve the form object's template controller identifier. - * - * @return mixed */ - public function controllerIdent() + public function controllerIdent(): ?string { return $this->controllerIdent; } @@ -132,7 +128,7 @@ public function controllerIdent() * @throws UnexpectedValueException If a property data is invalid. * @return StructureFormGroup */ - public function setTemplateProperty($propertyIdent) + public function setTemplateProperty($propertyIdent): static { if ($propertyIdent === null) { $this->templateProperty = null; @@ -155,11 +151,11 @@ public function setTemplateProperty($propertyIdent) throw new UnexpectedValueException(sprintf( 'The "%1$s" property is not defined on [%2$s]', $propertyIdent, - get_class($this->obj()) + $this->obj()::class )); } - if ($property === null) { + if (!$property instanceof \Charcoal\Property\PropertyInterface) { $property = $obj->property($propertyIdent); } @@ -183,7 +179,7 @@ public function templateProperty() } else { throw new RuntimeException(sprintf( 'Storage property owner is not defined for "%s"', - get_class($this) + static::class )); } } @@ -197,6 +193,7 @@ public function templateProperty() * @throws RuntimeException If the storage property was not previously set. * @return PropertyInterface */ + #[\Override] public function storageProperty() { if ($this->storageProperty === null) { @@ -206,7 +203,7 @@ public function storageProperty() } else { throw new RuntimeException(sprintf( 'Storage property owner is not defined for "%s"', - get_class($this) + static::class )); } } @@ -220,6 +217,7 @@ public function storageProperty() * @param Container $container A dependencies container instance. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -231,9 +229,8 @@ protected function setDependencies(Container $container) * Set a metadata loader. * * @param MetadataLoader $loader The loader instance, used to load metadata. - * @return self */ - protected function setMetadataLoader(MetadataLoader $loader) + protected function setMetadataLoader(MetadataLoader $loader): static { $this->metadataLoader = $loader; @@ -244,14 +241,13 @@ protected function setMetadataLoader(MetadataLoader $loader) * Retrieve the metadata loader. * * @throws RuntimeException If the metadata loader was not previously set. - * @return MetadataLoader */ - protected function metadataLoader() + protected function metadataLoader(): \Charcoal\Model\Service\MetadataLoader { - if ($this->metadataLoader === null) { + if (!$this->metadataLoader instanceof \Charcoal\Model\Service\MetadataLoader) { throw new RuntimeException(sprintf( 'Metadata Loader is not defined for "%s"', - get_class($this) + static::class )); } @@ -266,8 +262,7 @@ protected function metadataLoader() */ protected function loadMetadata($metadataIdent) { - $metadata = $this->metadataLoader()->load($metadataIdent, $this->metadataClass()); - return $metadata; + return $this->metadataLoader()->load($metadataIdent, $this->metadataClass()); } /** @@ -283,10 +278,8 @@ protected function createMetadata() /** * Retrieve the class name of the metadata object. - * - * @return string */ - protected function metadataClass() + protected function metadataClass(): string { return StructureMetadata::class; } @@ -297,6 +290,7 @@ protected function metadataClass() * @param boolean $reload Rebuild the form group's structure. * @return void */ + #[\Override] protected function finalizeStructure($reload = false) { if ($reload || !$this->isStructureFinalized) { @@ -311,7 +305,7 @@ protected function finalizeStructure($reload = false) } $controllerInterfaces = (array)$this->controllerIdent(); - if (!empty($controllerInterfaces)) { + if ($controllerInterfaces !== []) { $metadataLoader = $this->metadataLoader(); $controllerStructKey = $controllerInterfaces; diff --git a/packages/admin/src/Charcoal/Admin/Widget/FormGroupWidget.php b/packages/admin/src/Charcoal/Admin/Widget/FormGroupWidget.php index b4768d05e..0c479e063 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/FormGroupWidget.php +++ b/packages/admin/src/Charcoal/Admin/Widget/FormGroupWidget.php @@ -34,30 +34,22 @@ class FormGroupWidget extends AbstractUiItem implements /** * Whether notes shoudl be display before or after the form fields. - * - * @var boolean */ - private $showNotesAbove = false; + private bool $showNotesAbove = false; /** * @var array|null $parsedFormProperties */ protected $parsedFormProperties; - /** - * @var array $groupProperties - */ - private $groupProperties = []; + private array $groupProperties = []; - /** - * @var array $propertiesOptions - */ - private $propertiesOptions = []; + private array $propertiesOptions = []; /** * @param array|\ArrayAccess $data Dependencies. */ - public function __construct($data) + public function __construct(?array $data) { parent::__construct($data); @@ -70,7 +62,8 @@ public function __construct($data) * @param array $data Widget data. * @return FormGroupWidget Chainable */ - public function setData(array $data) + #[\Override] + public function setData(array $data): static { if (!empty($data['properties'])) { $this->setGroupProperties($data['properties']); @@ -92,10 +85,8 @@ public function setData(array $data) return $this; } - /** - * @return string - */ - public function type() + #[\Override] + public function type(): string { return 'charcoal/admin/widget/form-group-widget'; } @@ -104,7 +95,7 @@ public function type() * @param string $widgetId The widget identifier. * @return AdminWidget Chainable */ - public function setWidgetId($widgetId) + public function setWidgetId($widgetId): static { $this->widgetId = $widgetId; @@ -127,7 +118,7 @@ public function widgetId() * @param array $properties The group properties. * @return FormGroupWidget Chainable */ - public function setGroupProperties(array $properties) + public function setGroupProperties(array $properties): static { $this->groupProperties = $properties; $this->parsedFormProperties = null; @@ -135,10 +126,7 @@ public function setGroupProperties(array $properties) return $this; } - /** - * @return array - */ - public function groupProperties() + public function groupProperties(): array { return $this->groupProperties; } @@ -147,29 +135,24 @@ public function groupProperties() * @param array $properties The options to customize the group properties. * @return FormGroupWidget Chainable */ - public function setPropertiesOptions(array $properties) + public function setPropertiesOptions(array $properties): static { $this->propertiesOptions = $properties; return $this; } - /** - * @return array - */ - public function propertiesOptions() + public function propertiesOptions(): array { return $this->propertiesOptions; } /** * Determine if the form group has properties. - * - * @return boolean */ - public function hasFormProperties() + public function hasFormProperties(): bool { - return !!count($this->parsedFormProperties()); + return (bool) count($this->parsedFormProperties()); } /** @@ -182,7 +165,7 @@ public function formProperties() $form = $this->form(); $obj = ($form instanceof ObjectContainerInterface) ? $form->obj() : null; - $groupProperties = array_map([ $this, 'camelize' ], $this->groupProperties()); + $groupProperties = array_map($this->camelize(...), $this->groupProperties()); $formProperties = $this->parsedFormProperties(); $propOptions = $this->propertiesOptions(); @@ -233,9 +216,8 @@ public function formProperties() * Retrieve the available languages, formatted for the sidebar language-switcher. * * @see FormSidebarWidget::languages() - * @return array */ - public function languages() + public function languages(): array { $currentLocale = $this->translator()->getLocale(); $languages = []; @@ -249,7 +231,7 @@ public function languages() } else { $trans = 'locale.' . $locale; if ($trans === $this->translator()->translate($trans)) { - $label = strtoupper($locale); + $label = strtoupper((string) $locale); } else { $label = $this->translator()->translation($trans); } @@ -300,7 +282,8 @@ public function locale() /** * @return Translation|string|null */ - public function description() + #[\Override] + public function description(): string { return $this->renderTemplate((string)parent::description()); } @@ -308,7 +291,8 @@ public function description() /** * @return Translation|string|null */ - public function notes() + #[\Override] + public function notes(): string { return $this->renderTemplate((string)parent::notes()); } @@ -319,6 +303,7 @@ public function notes() * @param boolean|string $show Whether to show or hide notes. * @return FormGroupWidget Chainable */ + #[\Override] public function setShowNotes($show) { $this->showNotesAbove = ($show === 'above'); @@ -326,10 +311,7 @@ public function setShowNotes($show) return parent::setShowNotes($show); } - /** - * @return boolean - */ - public function showNotesAbove() + public function showNotesAbove(): bool { return $this->showNotesAbove && $this->showNotes(); } @@ -338,6 +320,7 @@ public function showNotesAbove() * @param Container $container The DI container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); diff --git a/packages/admin/src/Charcoal/Admin/Widget/FormPropertyWidget.php b/packages/admin/src/Charcoal/Admin/Widget/FormPropertyWidget.php index e690ff3f1..efafd9d14 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/FormPropertyWidget.php +++ b/packages/admin/src/Charcoal/Admin/Widget/FormPropertyWidget.php @@ -80,10 +80,8 @@ class FormPropertyWidget extends AdminWidget implements /** * The model property's metadata. - * - * @var array */ - private $propertyData = []; + private array $propertyData = []; /** * Store the property control instance. @@ -164,31 +162,23 @@ class FormPropertyWidget extends AdminWidget implements /** * Store the model property factory. - * - * @var FactoryInterface */ - private $propertyFactory; + private ?\Charcoal\Factory\FactoryInterface $propertyFactory = null; /** * Store the property form control factory. - * - * @var FactoryInterface */ - private $propertyInputFactory; + private ?\Charcoal\Factory\FactoryInterface $propertyInputFactory = null; /** * Store the property display factory. - * - * @var FactoryInterface */ - private $propertyDisplayFactory; + private ?\Charcoal\Factory\FactoryInterface $propertyDisplayFactory = null; /** * Track the state of data merging. - * - * @var boolean */ - private $isMergingWidgetData = false; + private bool $isMergingWidgetData = false; /** * The UI item's icon. @@ -204,11 +194,10 @@ class FormPropertyWidget extends AdminWidget implements * Retrieve the property factory. * * @throws RuntimeException If the property factory is missing. - * @return FactoryInterface */ - public function propertyFactory() + public function propertyFactory(): \Charcoal\Factory\FactoryInterface { - if ($this->propertyFactory === null) { + if (!$this->propertyFactory instanceof \Charcoal\Factory\FactoryInterface) { throw new RuntimeException( 'Missing Property Factory' ); @@ -221,11 +210,10 @@ public function propertyFactory() * Retrieve the property control factory. * * @throws RuntimeException If the property control factory is missing. - * @return FactoryInterface */ - public function propertyInputFactory() + public function propertyInputFactory(): \Charcoal\Factory\FactoryInterface { - if ($this->propertyInputFactory === null) { + if (!$this->propertyInputFactory instanceof \Charcoal\Factory\FactoryInterface) { throw new RuntimeException( 'Missing Property Input Factory' ); @@ -238,11 +226,10 @@ public function propertyInputFactory() * Retrieve the property display factory. * * @throws RuntimeException If the property display factory is missing. - * @return FactoryInterface */ - public function propertyDisplayFactory() + public function propertyDisplayFactory(): \Charcoal\Factory\FactoryInterface { - if ($this->propertyDisplayFactory === null) { + if (!$this->propertyDisplayFactory instanceof \Charcoal\Factory\FactoryInterface) { throw new RuntimeException( 'Missing Property Display Factory' ); @@ -256,6 +243,7 @@ public function propertyDisplayFactory() * * @return string */ + #[\Override] public function widgetId() { $type = $this->outputType(); @@ -281,7 +269,8 @@ public function widgetId() * @throws InvalidArgumentException If the argument is not a string. * @return FormPropertyWidget Chainable */ - public function setType($type) + #[\Override] + public function setType($type): static { if (empty($type)) { $this->type = null; @@ -313,7 +302,7 @@ public function setType($type) * @throws InvalidArgumentException If the argument is not a string. * @return FormPropertyWidget Chainable */ - public function setOutputType($type) + public function setOutputType($type): static { if (empty($type)) { $this->outputType = static::DEFAULT_OUTPUT; @@ -368,10 +357,8 @@ public function outputType() /** * Retrieve the supported property output types. - * - * @return array */ - public function supportedOutputTypes() + public function supportedOutputTypes(): array { return [ static::PROPERTY_CONTROL, static::PROPERTY_DISPLAY ]; } @@ -382,7 +369,7 @@ public function supportedOutputTypes() * @param FormGroupInterface $formGroup The parent form group object. * @return FormPropertyWidget Chainable */ - public function setFormGroup(FormGroupInterface $formGroup) + public function setFormGroup(FormGroupInterface $formGroup): static { $this->formGroup = $formGroup; @@ -404,7 +391,7 @@ public function formGroup() * * @return FormPropertyWidget Chainable */ - public function clearFormGroup() + public function clearFormGroup(): static { $this->formGroup = null; @@ -417,7 +404,8 @@ public function clearFormGroup() * @param array|ArrayAccess $data Widget and property data. * @return FormPropertyWidget Chainable */ - public function setData(array $data) + #[\Override] + public function setData(array $data): static { $this->isMergingWidgetData = true; @@ -439,7 +427,7 @@ public function setData(array $data) * @param array $data Widget and property data. * @return FormPropertyWidget Chainable */ - public function merge(array $data) + public function merge(array $data): static { $this->isMergingWidgetData = true; @@ -459,7 +447,7 @@ public function merge(array $data) * @throws InvalidArgumentException If the argument is not a string. * @return FormPropertyWidget Chainable */ - public function setPropertyType($type) + public function setPropertyType($type): static { if (empty($type)) { $this->propertyType = null; @@ -494,7 +482,7 @@ public function propertyType() * @throws InvalidArgumentException If the property ident is not a string. * @return FormPropertyWidget Chainable */ - public function setPropertyIdent($propertyIdent) + public function setPropertyIdent($propertyIdent): static { if (!is_string($propertyIdent)) { throw new InvalidArgumentException( @@ -523,7 +511,7 @@ public function propertyIdent() * @param array $data The property configset. * @return FormPropertyWidget Chainable */ - public function setPropertyData(array $data) + public function setPropertyData(array $data): static { $this->propertyData = $data; @@ -544,7 +532,7 @@ public function setPropertyData(array $data) * @param array $data The property configset. * @return FormPropertyWidget Chainable */ - public function mergePropertyData(array $data) + public function mergePropertyData(array $data): static { $this->propertyData = array_replace($this->propertyData, $data); @@ -561,10 +549,8 @@ public function mergePropertyData(array $data) /** * Retrieve the property metadata. - * - * @return array */ - public function propertyData() + public function propertyData(): array { return $this->propertyData; } @@ -575,7 +561,7 @@ public function propertyData() * @param mixed $propertyVal The property value. * @return FormPropertyWidget Chainable */ - public function setPropertyVal($propertyVal) + public function setPropertyVal($propertyVal): static { $this->propertyVal = $propertyVal; @@ -598,9 +584,10 @@ public function propertyVal() * @param boolean $show Show (TRUE) or hide (FALSE) the label. * @return FormPropertyWidget Chainable */ - public function setShowLabel($show) + #[\Override] + public function setShowLabel($show): static { - $this->showLabel = !!$show; + $this->showLabel = (bool) $show; return $this; } @@ -610,20 +597,17 @@ public function setShowLabel($show) * * @return boolean If TRUE or unset, check if there is a label. */ + #[\Override] public function showLabel() { if ($this->showLabel === null) { $prop = $this->property(); $show = $prop['show_label']; - if ($show !== null) { - $this->showLabel = $show; - } else { - $this->showLabel = true; - } + $this->showLabel = $show ?? true; } if ($this->showLabel !== false) { - return !!strval($this->property()['label']); + return (bool) strval($this->property()['label']); } else { return false; } @@ -635,9 +619,9 @@ public function showLabel() * @param boolean $show Show (TRUE) or hide (FALSE) the description. * @return FormPropertyWidget Chainable */ - public function setShowDescription($show) + public function setShowDescription($show): static { - $this->showDescription = !!$show; + $this->showDescription = (bool) $show; return $this; } @@ -652,15 +636,11 @@ public function showDescription() if ($this->showDescription === null) { $prop = $this->property(); $show = $prop['show_description']; - if ($show !== null) { - $this->showDescription = $show; - } else { - $this->showDescription = true; - } + $this->showDescription = $show ?? true; } if ($this->showDescription !== false) { - return !!strval($this->property()['description']); + return (bool) strval($this->property()['description']); } else { return false; } @@ -672,9 +652,9 @@ public function showDescription() * @param boolean|string $show Show (TRUE) or hide (FALSE) the notes. * @return FormPropertyWidget Chainable */ - public function setShowNotes($show) + public function setShowNotes($show): static { - $this->showNotes = ($show === 'above' ? $show : !!$show); + $this->showNotes = ($show === 'above' ? $show : (bool) $show); return $this; } @@ -689,15 +669,11 @@ public function showNotes() if ($this->showNotes === null) { $prop = $this->property(); $show = $prop['show_notes']; - if ($show !== null) { - $this->showNotes = $show; - } else { - $this->showNotes = true; - } + $this->showNotes = $show ?? true; } if ($this->showNotes !== false) { - return !!strval($this->property()['notes']); + return (bool) strval($this->property()['notes']); } else { return false; } @@ -720,13 +696,13 @@ public function showNotesAbove() $notes = $this->property()['notes']; - return !!$notes; + return (bool) $notes; } /** * @return Translation|string|null */ - public function description() + public function description(): string { return $this->renderTemplate((string)$this->property()['description']); } @@ -734,23 +710,17 @@ public function description() /** * @return Translation|string|null */ - public function notes() + public function notes(): string { return $this->renderTemplate((string)$this->property()['notes']); } - /** - * @return boolean - */ - public function hidden() + public function hidden(): bool { return ($this->inputType() === static::HIDDEN_FORM_CONTROL || $this->property()['hidden']); } - /** - * @return boolean - */ - public function editable() + public function editable(): bool { return ($this->inputType() !== static::READONLY_FORM_CONTROL && $this->outputType() === static::PROPERTY_CONTROL); } @@ -765,34 +735,22 @@ public function required() return $this->property()['required']; } - /** - * @return string - */ - public function inputId() + public function inputId(): string { return 'input_id'; } - /** - * @return string - */ - public function inputName() + public function inputName(): string { return 'input_name'; } - /** - * @return string - */ - public function displayId() + public function displayId(): string { return 'display_id'; } - /** - * @return string - */ - public function displayName() + public function displayName(): string { return 'display_name'; } @@ -804,7 +762,7 @@ public function displayName() * @throws InvalidArgumentException If the argument is not a string. * @return FormPropertyWidget Chainable */ - public function setInputType($type) + public function setInputType($type): static { if (empty($type)) { $this->inputType = null; @@ -843,7 +801,7 @@ public function inputType() * @throws InvalidArgumentException If the argument is not a string. * @return FormPropertyWidget Chainable */ - public function setDisplayType($type) + public function setDisplayType($type): static { if (empty($type)) { $this->displayType = null; @@ -881,7 +839,7 @@ public function displayType() * @param PropertyInterface $property The property. * @return FormPropertyWidget Chainable */ - public function setProperty(PropertyInterface $property) + public function setProperty(PropertyInterface $property): static { $this->property = $property; $this->propertyType = $property->type(); @@ -926,10 +884,8 @@ public function prop() /** * Determine if the form control's active language should be displayed. - * - * @return boolean */ - public function showActiveLanguage() + public function showActiveLanguage(): bool { $property = $this->property(); $locales = count($this->translator()->availableLocales()); @@ -945,18 +901,16 @@ public function showActiveLanguage() public function inputNameAsCssClass() { $name = str_replace([ ']', '[' ], [ '', '-' ], $this->propertyIdent()); - $name = $this->camelize($name); - return $name; + return $this->camelize($name); } /** * Set the CSS class name(s) for the `.form-field`. * * @param mixed $cssClass One or more CSS class names. - * @return self */ - public function setFormFieldCssClass($cssClass) + public function setFormFieldCssClass($cssClass): static { $cssClass = array_merge($this->defaultFormFieldCssClasses(), $this->parseCssClasses($cssClass)); $this->formFieldCssClass = array_unique($cssClass); @@ -967,11 +921,10 @@ public function setFormFieldCssClass($cssClass) * Add CSS class name(s) for the `.form-field`. * * @param mixed $cssClass One or more CSS class names. - * @return self */ - public function addFormFieldCssClass($cssClass) + public function addFormFieldCssClass($cssClass): static { - if (empty($this->formFieldCssClass)) { + if ($this->formFieldCssClass === []) { $this->formFieldCssClass = $this->defaultFormFieldCssClasses(); } @@ -982,12 +935,10 @@ public function addFormFieldCssClass($cssClass) /** * Retrieve the CSS class name(s) for the `.form-field`. - * - * @return string */ - public function formFieldCssClass() + public function formFieldCssClass(): string { - if (empty($this->formFieldCssClass)) { + if ($this->formFieldCssClass === []) { $this->formFieldCssClass = $this->defaultFormFieldCssClasses(); } @@ -998,9 +949,8 @@ public function formFieldCssClass() * Set the CSS class name(s) for the `.form-group`. * * @param mixed $cssClass One or more CSS class names. - * @return self */ - public function setFormGroupCssClass($cssClass) + public function setFormGroupCssClass($cssClass): static { $cssClass = array_merge($this->defaultFormGroupCssClasses(), $this->parseCssClasses($cssClass)); $this->formGroupCssClass = array_unique($cssClass); @@ -1011,11 +961,10 @@ public function setFormGroupCssClass($cssClass) * Add CSS class name(s) for the `.form-group`. * * @param mixed $cssClass One or more CSS class names. - * @return self */ - public function addFormGroupCssClass($cssClass) + public function addFormGroupCssClass($cssClass): static { - if (empty($this->formGroupCssClass)) { + if ($this->formGroupCssClass === []) { $this->formGroupCssClass = $this->defaultFormGroupCssClasses(); } @@ -1026,12 +975,10 @@ public function addFormGroupCssClass($cssClass) /** * Retrieve the CSS class name(s) for the `.form-group`. - * - * @return string */ - public function formGroupCssClass() + public function formGroupCssClass(): string { - if (empty($this->formGroupCssClass)) { + if ($this->formGroupCssClass === []) { $this->formGroupCssClass = $this->defaultFormGroupCssClasses(); } @@ -1044,7 +991,7 @@ public function formGroupCssClass() * @param string $mode The L10N display mode. * @return FormPropertyWidget Chainable */ - public function setL10nMode($mode) + public function setL10nMode($mode): static { $this->l10nMode = $mode; return $this; @@ -1062,10 +1009,8 @@ public function l10nMode() /** * Determine if the property should output for each language. - * - * @return boolean */ - public function loopL10n() + public function loopL10n(): bool { return ($this->l10nMode() === 'loop_inputs'); } @@ -1159,6 +1104,7 @@ public function output() * @param Container $container Service container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -1175,7 +1121,7 @@ protected function setDependencies(Container $container) * @param FactoryInterface $factory The factory to create property values. * @return FormPropertyWidget Chainable */ - protected function setPropertyFactory(FactoryInterface $factory) + protected function setPropertyFactory(FactoryInterface $factory): static { $this->propertyFactory = $factory; @@ -1188,7 +1134,7 @@ protected function setPropertyFactory(FactoryInterface $factory) * @param FactoryInterface $factory The factory to create form controls for property values. * @return FormPropertyWidget Chainable */ - protected function setPropertyInputFactory(FactoryInterface $factory) + protected function setPropertyInputFactory(FactoryInterface $factory): static { $this->propertyInputFactory = $factory; @@ -1201,7 +1147,7 @@ protected function setPropertyInputFactory(FactoryInterface $factory) * @param FactoryInterface $factory The factory to create displayable property values. * @return FormPropertyWidget Chainable */ - protected function setPropertyDisplayFactory(FactoryInterface $factory) + protected function setPropertyDisplayFactory(FactoryInterface $factory): static { $this->propertyDisplayFactory = $factory; @@ -1229,7 +1175,7 @@ protected function resolveOutputType($type) } else { throw new InvalidArgumentException(sprintf( 'Invalid form property output type, received %s', - is_object($type) ? get_class($type) : gettype($type) + get_debug_type($type) )); } } @@ -1255,7 +1201,7 @@ public function resolvedOutputType() * * @return string[] */ - protected function defaultFormFieldCssClasses() + protected function defaultFormFieldCssClasses(): array { $classes = [ 'form-field', 'form-field-' . $this->widgetId() ]; @@ -1287,7 +1233,7 @@ protected function defaultFormFieldCssClasses() * * @return string[] */ - protected function defaultFormGroupCssClasses() + protected function defaultFormGroupCssClasses(): array { return [ 'form-group' ]; } @@ -1299,7 +1245,7 @@ protected function defaultFormGroupCssClasses() * @throws InvalidArgumentException If a class name is not a string. * @return string[] */ - protected function parseCssClasses($classes) + protected function parseCssClasses($classes): array { if (is_string($classes)) { $classes = explode(' ', $classes); @@ -1309,7 +1255,7 @@ protected function parseCssClasses($classes) throw new InvalidArgumentException('CSS Class(es) must be a space-delimited string or an array'); } - return array_filter($classes, 'strlen'); + return array_filter($classes, strlen(...)); } /** @@ -1318,7 +1264,7 @@ protected function parseCssClasses($classes) * @param array $data The widget and property data. * @return array Returns the remaining dataset. */ - private function setCoreData(array $data) + private function setCoreData(array $data): array { if (isset($data['ident'])) { $this->setPropertyIdent($data['ident']); @@ -1370,7 +1316,7 @@ private function resolveInputType() /** Attempt input type resolution without instantiating the property, at first. */ $metadata = $this->propertyData(); - if ($metadata) { + if ($metadata !== []) { if (isset($metadata['hidden']) && $metadata['hidden']) { $type = static::HIDDEN_FORM_CONTROL; } @@ -1415,10 +1361,8 @@ private function resolveDisplayType() /** Attempt display type resolution without instantiating the property, at first. */ $metadata = $this->propertyData(); - if ($metadata) { - if (isset($metadata['display_type'])) { - $type = $metadata['display_type']; - } + if ($metadata && isset($metadata['display_type'])) { + $type = $metadata['display_type']; } if ($this->propertyType || $this->property) { @@ -1443,7 +1387,7 @@ private function createProperty() if ($ident && is_string($ident)) { $message = sprintf('Missing property type for property "%s"', $ident); } else { - $message = sprintf('Missing property type'); + $message = 'Missing property type'; } throw new UnexpectedValueException($message); } @@ -1534,7 +1478,7 @@ private function createDisplayProperty() * @param array $data The widget and property data. * @return array Returns the remaining dataset. */ - protected function filterInputPropertyData(array $data) + protected function filterInputPropertyData(array $data): array { unset( $data['formFieldCssClass'], @@ -1552,7 +1496,7 @@ protected function filterInputPropertyData(array $data) * @param array $data The widget and property data. * @return array Returns the remaining dataset. */ - protected function filterDisplayPropertyData(array $data) + protected function filterDisplayPropertyData(array $data): array { return $data; } @@ -1571,9 +1515,8 @@ public function icon() * Set the path to the item's icon associated with the object. * * @param string $icon A path to an image. - * @return self */ - public function setIcon($icon) + public function setIcon($icon): static { $this->icon = $icon; @@ -1582,15 +1525,13 @@ public function setIcon($icon) /** * Check for icon(s) - * - * @return boolean */ - public function hasIcon() + public function hasIcon(): bool { if (is_array($this->icon())) { - return !!count($this->icon()); + return (bool) count($this->icon()); } - return !!$this->icon(); + return (bool) $this->icon(); } } diff --git a/packages/admin/src/Charcoal/Admin/Widget/FormSidebarWidget.php b/packages/admin/src/Charcoal/Admin/Widget/FormSidebarWidget.php index d2189706b..aaf2c3f7f 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/FormSidebarWidget.php +++ b/packages/admin/src/Charcoal/Admin/Widget/FormSidebarWidget.php @@ -39,10 +39,8 @@ class FormSidebarWidget extends AdminWidget implements /** * Store a reference to the parent form widget. - * - * @var FormInterface */ - private $form; + private ?\Charcoal\Ui\Form\FormInterface $form = null; /** * Store the sidebar actions. @@ -74,17 +72,13 @@ class FormSidebarWidget extends AdminWidget implements /** * Customize the shown properties. - * - * @var array */ - private $propertiesOptions = []; + private array $propertiesOptions = []; /** * The title is displayed by default. - * - * @var boolean */ - private $showTitle = true; + private bool $showTitle = true; /** * The sidebar's title. @@ -95,10 +89,8 @@ class FormSidebarWidget extends AdminWidget implements /** * The subtitle is displayed by default. - * - * @var boolean */ - private $showSubtitle = true; + private bool $showSubtitle = true; /** * The sidebar's subtitle. @@ -151,23 +143,22 @@ class FormSidebarWidget extends AdminWidget implements /** * Whether the object is revisionable. - * - * @var boolean */ - private $isObjRevisionable; + private ?bool $isObjRevisionable = null; /** * The required Acl permissions for the whole sidebar. * * @var string[] */ - private $requiredGlobalAclPermissions = []; + private array $requiredGlobalAclPermissions = []; /** * @param array|ArrayInterface $data Class data. * @return FormSidebarWidget Chainable */ - public function setData(array $data) + #[\Override] + public function setData(array $data): static { parent::setData($data); @@ -199,7 +190,7 @@ public function setData(array $data) * @param FormInterface $form The related form widget. * @return FormSidebarWidget Chainable */ - public function setForm(FormInterface $form) + public function setForm(FormInterface $form): static { $this->form = $form; @@ -211,7 +202,7 @@ public function setForm(FormInterface $form) * * @return FormInterface */ - public function form() + public function form(): ?\Charcoal\Ui\Form\FormInterface { return $this->form; } @@ -222,7 +213,7 @@ public function form() * @param array $properties The form's object properties. * @return FormSidebarWidget Chainable */ - public function setSidebarProperties(array $properties) + public function setSidebarProperties(array $properties): static { $this->sidebarProperties = $properties; @@ -241,20 +232,16 @@ public function sidebarProperties() /** * Determine if the sidebar has any object properties. - * - * @return boolean */ - public function hasSidebarProperties() + public function hasSidebarProperties(): bool { return ($this->numSidebarProperties() > 0); } /** * Count the number of object properties in the sidebar. - * - * @return integer */ - public function numSidebarProperties() + public function numSidebarProperties(): int { return count($this->sidebarProperties()); } @@ -265,7 +252,7 @@ public function numSidebarProperties() * @param array $properties The options to customize the group properties. * @return FormSidebarWidget Chainable */ - public function setPropertiesOptions(array $properties) + public function setPropertiesOptions(array $properties): static { $this->propertiesOptions = $properties; @@ -274,10 +261,8 @@ public function setPropertiesOptions(array $properties) /** * Retrieve the map of object property customizations. - * - * @return array */ - public function propertiesOptions() + public function propertiesOptions(): array { return $this->propertiesOptions; } @@ -292,7 +277,7 @@ public function formProperties() $form = $this->form(); $obj = $form->obj(); - $availableProperties = $obj->properties(); + $obj->properties(); $sidebarProperties = $this->sidebarProperties(); $propertiesOptions = $this->propertiesOptions(); @@ -345,9 +330,7 @@ public function showSidebarActions() return false; } - $actions = array_filter($actions, function ($action) { - return $action['active'] === true; - }); + $actions = array_filter($actions, fn(array $action): bool => $action['active'] === true); $this->showSidebarActions = (count($actions) > 0); } @@ -380,7 +363,7 @@ public function sidebarActions() * @param array $actions One or more actions. * @return FormSidebarWidget Chainable. */ - protected function setSidebarActions(array $actions) + protected function setSidebarActions(array $actions): static { $this->parsedSidebarActions = false; @@ -399,22 +382,19 @@ protected function setSidebarActions(array $actions) * @param array $actions Actions to resolve. * @return array Sidebar actions. */ - protected function createSidebarActions(array $actions) + protected function createSidebarActions(array $actions): array { $this->actionsPriority = $this->defaultActionPriority(); - $sidebarActions = $this->parseAsSidebarActions($actions); - - return $sidebarActions; + return $this->parseAsSidebarActions($actions); } /** * Parse the given actions as object actions. * * @param array $actions Actions to resolve. - * @return array */ - protected function parseAsSidebarActions(array $actions) + protected function parseAsSidebarActions(array $actions): array { $sidebarActions = []; foreach ($actions as $ident => $action) { @@ -441,9 +421,7 @@ protected function parseAsSidebarActions(array $actions) if ($action['actions']) { $action['actions'] = $this->parseAsSidebarActions($action['actions']); - $action['hasActions'] = !!array_filter($action['actions'], function ($action) { - return $action['active']; - }); + $action['hasActions'] = (bool) array_filter($action['actions'], fn(array $action): mixed => $action['active']); } if (isset($sidebarActions[$ident])) { @@ -458,7 +436,7 @@ protected function parseAsSidebarActions(array $actions) } } - usort($sidebarActions, [ 'Charcoal\Admin\Support\Sorter', 'sortByPriority' ]); + usort($sidebarActions, \Charcoal\Admin\Support\Sorter::sortByPriority(...)); while (($first = reset($sidebarActions)) && $first['isSeparator']) { array_shift($sidebarActions); @@ -481,7 +459,7 @@ protected function defaultSidebarActions() if ($this->defaultSidebarActions === null) { $this->defaultSidebarActions = []; - if ($this->form()) { + if ($this->form() instanceof \Charcoal\Ui\Form\FormInterface) { $save = [ 'label' => $this->form()->submitLabel(), 'ident' => 'save', @@ -495,10 +473,7 @@ protected function defaultSidebarActions() return $this->defaultSidebarActions; } - /** - * @return string - */ - public function jsActionPrefix() + public function jsActionPrefix(): string { return 'js-sidebar'; } @@ -519,7 +494,7 @@ public function isObjDeletable() $this->isObjDeletable = false; } else { $obj = $this->form()->obj(); - $this->isObjDeletable = !!$obj->id(); + $this->isObjDeletable = (bool) $obj->id(); $method = [ $obj, 'isDeletable' ]; if (is_callable($method)) { @@ -539,7 +514,7 @@ public function isObjDeletable() * * @return boolean */ - public function isObjRevisionable() + public function isObjRevisionable(): ?bool { if ($this->isObjRevisionable === null) { // Overridden by permissions @@ -553,7 +528,7 @@ public function isObjRevisionable() } if ($obj instanceof RevisionableInterface && $obj['revisionEnabled']) { - $this->isObjRevisionable = !!count($obj->allRevisions()); + $this->isObjRevisionable = (bool) count($obj->allRevisions()); } } } @@ -634,7 +609,7 @@ public function isObjViewable() $this->isObjViewable = false; } else { $obj = $this->form()->obj(); - $this->isObjViewable = !!$obj->id(); + $this->isObjViewable = (bool) $obj->id(); $method = [ $obj, 'isViewable' ]; if (is_callable($method)) { @@ -652,9 +627,9 @@ public function isObjViewable() * @param boolean $show Show (TRUE) or hide (FALSE) the title. * @return UiItemInterface Chainable */ - public function setShowTitle($show) + public function setShowTitle($show): static { - $this->showTitle = !!$show; + $this->showTitle = (bool) $show; return $this; } @@ -669,7 +644,7 @@ public function showTitle() if ($this->showTitle === false) { return false; } else { - return !!$this->title(); + return (bool) $this->title(); } } @@ -677,7 +652,7 @@ public function showTitle() * @param mixed $title The sidebar title. * @return FormSidebarWidget Chainable */ - public function setTitle($title) + public function setTitle($title): static { $this->title = $this->translator()->translation($title); @@ -700,9 +675,9 @@ public function title() * @param boolean $show The show subtitle flag. * @return FormSidebarWidget Chainable */ - public function setShowSubtitle($show) + public function setShowSubtitle($show): static { - $this->showSubtitle = !!$show; + $this->showSubtitle = (bool) $show; return $this; } @@ -714,7 +689,7 @@ public function showSubtitle() if ($this->showSubtitle === false) { return false; } else { - return !!$this->subtitle(); + return (bool) $this->subtitle(); } } @@ -722,7 +697,7 @@ public function showSubtitle() * @param mixed $subtitle The sidebar widget subtitle. * @return FormSidebarWidget Chainable */ - public function setSubtitle($subtitle) + public function setSubtitle($subtitle): static { $this->subtitle = $this->translator()->translation($subtitle); @@ -761,31 +736,26 @@ public function showFooter() * Enable / Disable the sidebar's footer. * * @param mixed $show The show footer flag. - * @return FormSidebarWidget */ - public function setShowFooter($show) + public function setShowFooter($show): static { - $this->showFooter = !!$show; + $this->showFooter = (bool) $show; return $this; } /** * Determine if wrapper containing the subtitle and properties should be displayed. - * - * @return boolean */ - public function showPropertiesWrapper() + public function showPropertiesWrapper(): bool { return $this->showSubtitle() || $this->hasSidebarProperties(); } /** * Determine if wrapper containing the language switcher and actions should be displayed. - * - * @return boolean */ - public function showActionsWrapper() + public function showActionsWrapper(): bool { return $this->showLanguageSwitch() || $this->showSidebarActions(); } @@ -797,10 +767,8 @@ public function showActionsWrapper() protected function resolveShowLanguageSwitch() { $form = $this->form(); - if ($form) { - if ($form instanceof LanguageSwitcherAwareInterface) { - return $form->supportsLanguageSwitch(); - } + if ($form && $form instanceof LanguageSwitcherAwareInterface) { + return $form->supportsLanguageSwitch(); } return false; @@ -811,24 +779,24 @@ protected function resolveShowLanguageSwitch() * * @see AdminWidget::resolveConditionalLogic() * @param callable|string $condition The callable or renderable condition. - * @return boolean */ - protected function resolveConditionalLogic($condition) + #[\Override] + protected function resolveConditionalLogic($condition): bool { $renderer = $this->getActionRenderer(); if ($renderer && is_callable([ $renderer, $condition ])) { - return !!$renderer->{$condition}(); + return (bool) $renderer->{$condition}(); } elseif (is_callable([ $this, $condition ])) { - return !!$this->{$condition}(); + return (bool) $this->{$condition}(); } elseif (is_callable($condition)) { - return !!$condition(); - } elseif ($renderer) { - return !!$renderer->renderTemplate($condition); - } elseif ($this->view()) { - return !!$this->renderTemplate($condition); + return (bool) $condition(); + } elseif ($renderer instanceof \Charcoal\View\ViewableInterface) { + return (bool) $renderer->renderTemplate($condition); + } elseif ($this->view() instanceof \Charcoal\View\ViewInterface) { + return (bool) $this->renderTemplate($condition); } - return !!$condition; + return (bool) $condition; } // ACL Permissions @@ -851,26 +819,21 @@ protected function checkPermission($permissionName) } $authUser = $this->authenticator()->user(); - if (!$authUser || !$this->authorizer()->userAllowed($authUser, $permissions)) { - return false; - } - - return true; + return $authUser && $this->authorizer()->userAllowed($authUser, $permissions); } /** * @return string[] */ - public function requiredGlobalAclPermissions() + public function requiredGlobalAclPermissions(): array { return $this->requiredGlobalAclPermissions; } /** * @param array $permissions The GlobalAcl permissions required pby the form group. - * @return self */ - public function setRequiredGlobalAclPermissions(array $permissions) + public function setRequiredGlobalAclPermissions(array $permissions): static { $this->requiredGlobalAclPermissions = $permissions; @@ -892,6 +855,6 @@ protected function isAssoc(array $array) return false; } - return !!array_filter($array, 'is_string', ARRAY_FILTER_USE_KEY); + return (bool) array_filter($array, is_string(...), ARRAY_FILTER_USE_KEY); } } diff --git a/packages/admin/src/Charcoal/Admin/Widget/FormWidget.php b/packages/admin/src/Charcoal/Admin/Widget/FormWidget.php index 233531116..ba099e94b 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/FormWidget.php +++ b/packages/admin/src/Charcoal/Admin/Widget/FormWidget.php @@ -99,16 +99,15 @@ class FormWidget extends AdminWidget implements /** * Store the factory instance for the current class. - * - * @var FactoryInterface */ - private $widgetFactory; + private ?\Charcoal\Factory\FactoryInterface $widgetFactory = null; /** * @param array $data The widget data. * @return FormWidget Chainable */ - public function setData(array $data) + #[\Override] + public function setData(array $data): static { $this->setDefaultFormGroupsTemplate(); $this->setDefaultFormTabsTemplate(); @@ -122,7 +121,7 @@ public function setData(array $data) * @param array $data Optional. The form property data to set. * @return FormPropertyWidget */ - public function createFormProperty(array $data = null) + public function createFormProperty(?array $data = null) { $p = $this->widgetFactory()->create($this->formPropertyClass()); if ($data !== null) { @@ -139,7 +138,7 @@ public function createFormProperty(array $data = null) * @throws InvalidArgumentException If the class name is not a string. * @return FormWidget Chainable */ - protected function setFormPropertyClass($className) + protected function setFormPropertyClass($className): static { if (!is_string($className)) { throw new InvalidArgumentException( @@ -167,7 +166,7 @@ public function formPropertyClass() * @throws InvalidArgumentException If the property is already registered. * @return \Charcoal\Admin\Widget\FormPropertyWidget|mixed */ - public function getOrCreateFormProperty($ident, array $data = null) + public function getOrCreateFormProperty($ident, ?array $data = null) { if ($this->updateFormProperty($ident, $data)) { return $this->formProperties[$ident]; @@ -203,7 +202,7 @@ public function getOrCreateFormProperty($ident, array $data = null) * @throws InvalidArgumentException If the property is already registered. * @return \Charcoal\Admin\Widget\FormPropertyWidget|mixed */ - public function getOrCreateHiddenProperty($ident, array $data = null) + public function getOrCreateHiddenProperty($ident, ?array $data = null) { if ($this->updateHiddenProperty($ident, $data)) { return $this->hiddenProperties[$ident]; @@ -233,7 +232,7 @@ public function getOrCreateHiddenProperty($ident, array $data = null) * @param array $data Property metadata. * @return \Charcoal\Admin\Widget\FormPropertyWidget|mixed */ - protected function buildFormProperty($ident, array $data = null) + protected function buildFormProperty($ident, ?array $data = null) { $formProperty = $this->createFormProperty(); $formProperty->setPropertyIdent($ident); @@ -264,7 +263,7 @@ protected function buildFormProperty($ident, array $data = null) * @param array $data Property metadata. * @return \Charcoal\Admin\Widget\FormPropertyWidget|null */ - protected function updateFormProperty($ident, array $data = null) + protected function updateFormProperty($ident, ?array $data = null) { if ($ident && isset($this->formProperties[$ident])) { $formProperty = $this->formProperties[$ident]; @@ -286,7 +285,7 @@ protected function updateFormProperty($ident, array $data = null) * @param array $data Property metadata. * @return \Charcoal\Admin\Widget\FormPropertyWidget|null */ - protected function updateHiddenProperty($ident, array $data = null) + protected function updateHiddenProperty($ident, ?array $data = null) { if ($ident && isset($this->hiddenProperties[$ident])) { $formProperty = $this->hiddenProperties[$ident]; @@ -306,27 +305,24 @@ protected function updateHiddenProperty($ident, array $data = null) /** * @param string $ident Property ident. - * @return boolean */ - protected function hasFormProperty($ident) + protected function hasFormProperty($ident): bool { return ($ident && isset($this->formProperties[$ident])); } /** * @param string $ident Property ident. - * @return boolean */ - protected function hasHiddenProperty($ident) + protected function hasHiddenProperty($ident): bool { return ($ident && isset($this->hiddenProperties[$ident])); } /** * @param array $sidebars The form sidebars. - * @return self */ - public function setSidebars(array $sidebars) + public function setSidebars(array $sidebars): static { $this->sidebars = []; foreach ($sidebars as $sidebarIdent => $sidebar) { @@ -340,9 +336,8 @@ public function setSidebars(array $sidebars) * @param string $sidebarIdent The sidebar identifier. * @param array|FormSidebarInterface $sidebar The sidebar data or object. * @throws InvalidArgumentException If the ident is not a string or the sidebar is not valid. - * @return self */ - public function addSidebar($sidebarIdent, $sidebar) + public function addSidebar($sidebarIdent, $sidebar): static { if (!is_string($sidebarIdent)) { throw new InvalidArgumentException( @@ -381,10 +376,8 @@ public function addSidebar($sidebarIdent, $sidebar) /** * Determines if any sidebars are defined. - * - * @return boolean */ - public function hasSidebars() + public function hasSidebars(): bool { return (bool)$this->sidebars; } @@ -397,17 +390,13 @@ public function hasSidebars() public function sidebars() { $sidebars = $this->sidebars; - uasort($sidebars, [ $this, 'sortSidebarsByPriority' ]); + uasort($sidebars, $this->sortSidebarsByPriority(...)); foreach ($sidebars as $sidebarIdent => $sidebar) { if (!$sidebar->active()) { continue; } - if ($sidebar->template()) { - $template = $sidebar->template(); - } else { - $template = 'charcoal/admin/widget/form.sidebar'; - } + $template = $sidebar->template() ?: 'charcoal/admin/widget/form.sidebar'; $this->setDynamicTemplate('widget_template', $template); yield $sidebarIdent => $sidebar; @@ -418,9 +407,8 @@ public function sidebars() * Replace property controls to the form. * * @param array $properties The form properties. - * @return self */ - public function setFormProperties(array $properties) + public function setFormProperties(array $properties): static { $this->formProperties = []; @@ -433,9 +421,8 @@ public function setFormProperties(array $properties) * Add property controls to the form. * * @param array $properties The form properties. - * @return self */ - public function addFormProperties(array $properties) + public function addFormProperties(array $properties): static { foreach ($properties as $propertyIdent => $property) { $this->addFormProperty($propertyIdent, $property); @@ -455,7 +442,7 @@ public function addFormProperties(array $properties) * @throws InvalidArgumentException If the identifier or the property is invalid. * @return FormInterface Chainable */ - public function addFormProperty($propertyIdent, $formProperty) + public function addFormProperty($propertyIdent, $formProperty): static { if (!is_string($propertyIdent)) { throw new InvalidArgumentException( @@ -480,7 +467,7 @@ public function addFormProperty($propertyIdent, $formProperty) throw new InvalidArgumentException(sprintf( 'Property must be an array or an instance of FormPropertyWidget, received %s', - is_object($formProperty) ? get_class($formProperty) : gettype($formProperty) + get_debug_type($formProperty) )); } @@ -510,7 +497,7 @@ public function formProperties() * * @return FormPropertyWidget[] */ - public function getFormProperties() + public function getFormProperties(): array { $formProperties = []; foreach ($this->formProperties as $formProperty) { @@ -530,7 +517,7 @@ public function getFormProperties() * @param array $properties The hidden form properties. * @return FormInterface Chainable */ - public function setHiddenProperties(array $properties) + public function setHiddenProperties(array $properties): static { $this->hiddenProperties = []; @@ -545,7 +532,7 @@ public function setHiddenProperties(array $properties) * @param array $properties The hidden form properties. * @return FormInterface Chainable */ - public function addHiddenProperties(array $properties) + public function addHiddenProperties(array $properties): static { foreach ($properties as $propertyIdent => $property) { $this->addHiddenProperty($propertyIdent, $property); @@ -562,7 +549,7 @@ public function addHiddenProperties(array $properties) * @throws InvalidArgumentException If the identifier or the property is invalid. * @return FormInterface Chainable */ - public function addHiddenProperty($propertyIdent, $formProperty) + public function addHiddenProperty($propertyIdent, $formProperty): static { if (!is_string($propertyIdent)) { throw new InvalidArgumentException( @@ -588,7 +575,7 @@ public function addHiddenProperty($propertyIdent, $formProperty) throw new InvalidArgumentException(sprintf( 'Form property must be an array or an instance of FormPropertyWidget, received %s', - is_object($formProperty) ? get_class($formProperty) : gettype($formProperty) + get_debug_type($formProperty) )); } @@ -610,8 +597,6 @@ public function hiddenProperties() /** * Whether a language switcher could be displayed. - * - * @return bool */ public function supportsLanguageSwitch(): bool { @@ -620,10 +605,8 @@ public function supportsLanguageSwitch(): bool /** * Determine if the form has any multilingual properties. - * - * @return boolean */ - public function hasL10nFormProperties() + public function hasL10nFormProperties(): bool { $locales = count($this->translator()->availableLocales()); if ($locales > 1) { @@ -653,9 +636,8 @@ public function submitLabel() /** * @param string|Translation $label The submit label for the form. - * @return self */ - public function setSubmitLabel($label) + public function setSubmitLabel($label): static { $this->submitLabel = $this->translator()->translate($label); @@ -664,18 +646,13 @@ public function setSubmitLabel($label) /** * Retrieve the default label for the form submission button. - * - * @return \Charcoal\Translator\Translation|null */ - public function defaultSubmitLabel() + public function defaultSubmitLabel(): ?\Charcoal\Translator\Translation { return $this->translator()->translation('Save'); } - /** - * @return string - */ - public function defaultGroupType() + public function defaultGroupType(): string { return 'charcoal/admin/widget/form-group/generic'; } @@ -686,7 +663,7 @@ public function defaultGroupType() * @param string $partial The partial template to render the form groups within. * @return FormWidget Chainable */ - public function setGroupsTemplate($partial) + public function setGroupsTemplate(?string $partial): static { $this->setDynamicTemplate('form_groups_template', $partial); $this->groupsTemplate = $partial; @@ -707,18 +684,12 @@ public function groupsTemplate() return $this->groupsTemplate; } - /** - * @return void - */ - public function setDefaultFormGroupsTemplate() + public function setDefaultFormGroupsTemplate(): void { $this->setGroupsTemplate($this->defaultFormGroupsTemplate()); } - /** - * @return string - */ - public function defaultFormGroupsTemplate() + public function defaultFormGroupsTemplate(): string { return 'charcoal/admin/template/form/groups-wrapper'; } @@ -729,7 +700,7 @@ public function defaultFormGroupsTemplate() * @param string $partial The partial template to render the form tabs within. * @return FormWidget Chainable */ - public function setTabsTemplate($partial) + public function setTabsTemplate(?string $partial): static { $this->setDynamicTemplate('form_tabs_template', $partial); $this->tabsTemplate = $partial; @@ -750,18 +721,12 @@ public function tabsTemplate() return $this->tabsTemplate; } - /** - * @return void - */ - public function setDefaultFormTabsTemplate() + public function setDefaultFormTabsTemplate(): void { $this->setTabsTemplate($this->defaultFormTabsTemplate()); } - /** - * @return string - */ - public function defaultFormTabsTemplate() + public function defaultFormTabsTemplate(): string { return 'charcoal/admin/template/form/nav-tabs'; } @@ -770,6 +735,7 @@ public function defaultFormTabsTemplate() * @param Container $container The DI container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -790,14 +756,13 @@ protected function setDependencies(Container $container) * Retrieve the widget factory. * * @throws RuntimeException If the widget factory was not previously set. - * @return FactoryInterface */ - protected function widgetFactory() + protected function widgetFactory(): \Charcoal\Factory\FactoryInterface { - if ($this->widgetFactory === null) { + if (!$this->widgetFactory instanceof \Charcoal\Factory\FactoryInterface) { throw new RuntimeException(sprintf( 'Widget Factory is not defined for "%s"', - get_class($this) + static::class )); } @@ -827,10 +792,8 @@ protected function dataFromRequest() /** * Retrieve the accepted metadata from the current request. - * - * @return array */ - protected function acceptedRequestData() + protected function acceptedRequestData(): array { return [ 'form_ident', @@ -852,15 +815,10 @@ protected function acceptedRequestData() protected function sortItemsByPriority( PrioritizableInterface $a, PrioritizableInterface $b - ) { + ): int { $priorityA = $a->priority(); $priorityB = $b->priority(); - - if ($priorityA === $priorityB) { - return 0; - } - - return ($priorityA < $priorityB) ? (-1) : 1; + return $priorityA <=> $priorityB; } /** @@ -873,14 +831,9 @@ protected function sortItemsByPriority( protected function sortSidebarsByPriority( FormSidebarInterface $a, FormSidebarInterface $b - ) { + ): int { $a = $a->priority(); $b = $b->priority(); - - if ($a === $b) { - return 0; - } - - return ($a < $b) ? (-1) : 1; + return $a <=> $b; } } diff --git a/packages/admin/src/Charcoal/Admin/Widget/Graph/AbstractGraphWidget.php b/packages/admin/src/Charcoal/Admin/Widget/Graph/AbstractGraphWidget.php index faacc7cea..e5442534c 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/Graph/AbstractGraphWidget.php +++ b/packages/admin/src/Charcoal/Admin/Widget/Graph/AbstractGraphWidget.php @@ -164,10 +164,9 @@ public function categoriesJson() /** * Retrieve the widget's data options for JavaScript components. - * - * @return array */ - public function widgetDataForJs() + #[\Override] + public function widgetDataForJs(): array { return [ 'list_actions' => $this->graphActions(), @@ -278,9 +277,7 @@ protected function createGraphActions(array $actions) { $this->actionsPriority = $this->defaultActionPriority(); - $graphActions = $this->parseAsGraphActions($actions); - - return $graphActions; + return $this->parseAsGraphActions($actions); } /** @@ -300,13 +297,11 @@ protected function parseAsGraphActions(array $actions) $action['priority'] = $this->actionsPriority++; } - $action['empty'] = (isset($action['empty']) ? boolval($action['empty']) : false); + $action['empty'] = (isset($action['empty']) && boolval($action['empty'])); if (is_array($action['actions'])) { $action['actions'] = $this->parseAsGraphActions($action['actions']); - $action['hasActions'] = !!array_filter($action['actions'], function ($action) { - return $action['active']; - }); + $action['hasActions'] = (bool) array_filter($action['actions'], fn(array $action): mixed => $action['active']); } if (isset($graphActions[$ident])) { @@ -321,7 +316,7 @@ protected function parseAsGraphActions(array $actions) } } - usort($graphActions, [ 'Charcoal\Admin\Support\Sorter', 'sortByPriority' ]); + usort($graphActions, \Charcoal\Admin\Support\Sorter::sortByPriority(...)); while (($first = reset($graphActions)) && $first['isSeparator']) { array_shift($graphActions); diff --git a/packages/admin/src/Charcoal/Admin/Widget/Graph/AbstractTimeGraphWidget.php b/packages/admin/src/Charcoal/Admin/Widget/Graph/AbstractTimeGraphWidget.php index 6693057f3..26c86bad7 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/Graph/AbstractTimeGraphWidget.php +++ b/packages/admin/src/Charcoal/Admin/Widget/Graph/AbstractTimeGraphWidget.php @@ -27,30 +27,16 @@ abstract class AbstractTimeGraphWidget extends AbstractGraphWidget implements Ti /** * The date grouping type can be "hour", "day" or "month". - * - * @var string $groupingType */ - private $groupingType; + private ?string $groupingType = null; - /** - * @var string $dateFirnat - */ - private $dateFormat; + private ?string $dateFormat = null; - /** - * @var string $sqlDateFormat - */ - private $sqlDateFormat; + private ?string $sqlDateFormat = null; - /** - * @var DateTimeInterface $startDate - */ - private $startDate; + private ?\DateTimeInterface $startDate = null; - /** - * @var DateTimeInterface $endDate - */ - private $endDate; + private ?\DateTimeInterface $endDate = null; /** * @var DateInterval $dateInterval @@ -150,7 +136,7 @@ public function setStartDate($ts) throw new InvalidArgumentException(sprintf( 'Invalid start date: %s', $e->getMessage() - ), $e); + ), $e, $e); } } if (!($ts instanceof DateTimeInterface)) { @@ -184,7 +170,7 @@ public function setEndDate($ts) throw new InvalidArgumentException(sprintf( 'Invalid end date: %s', $e->getMessage() - ), $e); + ), $e, $e); } } if (!($ts instanceof DateTimeInterface)) { diff --git a/packages/admin/src/Charcoal/Admin/Widget/Graph/GraphWidgetInterface.php b/packages/admin/src/Charcoal/Admin/Widget/Graph/GraphWidgetInterface.php index 143d3935f..7b96d5de1 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/Graph/GraphWidgetInterface.php +++ b/packages/admin/src/Charcoal/Admin/Widget/Graph/GraphWidgetInterface.php @@ -1,5 +1,7 @@ showAsCard = !!$show; + $this->showAsCard = (bool) $show; return $this; } @@ -71,9 +71,8 @@ public function getShowAsCard() * Set the input options. * * @param array $options Optional property input settings. - * @return self */ - public function setGraphOptions(array $options) + public function setGraphOptions(array $options): static { $this->graphOptions = array_merge($this->getDefaultGraphOptions(), $options); @@ -91,11 +90,7 @@ public function getGraphOption($key, $default = null) { $options = $this->getGraphOptions(); - if (isset($options[$key])) { - return $options[$key]; - } - - return $default; + return $options[$key] ?? $default; } /** @@ -114,20 +109,17 @@ public function getGraphOptions() /** * Retrieve the default display options. - * - * @return array */ - public function getDefaultGraphOptions() + public function getDefaultGraphOptions(): array { return []; } /** * Retrieve the widget's data options for JavaScript components. - * - * @return array */ - public function widgetDataForJs() + #[\Override] + public function widgetDataForJs(): array { return [ 'graph_options' => $this->getGraphOptions(), diff --git a/packages/admin/src/Charcoal/Admin/Widget/GridStackDashboardWidget.php b/packages/admin/src/Charcoal/Admin/Widget/GridStackDashboardWidget.php index 76ad80e89..7cac9ead2 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/GridStackDashboardWidget.php +++ b/packages/admin/src/Charcoal/Admin/Widget/GridStackDashboardWidget.php @@ -41,6 +41,7 @@ class GridStackDashboardWidget extends AdminWidget implements * @param Container $container The DI container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -55,49 +56,46 @@ protected function setDependencies(Container $container) * @param callable $widgetCallback A callback applied to each widget. * @return UiItemInterface[]|\Generator */ - public function widgets(callable $widgetCallback = null) + public function widgets(?callable $widgetCallback = null) { $widgets = $this->widgets; $gridStack = $this->gridStack() ?: []; $parsedGridStack = []; - array_walk($gridStack, function ($item) use (&$parsedGridStack) { + array_walk($gridStack, function (array $item) use (&$parsedGridStack): void { $parsedGridStack[$item['id']] = $item; }); // Load gridStack from user preferences $user = $this->adminUser(); - $userGridStack = json_decode($user->preferences(), true)['grid_stack'] ?: []; + $userGridStack = json_decode((string) $user->preferences(), true)['grid_stack'] ?: []; $parsedUserGridStack = []; - array_walk($userGridStack, function ($item) use (&$parsedUserGridStack) { + array_walk($userGridStack, function (array $item) use (&$parsedUserGridStack): void { $parsedUserGridStack[$item['id']] = $item; }); - $widgetCallback = isset($widgetCallback) ? $widgetCallback : $this->widgetCallback; + $widgetCallback ??= $this->widgetCallback; foreach ($widgets as $widget) { if (isset($widget['permissions']) && $this instanceof AuthAwareInterface) { $widget->setActive($this->hasPermissions($widget['permissions'])); } - if (!!count($parsedUserGridStack)) { + if ((bool) count($parsedUserGridStack)) { if (isset($parsedUserGridStack[$widget->ident()])) { $widget->setData([ 'grid_stack' => $parsedUserGridStack[$widget->ident()] ]); } - } else { - if (isset($parsedGridStack[$widget->ident()])) { - $gridStack = array_replace_recursive( - $parsedGridStack[$widget->ident()], - $widget['grid_stack'] ?: [] - ); - - $widget->setData([ - 'grid_stack' => $gridStack - ]); - } + } elseif (isset($parsedGridStack[$widget->ident()])) { + $gridStack = array_replace_recursive( + $parsedGridStack[$widget->ident()], + $widget['grid_stack'] ?: [] + ); + $widget->setData([ + 'grid_stack' => $gridStack + ]); } $gridStackDeco = new GridStackWidgetDecorator($widget); @@ -126,9 +124,8 @@ public function gridStack() /** * @param mixed $gridStack GridStack for AdvancedDashboardWidget. - * @return self */ - public function setGridStack($gridStack) + public function setGridStack($gridStack): static { $this->gridStack = $gridStack; @@ -157,7 +154,7 @@ public function adminUser() * @param LayoutBuilder $builder The layout builder, to create customized layout object(s). * @return \Charcoal\Ui\Layout\DashboardInterface Chainable */ - public function setLayoutBuilder(LayoutBuilder $builder) + public function setLayoutBuilder(LayoutBuilder $builder): null { return null; } @@ -166,7 +163,7 @@ public function setLayoutBuilder(LayoutBuilder $builder) * @param LayoutInterface|array $layout The layout object or structure. * @return \Charcoal\Ui\Layout\DashboardInterface Chainable */ - public function setLayout($layout) + public function setLayout($layout): null { return null; } @@ -174,7 +171,7 @@ public function setLayout($layout) /** * @return LayoutInterface */ - public function layout() + public function layout(): null { return null; } diff --git a/packages/admin/src/Charcoal/Admin/Widget/HierarchicalTableWidget.php b/packages/admin/src/Charcoal/Admin/Widget/HierarchicalTableWidget.php index f8e6ba872..290d02db8 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/HierarchicalTableWidget.php +++ b/packages/admin/src/Charcoal/Admin/Widget/HierarchicalTableWidget.php @@ -19,10 +19,9 @@ class HierarchicalTableWidget extends TableWidget { /** * Provide a template to fullfill UIItem interface. - * - * @return string */ - public function template() + #[\Override] + public function template(): string { return 'charcoal/admin/widget/table'; } @@ -36,6 +35,7 @@ public function template() * @param PropertyInterface $property The current property. * @return void */ + #[\Override] protected function setupDisplayPropertyValue( ModelInterface $object, PropertyInterface $property @@ -53,6 +53,7 @@ protected function setupDisplayPropertyValue( * @see \Charcoal\Admin\Ui\CollectionContainerTrait::sortObjects() * @return array */ + #[\Override] public function sortObjects() { $collection = new HierarchicalCollection($this->objects(), false); diff --git a/packages/admin/src/Charcoal/Admin/Widget/LayoutWidget.php b/packages/admin/src/Charcoal/Admin/Widget/LayoutWidget.php index ad496119b..198bda638 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/LayoutWidget.php +++ b/packages/admin/src/Charcoal/Admin/Widget/LayoutWidget.php @@ -1,5 +1,7 @@ null, @@ -89,9 +77,8 @@ public function widgetDataForJs() * * @param string|null $key The latitude property ident. * @throws InvalidArgumentException If the property key is not a string. - * @return self */ - public function setLatProperty($key) + public function setLatProperty($key): static { if ($key === null) { $this->latProperty = $key; @@ -110,10 +97,8 @@ public function setLatProperty($key) /** * Retrieve the $obj property key for the latitude. - * - * @return string|null */ - public function latProperty() + public function latProperty(): ?string { return $this->latProperty; } @@ -123,9 +108,8 @@ public function latProperty() * * @param string|null $key The longitude property key. * @throws InvalidArgumentException If the property key is not a string. - * @return self */ - public function setLngProperty($key) + public function setLngProperty($key): static { if ($key === null) { $this->lngProperty = $key; @@ -145,11 +129,10 @@ public function setLngProperty($key) /** * Set the $obj property key for the longitude. * - * @deprecated In favour of {@see self::setLngProperty()}. * @param string $key The longitude property key. - * @return self */ - public function setLonProperty($key) + #[\Deprecated(message: 'In favour of {@see self::setLngProperty()}.')] + public function setLonProperty($key): static { $this->logger->warning( 'MapWidget "lon_property" is deprecated. Use "lng_property".', @@ -161,10 +144,8 @@ public function setLonProperty($key) /** * Retrieve the $obj property key for the longitude. - * - * @return string|null */ - public function lngProperty() + public function lngProperty(): ?string { return $this->lngProperty; } @@ -174,9 +155,8 @@ public function lngProperty() * * @param float $coord The latitude of a location. * @throws InvalidArgumentException If the longitude is not a number. - * @return self */ - public function setLat($coord) + public function setLat($coord): static { if ($coord === null) { $this->lat = $coord; @@ -217,9 +197,8 @@ public function lat() * * @param float $coord The longitude of a location. * @throws InvalidArgumentException If the longitude is not a number. - * @return self */ - public function setLng($coord) + public function setLng($coord): static { if ($coord === null) { $this->lng = $coord; @@ -258,9 +237,9 @@ public function lng() /** * Set the $obj property key for the longitude. * - * @deprecated In favour of {@see self::lng()}. * @return self */ + #[\Deprecated(message: 'In favour of {@see self::lng()}.')] public function lon() { $this->logger->warning( @@ -275,7 +254,7 @@ public function lon() * * @return float[]|null */ - public function latLng() + public function latLng(): ?array { $lat = $this->lat(); $lng = $this->lng(); @@ -292,7 +271,7 @@ public function latLng() * * @return float[]|null */ - public function coords() + public function coords(): ?array { $lat = $this->lat(); $lng = $this->lng(); @@ -314,7 +293,7 @@ public function hasObj() if ($this->obj === null) { try { $this->obj(); - } catch (InvalidArgumentException $e) { + } catch (InvalidArgumentException) { return false; } } diff --git a/packages/admin/src/Charcoal/Admin/Widget/ObjectFormWidget.php b/packages/admin/src/Charcoal/Admin/Widget/ObjectFormWidget.php index a76dd8068..59ed05902 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/ObjectFormWidget.php +++ b/packages/admin/src/Charcoal/Admin/Widget/ObjectFormWidget.php @@ -27,6 +27,10 @@ class ObjectFormWidget extends FormWidget implements ObjectContainerInterface { + /** + * @var mixed + */ + public $nextUrl; use ObjectContainerTrait; /** @@ -54,10 +58,7 @@ class ObjectFormWidget extends FormWidget implements */ protected $forcePageReload = false; - /** - * @return string - */ - public function widgetType() + public function widgetType(): string { return 'charcoal/admin/widget/object-form'; } @@ -67,7 +68,8 @@ public function widgetType() * * @return Translation|string|null */ - public function defaultSubmitLabel() + #[\Override] + public function defaultSubmitLabel(): ?\Charcoal\Translator\Translation { if ($this->objId()) { return $this->translator()->translation('Update'); @@ -80,7 +82,8 @@ public function defaultSubmitLabel() * @param array $data The widget data. * @return ObjectFormWidget Chainable */ - public function setData(array $data) + #[\Override] + public function setData(array $data): static { parent::setData($data); @@ -99,7 +102,7 @@ public function setData(array $data) * @throws InvalidArgumentException If the identifier is not a string. * @return ObjectForm Chainable */ - public function setFormIdent($formIdent) + public function setFormIdent($formIdent): static { if (!is_string($formIdent)) { throw new InvalidArgumentException( @@ -147,7 +150,7 @@ public function formIdent() * @throws InvalidArgumentException If argument is not a string. * @return ActionInterface Chainable */ - public function setNextUrl($url) + public function setNextUrl($url): static { if (!is_string($url)) { throw new InvalidArgumentException( @@ -174,9 +177,8 @@ public function allowReload() /** * @param boolean $allowReload AllowReload for ObjectFormWidget. - * @return self */ - public function setAllowReload($allowReload) + public function setAllowReload($allowReload): static { $this->allowReload = $allowReload; @@ -193,9 +195,8 @@ public function forcePageReload() /** * @param boolean $forcePageReload ForcePageReload for ObjectFormWidget. - * @return self */ - public function setForcePageReload($forcePageReload) + public function setForcePageReload($forcePageReload): static { $this->forcePageReload = $forcePageReload; @@ -207,6 +208,7 @@ public function setForcePageReload($forcePageReload) * * @return string Relative URL */ + #[\Override] public function action() { $action = parent::action(); @@ -230,14 +232,15 @@ public function action() * @throws UnexpectedValueException If a property data is invalid. * @return FormPropertyWidget[]|\Generator */ - public function formProperties(array $group = null) + #[\Override] + public function formProperties(?array $group = null) { $obj = $this->obj(); $props = $obj->metadata()->properties(); // We need to sort form properties by form group property order if a group exists - if (!empty($group)) { - $group = array_map([ $this, 'camelize' ], $group); + if ($group !== null && $group !== []) { + $group = array_map($this->camelize(...), $group); $group = array_flip($group); $props = array_intersect_key($props, $group); $props = array_merge($group, $props); @@ -253,7 +256,7 @@ public function formProperties(array $group = null) throw new UnexpectedValueException(sprintf( 'Invalid property data for "%1$s", received %2$s', $propertyIdent, - (is_object($propertyMetadata) ? get_class($propertyMetadata) : gettype($propertyMetadata)) + (get_debug_type($propertyMetadata)) )); } @@ -293,13 +296,11 @@ public function formProperty($propertyIdent) throw new UnexpectedValueException(sprintf( 'Invalid property data for "%1$s", received %2$s', $propertyIdent, - (is_object($propertyMetadata) ? get_class($propertyMetadata) : gettype($propertyMetadata)) + (get_debug_type($propertyMetadata)) )); } - $p = $this->getOrCreateFormProperty($propertyIdent, $propertyMetadata); - - return $p; + return $this->getOrCreateFormProperty($propertyIdent, $propertyMetadata); } /** @@ -311,19 +312,14 @@ public function formProperty($propertyIdent) * @param array $data Data. * @return ObjectFormWidget Chainable. */ - public function setFormData(array $data) + #[\Override] + public function setFormData(array $data): static { $objData = $this->objData(); $merged = array_replace_recursive($objData, $data); // Remove null values - $merged = array_filter($merged, function ($val) { - if ($val === null) { - return false; - } - - return true; - }); + $merged = array_filter($merged, fn($val): bool => $val !== null); $this->formData = $merged; $this->obj()->setData($merged); @@ -336,6 +332,7 @@ public function setFormData(array $data) * * @return array */ + #[\Override] public function formData() { if (!$this->formData) { @@ -356,10 +353,9 @@ public function objData() /** * Retrieve the widget's data options for JavaScript components. - * - * @return array */ - public function widgetDataForJs() + #[\Override] + public function widgetDataForJs(): array { return [ 'obj_id' => $this->objId(), @@ -378,9 +374,8 @@ public function widgetDataForJs() * Self recursive when a groups is an instance of FormInterface. * * @param array|null $groups Form groups to parse. - * @return array */ - protected function groupsConditionalLogic(array $groups = null) + protected function groupsConditionalLogic(?array $groups = null): array { if (!$groups) { $groups = iterator_to_array($this->groups()); @@ -391,7 +386,7 @@ protected function groupsConditionalLogic(array $groups = null) foreach ($groups as $group) { if ($group instanceof FormInterface) { $groupGroups = iterator_to_array($group->groups()); - if (!empty($groupGroups)) { + if ($groupGroups !== []) { $conditions = array_merge( $conditions, $this->groupsConditionalLogic($groupGroups) @@ -411,6 +406,7 @@ protected function groupsConditionalLogic(array $groups = null) * @param Container $container The DI container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -424,17 +420,17 @@ protected function setDependencies(Container $container) * * @return string[] */ - protected function defaultDataSources() + #[\Override] + protected function defaultDataSources(): array { return [static::DATA_SOURCE_REQUEST, static::DATA_SOURCE_OBJECT]; } /** * Retrieve the default data source filters (when setting data on an entity). - * - * @return array */ - protected function defaultDataSourceFilters() + #[\Override] + protected function defaultDataSourceFilters(): array { return [ 'request' => null, @@ -451,6 +447,7 @@ protected function defaultDataSourceFilters() * @param mixed $toResolve A callable used when merging data. * @return callable|null */ + #[\Override] protected function resolveDataSourceFilter($toResolve) { if (is_string($toResolve)) { @@ -477,10 +474,9 @@ protected function resolveDataSourceFilter($toResolve) /** * Retrieve the accepted metadata from the current request. - * - * @return array */ - protected function acceptedRequestData() + #[\Override] + protected function acceptedRequestData(): array { return array_merge([ 'obj_type', @@ -498,7 +494,7 @@ protected function dataFromObject() { $obj = $this->obj(); $objMetadata = $obj->metadata(); - $adminMetadata = (isset($objMetadata['admin']) ? $objMetadata['admin'] : null); + $adminMetadata = ($objMetadata['admin'] ?? null); $formIdent = $this->formIdent(); if (!$formIdent) { @@ -509,11 +505,7 @@ protected function dataFromObject() $formIdent = $obj->render($formIdent); } - if (isset($adminMetadata['forms'][$formIdent])) { - $objFormData = $adminMetadata['forms'][$formIdent]; - } else { - $objFormData = []; - } + $objFormData = $adminMetadata['forms'][$formIdent] ?? []; $formGroups = []; @@ -525,7 +517,7 @@ protected function dataFromObject() $formGroups = array_merge($formGroups, $adminMetadata['formGroups']); } - if (isset($objFormData['groups']) && !empty($formGroups)) { + if (isset($objFormData['groups']) && $formGroups !== []) { $extraFormGroups = array_intersect( array_keys($formGroups), array_keys($objFormData['groups']) @@ -548,7 +540,7 @@ protected function dataFromObject() $formSidebars = array_merge($formSidebars, $adminMetadata['formSidebars']); } - if (isset($objFormData['sidebars']) && !empty($formSidebars)) { + if (isset($objFormData['sidebars']) && $formSidebars !== []) { $extraFormSidebars = array_intersect( array_keys($formSidebars), array_keys($objFormData['sidebars']) @@ -566,9 +558,8 @@ protected function dataFromObject() /** * Whether a language switcher could be displayed. - * - * @return bool */ + #[\Override] public function supportsLanguageSwitch(): bool { if ($this->validateObjType()) { @@ -593,10 +584,9 @@ public function supportsLanguageSwitch(): bool /** * Determine if the form has any multilingual properties. - * - * @return boolean */ - public function hasL10nFormProperties() + #[\Override] + public function hasL10nFormProperties(): bool { if ($this->validateObjType()) { $locales = count($this->translator()->availableLocales()); @@ -616,11 +606,8 @@ public function hasL10nFormProperties() /** * Determine if the group has any multilingual properties. - * - * @param FormGroupInterface $group - * @return bool */ - protected function hasL10nGroupProperties(FormGroupInterface $group) + protected function hasL10nGroupProperties(FormGroupInterface $group): bool { if ($group instanceof AdminFormGroupInterface) { foreach ($group->groupProperties() as $prop) { @@ -635,10 +622,6 @@ protected function hasL10nGroupProperties(FormGroupInterface $group) /** * Determine if the model property is multilingual. - * - * @param ModelInterface $model - * @param string $propertyIdent - * @return ?bool */ protected function isL10nModelProperty(ModelInterface $model, string $propertyIdent): ?bool { @@ -667,6 +650,7 @@ protected function isL10nModelProperty(ModelInterface $model, string $propertyId * @throws InvalidArgumentException If the identifier is not a string or the group is invalid. * @return FormGroupInterface */ + #[\Override] protected function parseFormGroup($groupIdent, $group) { $group = parent::parseFormGroup($groupIdent, $group); @@ -680,10 +664,8 @@ protected function parseFormGroup($groupIdent, $group) /** * Yield the form's property controls. - * - * @return array */ - public function parseFormProperties() + public function parseFormProperties(): array { $props = []; foreach ($this->formProperties as $k => $v) { @@ -699,13 +681,10 @@ public function parseFormProperties() * @param array|null $data Optional. The form group data to set. * @return FormGroupInterface */ - protected function createFormGroup(array $data = null) + #[\Override] + protected function createFormGroup(?array $data = null) { - if (isset($data['type'])) { - $type = $data['type']; - } else { - $type = $this->defaultGroupType(); - } + $type = $data['type'] ?? $this->defaultGroupType(); $group = $this->formGroupFactory()->create($type); $group->setForm($this->formWidget()); @@ -734,13 +713,13 @@ protected function createFormGroup(array $data = null) * @param FormGroupInterface $group The form group to update. * @param array|null $groupData Optional. The new group data to apply. * @param string|null $groupIdent Optional. The new group identifier. - * @return FormGroupInterface */ + #[\Override] protected function updateFormGroup( FormGroupInterface $group, - array $groupData = null, + ?array $groupData = null, $groupIdent = null - ) { + ): FormGroupInterface { $group->setForm($this); if ($groupIdent !== null) { diff --git a/packages/admin/src/Charcoal/Admin/Widget/ObjectRevisionsWidget.php b/packages/admin/src/Charcoal/Admin/Widget/ObjectRevisionsWidget.php index cb482ded4..d5239e223 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/ObjectRevisionsWidget.php +++ b/packages/admin/src/Charcoal/Admin/Widget/ObjectRevisionsWidget.php @@ -1,5 +1,7 @@ objType() && $this->objId(); } @@ -45,9 +45,8 @@ public function objType() /** * @param string $objType ObjType for ObjectRevisionsWidget. - * @return self */ - public function setObjType($objType) + public function setObjType($objType): static { $this->objType = $objType; @@ -64,9 +63,8 @@ public function objId() /** * @param string|integer $objId ObjId for ObjectRevisionsWidget. - * @return self */ - public function setObjId($objId) + public function setObjId($objId): static { $this->objId = $objId; @@ -78,7 +76,8 @@ public function setObjId($objId) * * @return string[] */ - protected function defaultDataSources() + #[\Override] + protected function defaultDataSources(): array { return [ static::DATA_SOURCE_REQUEST, @@ -88,15 +87,9 @@ protected function defaultDataSources() /** * Retrieve the accepted metadata from the current request. - * - * @return array */ - protected function acceptedRequestData() + protected function acceptedRequestData(): array { - return array_merge([ - 'obj_type', - 'obj_id', - 'template', - ]); + return ['obj_type', 'obj_id', 'template']; } } diff --git a/packages/admin/src/Charcoal/Admin/Widget/PaginationWidget.php b/packages/admin/src/Charcoal/Admin/Widget/PaginationWidget.php index b72ce6473..bb08a46a7 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/PaginationWidget.php +++ b/packages/admin/src/Charcoal/Admin/Widget/PaginationWidget.php @@ -21,17 +21,13 @@ class PaginationWidget extends AdminWidget /** * The pager object. - * - * @var PaginationInterface */ - private $pager; + private ?\Charcoal\Source\Pagination $pager = null; /** * The total number of items. - * - * @var integer */ - private $numTotal; + private ?int $numTotal = null; /** * @var integer @@ -43,9 +39,9 @@ class PaginationWidget extends AdminWidget * * @return PaginationInterface */ - protected function pager() + protected function pager(): \Charcoal\Source\Pagination { - if ($this->pager === null) { + if (!$this->pager instanceof \Charcoal\Source\Pagination) { $this->pager = $this->createPagination(); } @@ -57,19 +53,17 @@ protected function pager() * * @return PaginationInterface */ - protected function createPagination() + protected function createPagination(): \Charcoal\Source\Pagination { - $pagination = new Pagination(); - return $pagination; + return new Pagination(); } /** * Set the page number. * * @param integer $page The current page. Pages should start at 1. - * @return self */ - public function setPage($page) + public function setPage($page): static { $this->pager()->setPage($page); @@ -88,20 +82,16 @@ public function page() /** * Retrieve the previous page number. - * - * @return integer */ - public function pagePrev() + public function pagePrev(): int { return max(1, ($this->page() - 1)); } /** * Retrieve the next page number. - * - * @return integer */ - public function pageNext() + public function pageNext(): int { return min($this->numPages(), ($this->page() + 1)); } @@ -111,9 +101,8 @@ public function pageNext() * * @param integer $count The number of results to return, per page. * Use 0 to request all results. - * @return self */ - public function setNumPerPage($count) + public function setNumPerPage($count): static { $this->pager()->setNumPerPage($count); @@ -135,7 +124,7 @@ public function numPerPage() * * @return integer */ - public function numTotal() + public function numTotal(): ?int { return $this->numTotal; } @@ -145,9 +134,8 @@ public function numTotal() * * @param integer $total The total number of items. * @throws InvalidArgumentException If the argument is not a number or lower than 0. - * @return self */ - public function setNumTotal($total) + public function setNumTotal($total): static { if (!is_numeric($total)) { throw new InvalidArgumentException( @@ -167,10 +155,7 @@ public function setNumTotal($total) return $this; } - /** - * @return array - */ - public function pages() + public function pages(): array { if ($this->quickJumpEnabled()) { return $this->buildQuickJumpForm(); @@ -189,12 +174,8 @@ public function pages() return $out; } - /** - * @return array - */ - private function buildQuickJumpForm() + private function buildQuickJumpForm(): array { - $out = []; $i = 1; $numPages = $this->numPages(); $maxPageCount = $this->maxPageCount(); @@ -253,16 +234,13 @@ private function buildQuickJumpForm() } } - $out = array_merge($left, $middle, $right); - - return $out; + return array_merge($left, $middle, $right); } /** * @param integer $page The page index. - * @return array */ - private function formatPage($page) + private function formatPage($page): array { return [ 'separator' => false, @@ -276,7 +254,7 @@ private function formatPage($page) * * @return integer */ - public function numPages() + public function numPages(): int|float { if ($this->numPerPage() == 0) { return 1; @@ -295,9 +273,8 @@ public function maxPageCount() /** * @param integer $maxPageCount The maximum number of page to display in pager at the same time. - * @return PaginationWidget */ - public function setMaxPageCount($maxPageCount) + public function setMaxPageCount($maxPageCount): static { $this->maxPageCount = $maxPageCount; @@ -307,40 +284,32 @@ public function setMaxPageCount($maxPageCount) /** * Determine if pagination can be displayed. - * - * @return boolean */ - public function showPagination() + public function showPagination(): bool { return ($this->numPages() > 1); } /** * Determine if the "previous page" link can be displayed. - * - * @return boolean */ - public function previousEnabled() + public function previousEnabled(): bool { return ($this->page() > 1); } /** * Determine if the "next page" link can be displayed. - * - * @return boolean */ - public function nextEnabled() + public function nextEnabled(): bool { return ($this->page() < $this->numPages()); } /** * His the quick jump input allowed? - * - * @return boolean */ - public function quickJumpEnabled() + public function quickJumpEnabled(): bool { return ($this->numPages() > $this->maxPageCount()); } diff --git a/packages/admin/src/Charcoal/Admin/Widget/QuickFormWidget.php b/packages/admin/src/Charcoal/Admin/Widget/QuickFormWidget.php index 8db61685b..c6cfaa9ed 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/QuickFormWidget.php +++ b/packages/admin/src/Charcoal/Admin/Widget/QuickFormWidget.php @@ -1,5 +1,7 @@ obj()->metadata(); @@ -60,7 +63,7 @@ public function formIdentFallback() if (isset($this->formData()['form_ident'])) { $ident = $this->formData()['form_ident']; - if (is_string($ident) && !empty($ident)) { + if (is_string($ident) && ($ident !== '' && $ident !== '0')) { return $ident; } } @@ -70,10 +73,9 @@ public function formIdentFallback() /** * Retrieve the widget's data options for JavaScript components. - * - * @return array */ - public function widgetDataForJs() + #[\Override] + public function widgetDataForJs(): array { return array_merge_recursive( parent::widgetDataForJs(), @@ -89,6 +91,7 @@ public function widgetDataForJs() * * @return \Charcoal\Translator\Translation|string|null */ + #[\Override] public function submitLabel() { if (isset($this->formData()['submit_label'])) { @@ -101,37 +104,30 @@ public function submitLabel() /** * @see HasLanguageSwitcherTrait::showLanguageSwitch() - * @return boolean */ - protected function resolveShowLanguageSwitch() + protected function resolveShowLanguageSwitch(): bool { return $this->supportsLanguageSwitch(); } /** * Determine if content groups are to be displayed as languages tabbable panes. - * - * @return boolean */ - public function isDisplayModeLang() + public function isDisplayModeLang(): bool { return ($this->groupDisplayMode() === self::DISPLAY_MODE_LANG); } /** * Determine if content groups are to be displayed as tabbable panes. - * - * @return boolean */ - public function isTabbable() + #[\Override] + public function isTabbable(): bool { return in_array($this->groupDisplayMode(), $this->getTabbableDisplayModes()); } - /** - * @return array - */ - public function getTabbableDisplayModes() + public function getTabbableDisplayModes(): array { return [ self::DISPLAY_MODE_TAB, @@ -147,16 +143,14 @@ public function availableLanguagesAsJson() $options = (JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); if ($this->debug()) { - $options = ($options | JSON_PRETTY_PRINT); + $options |= JSON_PRETTY_PRINT; } return json_encode($this->languages(), $options); } - /** - * @return string - */ - public function defaultFormTabsTemplate() + #[\Override] + public function defaultFormTabsTemplate(): string { return 'charcoal/admin/template/form/nav-tabs'; } diff --git a/packages/admin/src/Charcoal/Admin/Widget/SearchWidget.php b/packages/admin/src/Charcoal/Admin/Widget/SearchWidget.php index dacdb82bc..0c244b050 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/SearchWidget.php +++ b/packages/admin/src/Charcoal/Admin/Widget/SearchWidget.php @@ -26,9 +26,9 @@ class SearchWidget extends AdminWidget implements CollectionContainerInterface /** * @param array $data The search widget data. - * @return self */ - public function setData(array $data) + #[\Override] + public function setData(array $data): static { if (isset($data['obj_type'])) { $this->setObjType($data['obj_type']); @@ -51,10 +51,10 @@ public function dataFromObject() { $obj = $this->proto(); $metadata = $obj->metadata(); - $adminMetadata = isset($metadata['admin']) ? $metadata['admin'] : null; + $adminMetadata = $metadata['admin'] ?? null; $collectionIdent = $this->collectionIdent(); if (!$collectionIdent) { - $collectionIdent = isset($adminMetadata['default_list']) ? $adminMetadata['default_list'] : ''; + $collectionIdent = $adminMetadata['default_list'] ?? ''; } if (isset($adminMetadata['lists'][$collectionIdent])) { @@ -80,7 +80,7 @@ public function properties() $collectionIdent = $this->collectionIdent(); if ($collectionIdent) { $metadata = $model->metadata(); - $adminMetadata = isset($metadata['admin']) ? $metadata['admin'] : null; + $adminMetadata = $metadata['admin'] ?? null; if (isset($adminMetadata['lists'][$collectionIdent]['properties'])) { // Flipping to have property ident as key @@ -119,10 +119,9 @@ public function propertiesIdents() /** * Retrieve the widget's data options for JavaScript components. - * - * @return array */ - public function widgetDataForJs() + #[\Override] + public function widgetDataForJs(): array { return [ 'obj_type' => $this->objType(), diff --git a/packages/admin/src/Charcoal/Admin/Widget/SecondaryMenuWidget.php b/packages/admin/src/Charcoal/Admin/Widget/SecondaryMenuWidget.php index 96dc4bc25..b608699cc 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/SecondaryMenuWidget.php +++ b/packages/admin/src/Charcoal/Admin/Widget/SecondaryMenuWidget.php @@ -24,6 +24,7 @@ class SecondaryMenuWidget extends AdminWidget implements SecondaryMenuWidgetInterface { + public $isCurrent; use ActionContainerTrait; use HttpAwareTrait; @@ -71,31 +72,23 @@ class SecondaryMenuWidget extends AdminWidget implements /** * Whether the group is collapsed or not. - * - * @var boolean */ - private $collapsed = false; + private bool $collapsed = false; /** * Whether the group has siblings or not. - * - * @var boolean */ - private $parented = false; + private bool $parented = false; /** * The title is displayed by default. - * - * @var boolean */ - private $showTitle = true; + private bool $showTitle = true; /** * The description is displayed by default. - * - * @var boolean */ - private $showDescription = true; + private bool $showDescription = true; /** * The currently highlighted item. @@ -148,9 +141,9 @@ class SecondaryMenuWidget extends AdminWidget implements /** * @param array $data Class data. - * @return self */ - public function setData(array $data) + #[\Override] + public function setData(array $data): static { if (isset($data['actions'])) { @@ -242,9 +235,8 @@ public function adminRoute() /** * @param string $ident The ident for the current item to highlight. - * @return self */ - public function setCurrentItem($ident) + public function setCurrentItem($ident): static { $this->currentItem = $ident; return $this; @@ -262,9 +254,8 @@ public function currentItem() * Computes the intersection of values to determine if the link is the current item. * * @param mixed $linkIdent The link's value(s) to check. - * @return boolean */ - public function isCurrentItem($linkIdent) + public function isCurrentItem($linkIdent): bool { $context = array_filter([ $this->currentItem(), @@ -274,7 +265,7 @@ public function isCurrentItem($linkIdent) $matches = array_intersect((array)$linkIdent, $context); - return !!$matches; + return (bool) $matches; } /** @@ -291,11 +282,10 @@ public function objType() * Show/hide the widget's title. * * @param boolean $show Show (TRUE) or hide (FALSE) the title. - * @return self */ - public function setShowTitle($show) + public function setShowTitle($show): static { - $this->showTitle = !!$show; + $this->showTitle = (bool) $show; return $this; } @@ -310,7 +300,7 @@ public function showTitle() if ($this->showTitle === false) { return false; } else { - return !!$this->title(); + return (bool) $this->title(); } } @@ -318,9 +308,8 @@ public function showTitle() * Set the title of the secondary menu. * * @param mixed $title A title for the secondary menu. - * @return self */ - public function setTitle($title) + public function setTitle($title): static { $this->title = $this->translator()->translation($title); @@ -352,9 +341,8 @@ public function title() * Set the secondary menu links. * * @param array $links A collection of link objects. - * @return self */ - public function setLinks(array $links) + public function setLinks(array $links): static { $this->links = new ArrayIterator(); @@ -371,9 +359,8 @@ public function setLinks(array $links) * @param string $linkIdent The link identifier. * @param array|object $link The link object or structure. * @throws InvalidArgumentException If the link is invalid. - * @return self */ - public function addLink($linkIdent, $link) + public function addLink($linkIdent, $link): static { if (!is_string($linkIdent) && !is_numeric($linkIdent)) { throw new InvalidArgumentException( @@ -394,7 +381,7 @@ public function addLink($linkIdent, $link) } if (isset($link['active'])) { - $active = !!$link['active']; + $active = (bool) $link['active']; } if (isset($link['name'])) { @@ -423,7 +410,7 @@ public function addLink($linkIdent, $link) } else { throw new InvalidArgumentException(sprintf( 'Link must be an associative array, received %s', - (is_object($link) ? get_class($link) : gettype($link)) + (get_debug_type($link)) )); } @@ -463,10 +450,8 @@ public function links() unset($link['required_acl_permissions']); } - if (isset($link['permissions'])) { - if ($this->hasPermissions($link['permissions']) === false) { - continue; - } + if (isset($link['permissions']) && $this->hasPermissions($link['permissions']) === false) { + continue; } $out[] = $link; @@ -481,9 +466,8 @@ public function links() * * @param mixed $type The display type. * @throws InvalidArgumentException If the display type is invalid. - * @return self */ - public function setDisplayType($type) + public function setDisplayType($type): static { if (!is_string($type)) { throw new InvalidArgumentException('The display type must be a string.'); @@ -517,20 +501,16 @@ public function displayType() /** * Determine if the secondary menu groups should be displayed as panels. - * - * @return boolean */ - public function displayAsPanel() + public function displayAsPanel(): bool { return in_array($this->displayType(), [ 'panel', 'collapsible' ]); } /** * Determine if the display type is "collapsible". - * - * @return boolean */ - public function collapsible() + public function collapsible(): bool { return ($this->displayType() === 'collapsible'); } @@ -540,9 +520,8 @@ public function collapsible() * * @param array $options Display configuration. * @throws InvalidArgumentException If the display options are not an associative array. - * @return self */ - public function setDisplayOptions(array $options) + public function setDisplayOptions(array $options): static { $this->displayOptions = array_replace($this->defaultDisplayOptions(), $options); @@ -579,10 +558,8 @@ public function displayOptions() /** * Retrieve the default display options for the secondary menu. - * - * @return array */ - public function defaultDisplayOptions() + public function defaultDisplayOptions(): array { return [ 'parented' => false, @@ -618,9 +595,8 @@ public function collapsed() * Set the secondary menu's groups. * * @param array $groups A collection of group structures. - * @return self */ - public function setGroups(array $groups) + public function setGroups(array $groups): static { $this->groups = []; @@ -628,12 +604,10 @@ public function setGroups(array $groups) $this->addGroup($groupIdent, $group); } - uasort($this->groups, [ 'Charcoal\Admin\Support\Sorter', 'sortByPriority' ]); + uasort($this->groups, \Charcoal\Admin\Support\Sorter::sortByPriority(...)); // Remove items that are not active and reset keys. - $this->groups = array_values(array_filter($this->groups, function ($item) { - return ($item->active()); - })); + $this->groups = array_values(array_filter($this->groups, fn($item) => $item->active())); return $this; } @@ -644,9 +618,8 @@ public function setGroups(array $groups) * @param string $groupIdent The group identifier. * @param array|SecondaryMenuGroupInterface $group The group object or structure. * @throws InvalidArgumentException If the identifier is not a string or the group is invalid. - * @return self */ - public function addGroup($groupIdent, $group) + public function addGroup($groupIdent, $group): static { if (!is_string($groupIdent)) { throw new InvalidArgumentException( @@ -695,7 +668,7 @@ public function addGroup($groupIdent, $group) throw new InvalidArgumentException(sprintf( 'Group must be an instance of %s or an array of form group options, received %s', 'SecondaryMenuGroupInterface', - (is_object($group) ? get_class($group) : gettype($group)) + (get_debug_type($group)) )); } @@ -734,36 +707,30 @@ public function groups() /** * Retrieve the default secondary menu group class name. - * - * @return string */ - public function defaultGroupType() + public function defaultGroupType(): string { return 'charcoal/ui/secondary-menu/generic'; } /** * Determine if the secondary menu has any links. - * - * @return boolean */ - public function hasLinks() + public function hasLinks(): bool { - return !!$this->numLinks(); + return (bool) $this->numLinks(); } /** * Count the number of secondary menu links. - * - * @return integer */ - public function numLinks() + public function numLinks(): int { if (!is_array($this->links()) && !($this->links() instanceof \Traversable)) { return 0; } - $links = array_filter($this->links, function ($link) { + $links = array_filter($this->links, function (array $link): bool { if (isset($link['active']) && !$link['active']) { return false; } @@ -772,14 +739,7 @@ public function numLinks() $link['permissions'] = $link['required_acl_permissions']; unset($link['required_acl_permissions']); } - - if (isset($link['permissions'])) { - if ($this->hasPermissions($link['permissions']) === false) { - return false; - } - } - - return true; + return !(isset($link['permissions']) && $this->hasPermissions($link['permissions']) === false); }); return count($links); @@ -787,40 +747,32 @@ public function numLinks() /** * Determine if the secondary menu has any groups of links. - * - * @return boolean */ - public function hasGroups() + public function hasGroups(): bool { - return !!$this->numGroups(); + return (bool) $this->numGroups(); } /** * Count the number of secondary menu groups. - * - * @return integer */ - public function numGroups() + public function numGroups(): int { return count($this->groups()); } /** * Alias for {@see self::showSecondaryMenuActions()} - * - * @return boolean */ - public function hasActions() + public function hasActions(): int { return $this->showSecondaryMenuActions(); } /** * Determine if the secondary menu's actions should be shown. - * - * @return boolean */ - public function showSecondaryMenuActions() + public function showSecondaryMenuActions(): int { $actions = $this->secondaryMenuActions(); @@ -837,11 +789,7 @@ public function secondaryMenuActions() if ($this->secondaryMenuActions === null) { $ident = $this->ident(); $metadata = $this->adminSecondaryMenu(); - if (isset($metadata[$ident]['actions'])) { - $actions = $metadata[$ident]['actions']; - } else { - $actions = []; - } + $actions = $metadata[$ident]['actions'] ?? []; $this->setSecondaryMenuActions($actions); } @@ -857,9 +805,8 @@ public function secondaryMenuActions() * Set the description of the secondary menu. * * @param mixed $description A description for the secondary menu. - * @return self */ - public function setDescription($description) + public function setDescription($description): static { $this->description = $this->translator()->translation($description); @@ -891,11 +838,10 @@ public function description() * Determine if the description is to be displayed. * * @param boolean $show Show (TRUE) or hide (FALSE) the description. - * @return self */ - public function setShowDescription($show) + public function setShowDescription($show): static { - $this->showDescription = !!$show; + $this->showDescription = (bool) $show; return $this; } @@ -909,14 +855,11 @@ public function showDescription() if ($this->showDescription === false) { return false; } else { - return !!$this->description(); + return (bool) $this->description(); } } - /** - * @return string - */ - public function jsActionPrefix() + public function jsActionPrefix(): string { return 'js-secondary-menu'; } @@ -927,6 +870,7 @@ public function jsActionPrefix() * @param Container $container A dependencies container instance. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -951,9 +895,8 @@ public function isCurrent() * Set the widget's display state. * * @param boolean $flag A truthy state. - * @return self */ - protected function setIsCurrent($flag) + protected function setIsCurrent($flag): static { $this->isCurrent = boolval($flag); @@ -968,10 +911,10 @@ protected function setIsCurrent($flag) */ protected function secondaryMenu() { - if (!isset($this->secondaryMenu)) { + if ($this->secondaryMenu === null) { throw new RuntimeException(sprintf( 'Secondary Menu Group Factory is not defined for "%s"', - get_class($this) + static::class )); } @@ -982,9 +925,8 @@ protected function secondaryMenu() * Set the secondary menu's actions. * * @param array $actions One or more actions. - * @return self */ - protected function setSecondaryMenuActions(array $actions) + protected function setSecondaryMenuActions(array $actions): static { $this->parsedSecondaryMenuActions = false; @@ -1003,11 +945,9 @@ protected function setSecondaryMenuActions(array $actions) * @param array $actions Actions to resolve. * @return array Secondary menu actions. */ - protected function createSecondaryMenuActions(array $actions) + protected function createSecondaryMenuActions(array $actions): array { - $secondaryMenuActions = $this->parseActions($actions); - - return $secondaryMenuActions; + return $this->parseActions($actions); } /** @@ -1028,9 +968,8 @@ protected function defaultSecondaryMenuActions() * Set a secondary menu group factory. * * @param FactoryInterface $factory The group factory, to create objects. - * @return void */ - private function setSecondaryMenuGroupFactory(FactoryInterface $factory) + private function setSecondaryMenuGroupFactory(FactoryInterface $factory): void { $this->secondaryMenu = $factory; } diff --git a/packages/admin/src/Charcoal/Admin/Widget/SecondaryMenuWidgetInterface.php b/packages/admin/src/Charcoal/Admin/Widget/SecondaryMenuWidgetInterface.php index cc915305e..8ffa0fe1a 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/SecondaryMenuWidgetInterface.php +++ b/packages/admin/src/Charcoal/Admin/Widget/SecondaryMenuWidgetInterface.php @@ -1,5 +1,7 @@ setObjType($data['obj_type']); @@ -218,10 +210,8 @@ public function dataFromRequest() /** * Retrieve the accepted metadata from the current request. - * - * @return array */ - public function acceptedRequestData() + public function acceptedRequestData(): array { return [ 'obj_type', @@ -241,7 +231,7 @@ public function dataFromObject() { $proto = $this->proto(); $objMetadata = $proto->metadata(); - $adminMetadata = (isset($objMetadata['admin']) ? $objMetadata['admin'] : null); + $adminMetadata = ($objMetadata['admin'] ?? null); if (empty($adminMetadata['lists'])) { return []; @@ -260,11 +250,7 @@ public function dataFromObject() return []; } - if (isset($adminMetadata['lists'][$collectionIdent])) { - $objListData = $adminMetadata['lists'][$collectionIdent]; - } else { - $objListData = []; - } + $objListData = $adminMetadata['lists'][$collectionIdent] ?? []; $collectionConfig = []; @@ -320,7 +306,7 @@ public function dataFromObject() } } - if ($collectionConfig) { + if ($collectionConfig !== []) { $this->mergeCollectionConfig($collectionConfig); } @@ -329,10 +315,9 @@ public function dataFromObject() /** * Retrieve the widget's data options for JavaScript components. - * - * @return array */ - public function widgetDataForJs() + #[\Override] + public function widgetDataForJs(): array { return [ 'obj_type' => $this->objType(), @@ -376,7 +361,7 @@ public function properties() if ($listProperties) { $props = []; foreach ($listProperties as $k => $v) { - $k = lcfirst(implode('', array_map('ucfirst', explode('_', $k)))); + $k = lcfirst(implode('', array_map(ucfirst(...), explode('_', (string) $k)))); $props[$k] = $v; } // Replacing values of listProperties from index to actual property values @@ -398,11 +383,8 @@ public function properties() public function propertiesIdents() { $collectionConfig = $this->collectionConfig(); - if (isset($collectionConfig['properties'])) { - return $collectionConfig['properties']; - } - return []; + return $collectionConfig['properties'] ?? []; } /** @@ -472,11 +454,7 @@ public function collectionProperties() $options = $this->viewOptions($propertyIdent); $classes = $this->parsePropertyCellClasses($p); - if (isset($options['label'])) { - $label = $this->translator()->translate($options['label']); - } else { - $label = strval($p['label']); - } + $label = isset($options['label']) ? $this->translator()->translate($options['label']) : strval($p['label']); $column = [ 'label' => trim($label) @@ -489,24 +467,20 @@ public function collectionProperties() if (isset($options['attr'])) { $column['attr'] = array_merge($column['attr'], $options['attr']); } + if (isset($column['attr']['class'])) { + if (is_string($classes)) { + $classes = explode(' ', $column['attr']['class']); + } - if (isset($classes)) { - if (isset($column['attr']['class'])) { - if (is_string($classes)) { - $classes = explode(' ', $column['attr']['class']); - } - - if (is_string($column['attr']['class'])) { - $column['attr']['class'] = explode(' ', $column['attr']['class']); - } - - $column['attr']['class'] = array_unique(array_merge($column['attr']['class'], $classes)); - } else { - $column['attr']['class'] = $classes; + if (is_string($column['attr']['class'])) { + $column['attr']['class'] = explode(' ', $column['attr']['class']); } - unset($classes); + $column['attr']['class'] = array_unique(array_merge($column['attr']['class'], $classes)); + } else { + $column['attr']['class'] = $classes; } + unset($classes); $column['attr'] = html_build_attributes($column['attr']); @@ -520,19 +494,17 @@ public function collectionProperties() * @param boolean $show Show (TRUE) or hide (FALSE) the actions. * @return TableWidget Chainable */ - public function setShowObjectActions($show) + public function setShowObjectActions($show): static { - $this->showObjectActions = !!$show; + $this->showObjectActions = (bool) $show; return $this; } /** * Determine if the table's object actions should be shown. - * - * @return boolean */ - public function showObjectActions() + public function showObjectActions(): false|int { if ($this->showObjectActions === false) { return false; @@ -543,10 +515,8 @@ public function showObjectActions() /** * Retrieve the table's object actions. - * - * @return array */ - public function objectActions() + public function objectActions(): array { $this->rawObjectActions(); @@ -571,11 +541,7 @@ public function rawObjectActions() $parsed = $this->parsedObjectActions; $collectionConfig = $this->collectionConfig(); - if (isset($collectionConfig['object_actions'])) { - $actions = $collectionConfig['object_actions']; - } else { - $actions = []; - } + $actions = $collectionConfig['object_actions'] ?? []; $this->setObjectActions($actions); @@ -596,7 +562,7 @@ public function rawObjectActions() * @param array $actions One or more actions. * @return TableWidget Chainable. */ - public function setObjectActions(array $actions) + public function setObjectActions(array $actions): static { $this->parsedObjectActions = false; @@ -619,7 +585,7 @@ public function setObjectActions(array $actions) * @param array $actions Actions to resolve. * @return array Object actions. */ - public function createObjectActions(array $actions) + public function createObjectActions(array $actions): array { $this->parsingObjectActions = true; $objectActions = $this->parseActions($actions); @@ -632,9 +598,8 @@ public function createObjectActions(array $actions) * Parse the given actions as (row) object actions. * * @param array $actions Actions to resolve. - * @return array */ - protected function parseAsObjectActions(array $actions) + protected function parseAsObjectActions(array $actions): array { $objectActions = []; foreach ($actions as $action) { @@ -654,9 +619,7 @@ protected function parseAsObjectActions(array $actions) if ($action['actions']) { $action['actions'] = $this->parseAsObjectActions($action['actions']); - $action['hasActions'] = !!array_filter($action['actions'], function ($action) { - return $action['active']; - }); + $action['hasActions'] = (bool) array_filter($action['actions'], fn(array $action): mixed => $action['active']); } $objectActions[] = $action; @@ -667,10 +630,8 @@ protected function parseAsObjectActions(array $actions) /** * Determine if the table's empty collection actions should be shown. - * - * @return boolean */ - public function showEmptyListActions() + public function showEmptyListActions(): int { $actions = $this->emptyListActions(); @@ -679,16 +640,12 @@ public function showEmptyListActions() /** * Retrieve the table's empty collection actions. - * - * @return array */ - public function emptyListActions() + public function emptyListActions(): array { $actions = $this->listActions(); - $filteredArray = array_filter($actions, function ($action) { - return $action['empty'] && $action['active']; - }); + $filteredArray = array_filter($actions, fn(array $action): bool => $action['empty'] && $action['active']); return array_values($filteredArray); } @@ -699,19 +656,17 @@ public function emptyListActions() * @param boolean $show Show (TRUE) or hide (FALSE) the actions. * @return TableWidget Chainable */ - public function setShowListActions($show) + public function setShowListActions($show): static { - $this->showListActions = !!$show; + $this->showListActions = (bool) $show; return $this; } /** * Determine if the table's collection actions should be shown. - * - * @return boolean */ - public function showListActions() + public function showListActions(): false|int { if ($this->showListActions === false) { return false; @@ -729,11 +684,7 @@ public function listActions() { if ($this->listActions === null) { $collectionConfig = $this->collectionConfig(); - if (isset($collectionConfig['list_actions'])) { - $actions = $collectionConfig['list_actions']; - } else { - $actions = []; - } + $actions = $collectionConfig['list_actions'] ?? []; $this->setListActions($actions); } @@ -765,9 +716,9 @@ public function paginationWidget() * @param boolean $show The show flag. * @return TableWidget Chainable */ - public function setShowTableHeader($show) + public function setShowTableHeader($show): static { - $this->showTableHeader = !!$show; + $this->showTableHeader = (bool) $show; return $this; } @@ -784,9 +735,9 @@ public function showTableHeader() * @param boolean $show The show flag. * @return TableWidget Chainable */ - public function setShowTableHead($show) + public function setShowTableHead($show): static { - $this->showTableHead = !!$show; + $this->showTableHead = (bool) $show; return $this; } @@ -803,9 +754,9 @@ public function showTableHead() * @param boolean $show The show flag. * @return TableWidget Chainable */ - public function setShowTableFoot($show) + public function setShowTableFoot($show): static { - $this->showTableFoot = !!$show; + $this->showTableFoot = (bool) $show; return $this; } @@ -822,9 +773,9 @@ public function showTableFoot() * @param boolean $sortable The sortable flag. * @return TableWidget Chainable */ - public function setSortable($sortable) + public function setSortable($sortable): static { - $this->sortable = !!$sortable; + $this->sortable = (bool) $sortable; return $this; } @@ -842,30 +793,21 @@ public function sortable() * * @param mixed $action The action structure. * @param boolean $row Whether to resolve action type for a row. - * @return string */ - protected function resolveActionType($action, $row = false) + protected function resolveActionType(array $action, $row = false): string { if ($row || $this->parsingObjectActions) { - switch ($action['ident']) { - case 'reset': - return 'warning'; - - case 'delete': - return 'danger'; - - default: - return 'seamless'; - } + return match ($action['ident']) { + 'reset' => 'warning', + 'delete' => 'danger', + default => 'seamless', + }; } return $this->resolveDefaultActionType($action); } - /** - * @return string - */ - public function jsActionPrefix() + public function jsActionPrefix(): string { return ($this->currentObj) ? 'js-obj' : 'js-list'; } @@ -880,9 +822,9 @@ public function objectEditUrl() $url = 'object/edit?main_menu={{ main_menu }}&obj_type=' . $this->objType(); if ($this->isObjRenderable($model)) { - $url = $model->render((string)$url); + $url = $model->render($url); } else { - $url = preg_replace('~{{\s*id\s*}}~', $this->currentObjId, $url); + $url = preg_replace('~{{\s*id\s*}}~', (string) $this->currentObjId, $url); } return $url; @@ -897,17 +839,14 @@ public function objectCreateUrl() $actions = $this->listActions(); if ($actions) { foreach ($actions as $action) { - if (isset($action['ident']) && $action['ident'] === 'create') { - if (isset($action['url'])) { - $model = $this->proto(); - if ($this->isObjRenderable($model)) { - $action['url'] = $model->render((string)$action['url']); - } else { - $action['url'] = preg_replace('~{{\s*id\s*}}~', $this->currentObjId, $action['url']); - } - - return $action['url']; + if (isset($action['ident']) && $action['ident'] === 'create' && isset($action['url'])) { + $model = $this->proto(); + if ($this->isObjRenderable($model)) { + $action['url'] = $model->render((string)$action['url']); + } else { + $action['url'] = preg_replace('~{{\s*id\s*}}~', (string) $this->currentObjId, $action['url']); } + return $action['url']; } } } @@ -921,9 +860,9 @@ public function objectCreateUrl() * @param ModelInterface|null $object The object to test. * @return boolean */ - public function isObjActive(ModelInterface $object = null) + public function isObjActive(?ModelInterface $object = null) { - if ($object === null) { + if (!$object instanceof \Charcoal\Model\ModelInterface) { $object = $this->getCurrentObjOrProto(); } @@ -948,9 +887,9 @@ public function isObjActive(ModelInterface $object = null) * @param ModelInterface|null $object The object to test. * @return boolean */ - public function isObjCreatable(ModelInterface $object = null) + public function isObjCreatable(?ModelInterface $object = null) { - if ($object === null) { + if (!$object instanceof \Charcoal\Model\ModelInterface) { $object = $this->proto(); } @@ -971,9 +910,9 @@ public function isObjCreatable(ModelInterface $object = null) * @param ModelInterface|null $object The object to test. * @return boolean */ - public function isObjEditable(ModelInterface $object = null) + public function isObjEditable(?ModelInterface $object = null) { - if ($object === null) { + if (!$object instanceof \Charcoal\Model\ModelInterface) { $object = $this->getCurrentObjOrProto(); } @@ -994,9 +933,9 @@ public function isObjEditable(ModelInterface $object = null) * @param ModelInterface|null $object The object to test. * @return boolean */ - public function isObjDeletable(ModelInterface $object = null) + public function isObjDeletable(?ModelInterface $object = null) { - if ($object === null) { + if (!$object instanceof \Charcoal\Model\ModelInterface) { $object = $this->getCurrentObjOrProto(); } @@ -1017,9 +956,9 @@ public function isObjDeletable(ModelInterface $object = null) * @param ModelInterface|null $object The object to test. * @return boolean */ - public function isObjViewable(ModelInterface $object = null) + public function isObjViewable(?ModelInterface $object = null) { - if ($object === null) { + if (!$object instanceof \Charcoal\Model\ModelInterface) { $object = $this->getCurrentObjOrProto(); } @@ -1039,6 +978,7 @@ public function isObjViewable(ModelInterface $object = null) * @param Container $container Pimple DI container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -1063,27 +1003,23 @@ protected function setDependencies(Container $container) * @param array|null $data Optional collection data. * @return void */ - protected function configureCollectionLoader(CollectionLoader $loader, array $data = null) + protected function configureCollectionLoader(CollectionLoader $loader, ?array $data = null) { $this->configureCollectionLoaderFromTrait($loader, $data); - if (!isset($loader->hasMainMenuCallback)) { + if (!property_exists($loader, 'hasMainMenuCallback') || $loader->hasMainMenuCallback === null) { $mainMenu = filter_input(INPUT_GET, 'main_menu', FILTER_SANITIZE_STRING); if ($mainMenu) { - $fn = function (&$obj) use ($mainMenu) { + $fn = function (array &$obj) use ($mainMenu): void { if (!$obj['main_menu']) { $obj['main_menu'] = $mainMenu; } }; $callback = $loader->callback(); - if ($callback === null) { - $callback = $fn; - } else { - $callback = function (&$obj) use ($fn) { - $fn($obj); - }; - } + $callback = $callback === null ? $fn : function (&$obj) use ($fn): void { + $fn($obj); + }; $loader->setCallback($callback); $loader->hasMainMenuCallback = true; @@ -1095,13 +1031,12 @@ protected function configureCollectionLoader(CollectionLoader $loader, array $da * Retrieve the widget factory. * * @throws RuntimeException If the widget factory was not previously set. - * @return FactoryInterface */ - protected function widgetFactory() + protected function widgetFactory(): \Charcoal\Factory\FactoryInterface { - if ($this->widgetFactory === null) { + if (!$this->widgetFactory instanceof \Charcoal\Factory\FactoryInterface) { throw new RuntimeException( - sprintf('Widget Factory is not defined for "%s"', get_class($this)) + sprintf('Widget Factory is not defined for "%s"', static::class) ); } @@ -1110,11 +1045,10 @@ protected function widgetFactory() /** * @throws RuntimeException If the property factory was not previously set / injected. - * @return FactoryInterface */ - protected function propertyFactory() + protected function propertyFactory(): \Charcoal\Factory\FactoryInterface { - if ($this->propertyFactory === null) { + if (!$this->propertyFactory instanceof \Charcoal\Factory\FactoryInterface) { throw new RuntimeException( 'Property factory is not set for table widget' ); @@ -1132,6 +1066,7 @@ protected function propertyFactory() * @param mixed $toResolve A callable used when merging data. * @return callable|null */ + #[\Override] protected function resolveDataSourceFilter($toResolve) { if (is_string($toResolve)) { @@ -1162,7 +1097,7 @@ protected function resolveDataSourceFilter($toResolve) * @param array $actions One or more actions. * @return TableWidget Chainable. */ - protected function setListActions(array $actions) + protected function setListActions(array $actions): static { $this->parsedListActions = false; @@ -1183,7 +1118,7 @@ protected function setListActions(array $actions) * @param array $actions Actions to resolve. * @return array List actions. */ - protected function createListActions(array $actions) + protected function createListActions(array $actions): array { $this->actionsPriority = $this->defaultActionPriority(); @@ -1198,9 +1133,8 @@ protected function createListActions(array $actions) * Parse the given actions as collection actions. * * @param array $actions Actions to resolve. - * @return array */ - protected function parseAsListActions(array $actions) + protected function parseAsListActions(array $actions): array { $listActions = []; foreach ($actions as $ident => $action) { @@ -1218,14 +1152,12 @@ protected function parseAsListActions(array $actions) $action['active'] = false; } } else { - $action['empty'] = (isset($action['empty']) ? boolval($action['empty']) : false); + $action['empty'] = (isset($action['empty']) && boolval($action['empty'])); } if (is_array($action['actions'])) { $action['actions'] = $this->parseAsListActions($action['actions']); - $action['hasActions'] = !!array_filter($action['actions'], function ($action) { - return $action['active']; - }); + $action['hasActions'] = (bool) array_filter($action['actions'], fn(array $action): mixed => $action['active']); } if (isset($listActions[$ident])) { @@ -1240,7 +1172,7 @@ protected function parseAsListActions(array $actions) } } - usort($listActions, [ 'Charcoal\Admin\Support\Sorter', 'sortByPriority' ]); + usort($listActions, \Charcoal\Admin\Support\Sorter::sortByPriority(...)); while (($first = reset($listActions)) && $first['isSeparator']) { array_shift($listActions); @@ -1313,19 +1245,18 @@ protected function defaultPropertiesOptions() * @param ModelInterface $object The current row's object. * @param PropertyInterface $property The current property. * @param string $propertyValue The property $key's display value. - * @return array */ protected function parsePropertyCell( ModelInterface $object, PropertyInterface $property, $propertyValue - ) { + ): array { $cell = $this->parseCollectionPropertyCell($object, $property, $propertyValue); $ident = $property->ident(); $options = $this->viewOptions($ident); $classes = $this->parsePropertyCellClasses($property, $object); - $cell['truncate'] = (isset($options['truncate']) ? boolval($options['truncate']) : false); + $cell['truncate'] = (isset($options['truncate']) && boolval($options['truncate'])); if (!isset($cell['attr'])) { $cell['attr'] = []; @@ -1335,24 +1266,20 @@ protected function parsePropertyCell( unset($options['attr']['width']); $cell['attr'] = array_merge($cell['attr'], $options['attr']); } + if (isset($cell['attr']['class'])) { + if (is_string($classes)) { + $classes = explode(' ', $cell['attr']['class']); + } - if (isset($classes)) { - if (isset($cell['attr']['class'])) { - if (is_string($classes)) { - $classes = explode(' ', $cell['attr']['class']); - } - - if (is_string($cell['attr']['class'])) { - $cell['attr']['class'] = explode(' ', $cell['attr']['class']); - } - - $cell['attr']['class'] = array_unique(array_merge($cell['attr']['class'], $classes)); - } else { - $cell['attr']['class'] = $classes; + if (is_string($cell['attr']['class'])) { + $cell['attr']['class'] = explode(' ', $cell['attr']['class']); } - unset($classes); + $cell['attr']['class'] = array_unique(array_merge($cell['attr']['class'], $classes)); + } else { + $cell['attr']['class'] = $classes; } + unset($classes); $cell['attr'] = html_build_attributes($cell['attr']); @@ -1367,12 +1294,11 @@ protected function parsePropertyCell( * * @param PropertyInterface $property The current property. * @param ModelInterface|null $object Optional. The current row's object. - * @return array */ protected function parsePropertyCellClasses( PropertyInterface $property, - ModelInterface $object = null - ) { + ?ModelInterface $object = null + ): array { unset($object); $ident = $property->ident(); @@ -1397,13 +1323,12 @@ protected function parsePropertyCellClasses( * * @param ModelInterface $object The current row's object. * @param array $objectProperties The $object's display properties. - * @return array */ - protected function parseObjectRow(ModelInterface $object, array $objectProperties) + protected function parseObjectRow(ModelInterface $object, array $objectProperties): array { $row = $this->parseCollectionObjectRow($object, $objectProperties); $row['objectActions'] = $this->objectActions(); - $row['showObjectActions'] = ($this->showObjectActions() === false) ? false : !!$row['objectActions']; + $row['showObjectActions'] = ($this->showObjectActions() === false) ? false : (bool) $row['objectActions']; $row['attr'] = [ 'class' => [] @@ -1424,9 +1349,8 @@ protected function parseObjectRow(ModelInterface $object, array $objectPropertie * Set an widget factory. * * @param FactoryInterface $factory The factory to create widgets. - * @return void */ - private function setWidgetFactory(FactoryInterface $factory) + private function setWidgetFactory(FactoryInterface $factory): void { $this->widgetFactory = $factory; } @@ -1435,7 +1359,7 @@ private function setWidgetFactory(FactoryInterface $factory) * @param FactoryInterface $factory The property factory, to create properties. * @return TableWidget Chainable */ - private function setPropertyFactory(FactoryInterface $factory) + private function setPropertyFactory(FactoryInterface $factory): static { $this->propertyFactory = $factory; diff --git a/packages/admin/src/Charcoal/Admin/Widget/TextWidget.php b/packages/admin/src/Charcoal/Admin/Widget/TextWidget.php index d667bc7fd..0e26de274 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/TextWidget.php +++ b/packages/admin/src/Charcoal/Admin/Widget/TextWidget.php @@ -1,5 +1,7 @@ showTitle = !!$show; + $this->showTitle = (bool) $show; return $this; } @@ -68,17 +57,16 @@ public function showTitle() if ($this->showTitle === false) { return false; } else { - return !!$this->title(); + return (bool) $this->title(); } } /** * @param boolean $show The show subtitle flag. - * @return self */ - public function setShowSubtitle($show) + public function setShowSubtitle($show): static { - $this->showSubtitle = !!$show; + $this->showSubtitle = (bool) $show; return $this; } @@ -90,17 +78,16 @@ public function showSubtitle() if ($this->showSubtitle === false) { return false; } else { - return !!$this->subtitle(); + return (bool) $this->subtitle(); } } /** * @param boolean $show The show description flag. - * @return self */ - public function setShowDescription($show) + public function setShowDescription($show): static { - $this->showDescription = !!$show; + $this->showDescription = (bool) $show; return $this; } @@ -112,17 +99,16 @@ public function showDescription() if ($this->showDescription === false) { return false; } else { - return !!$this->description(); + return (bool) $this->description(); } } /** * @param boolean $show The "show notes" flag. - * @return self */ - public function setShowNotes($show) + public function setShowNotes($show): static { - $this->showNotes = !!$show; + $this->showNotes = (bool) $show; return $this; } @@ -134,15 +120,14 @@ public function showNotes() if ($this->showNotes === false) { return false; } else { - return !!$this->notes(); + return (bool) $this->notes(); } } /** * @param mixed $title The text widget title. - * @return self */ - public function setTitle($title) + public function setTitle($title): static { $this->title = $this->translator()->translation($title); @@ -159,9 +144,8 @@ public function title() /** * @param mixed $subtitle The text widget subtitle. - * @return self */ - public function setSubtitle($subtitle) + public function setSubtitle($subtitle): static { $this->subtitle = $this->translator()->translation($subtitle); @@ -178,9 +162,8 @@ public function subtitle() /** * @param mixed $description The text widget description (main content). - * @return self */ - public function setDescription($description) + public function setDescription($description): static { $this->description = $this->translator()->translation($description); @@ -197,9 +180,8 @@ public function description() /** * @param mixed $notes The text widget notes. - * @return self */ - public function setNotes($notes) + public function setNotes($notes): static { $this->notes = $this->translator()->translation($notes); diff --git a/packages/admin/tests/Charcoal/AbstractTestCase.php b/packages/admin/tests/Charcoal/AbstractTestCase.php index 59ba12ea0..80f1772c4 100644 --- a/packages/admin/tests/Charcoal/AbstractTestCase.php +++ b/packages/admin/tests/Charcoal/AbstractTestCase.php @@ -1,5 +1,7 @@ callMethod($this->obj, 'authRequired'); $this->assertFalse($res); } - /** - * @return void - */ - public function testRunWithoutEmailReturns400() + public function testRunWithoutEmailReturns400(): void { $request = Request::createFromEnvironment(Environment::mock()); $response = new Response(); @@ -84,10 +72,7 @@ public function testRunWithoutEmailReturns400() $this->assertFalse($results['success']); } - /** - * @return void - */ - public function testRunWithoutRecaptchaReturns400() + public function testRunWithoutRecaptchaReturns400(): void { $mock = m::mock($this->obj); $mock->shouldAllowMockingProtectedMethods() @@ -107,10 +92,7 @@ public function testRunWithoutRecaptchaReturns400() $this->assertFalse($results['success']); } - /** - * @return void - */ - public function testRunWithInvalidRecaptchaReturns400() + public function testRunWithInvalidRecaptchaReturns400(): void { $mock = m::mock($this->obj); $mock->shouldAllowMockingProtectedMethods() @@ -132,12 +114,10 @@ public function testRunWithInvalidRecaptchaReturns400() /** * Set up the service container. - * - * @return Container */ - protected function container() + protected function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerAdminServices($container); diff --git a/packages/admin/tests/Charcoal/Admin/Action/Account/ResetPasswordActionTest.php b/packages/admin/tests/Charcoal/Admin/Action/Account/ResetPasswordActionTest.php index 72255bd13..443760af0 100644 --- a/packages/admin/tests/Charcoal/Admin/Action/Account/ResetPasswordActionTest.php +++ b/packages/admin/tests/Charcoal/Admin/Action/Account/ResetPasswordActionTest.php @@ -38,15 +38,11 @@ class ResetPasswordActionTest extends AbstractTestCase /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ public function setUp(): void { @@ -60,19 +56,13 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testAuthRequiredIsFalse() + public function testAuthRequiredIsFalse(): void { $res = $this->callMethod($this->obj, 'authRequired'); $this->assertFalse($res); } - /** - * @return void - */ - public function testRunWithoutTokenReturns400() + public function testRunWithoutTokenReturns400(): void { $request = Request::createFromEnvironment(Environment::mock()); $response = new Response(); @@ -84,10 +74,7 @@ public function testRunWithoutTokenReturns400() $this->assertFalse($results['success']); } - /** - * @return void - */ - public function testRunWithoutEmailReturns400() + public function testRunWithoutEmailReturns400(): void { $request = Request::createFromEnvironment(Environment::mock([ 'QUERY_STRING' => 'token=foobar' @@ -101,10 +88,7 @@ public function testRunWithoutEmailReturns400() $this->assertFalse($results['success']); } - /** - * @return void - */ - public function testRunWithoutPasswordReturns400() + public function testRunWithoutPasswordReturns400(): void { $request = Request::createFromEnvironment(Environment::mock([ 'QUERY_STRING' => 'token=foobar&email=foobar@foo.bar' @@ -118,10 +102,7 @@ public function testRunWithoutPasswordReturns400() $this->assertFalse($results['success']); } - /** - * @return void - */ - public function testRunWithoutMatchingPasswordsReturns400() + public function testRunWithoutMatchingPasswordsReturns400(): void { $request = Request::createFromEnvironment(Environment::mock([ 'QUERY_STRING' => 'token=foobar&email=foobar@foo.bar&password1=foo&password2=bar' @@ -135,10 +116,7 @@ public function testRunWithoutMatchingPasswordsReturns400() $this->assertFalse($results['success']); } - /** - * @return void - */ - public function testRunWithoutRecaptchaReturns400() + public function testRunWithoutRecaptchaReturns400(): void { $mock = m::mock($this->obj); $mock->shouldAllowMockingProtectedMethods() @@ -158,10 +136,7 @@ public function testRunWithoutRecaptchaReturns400() $this->assertFalse($results['success']); } - /** - * @return void - */ - public function testRunWithInvalidRecaptchaReturns400() + public function testRunWithInvalidRecaptchaReturns400(): void { $mock = m::mock($this->obj); $mock->shouldAllowMockingProtectedMethods() @@ -183,12 +158,10 @@ public function testRunWithInvalidRecaptchaReturns400() /** * Set up the service container. - * - * @return Container */ - protected function container() + protected function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerAdminServices($container); diff --git a/packages/admin/tests/Charcoal/Admin/Action/LoginActionTest.php b/packages/admin/tests/Charcoal/Admin/Action/LoginActionTest.php index 8c814c806..4d7f90b32 100644 --- a/packages/admin/tests/Charcoal/Admin/Action/LoginActionTest.php +++ b/packages/admin/tests/Charcoal/Admin/Action/LoginActionTest.php @@ -30,22 +30,16 @@ class LoginActionTest extends AbstractTestCase /** * Tested Class. - * - * @var LoginAction */ - private $obj; + private \Charcoal\Admin\Action\LoginAction $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ public function setUp(): void { @@ -61,19 +55,13 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testAuthRequiredIsFalse() + public function testAuthRequiredIsFalse(): void { $res = $this->callMethod($this->obj, 'authRequired'); $this->assertFalse($res); } - /** - * @return void - */ - public function testRunWithoutParamsIs400() + public function testRunWithoutParamsIs400(): void { $request = Request::createFromEnvironment(Environment::mock()); $response = new Response(); @@ -82,10 +70,7 @@ public function testRunWithoutParamsIs400() $this->assertEquals(400, $response->getStatusCode()); } - /** - * @return void - */ - public function testRunWithInvalidCredentials() + public function testRunWithInvalidCredentials(): void { $this->createUser('foo@bar.com'); @@ -108,28 +93,25 @@ public function testRunWithInvalidCredentials() public function testRunWithValidCredentials() { $this->createUser('foo@bar.com'); - + $request = Request::createFromEnvironment(Environment::mock([ 'QUERY_STRING' => 'password=qwerty' ])); $response = new Response(); - + $response = $this->obj->run($request, $response); $this->assertEquals(200, $response->getStatusCode()); - + $results = $this->obj->results(); $this->assertTrue($results['success']); } */ - /** * Set up the service container. - * - * @return Container */ - protected function container() + protected function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerActionDependencies($container); diff --git a/packages/admin/tests/Charcoal/Admin/Action/LogoutActionTest.php b/packages/admin/tests/Charcoal/Admin/Action/LogoutActionTest.php index ccaf20f5f..493b4ac4b 100644 --- a/packages/admin/tests/Charcoal/Admin/Action/LogoutActionTest.php +++ b/packages/admin/tests/Charcoal/Admin/Action/LogoutActionTest.php @@ -29,22 +29,16 @@ class LogoutActionTest extends AbstractTestCase /** * Tested Class. - * - * @var LogoutAction */ - private $obj; + private \Charcoal\Admin\Action\LogoutAction $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ public function setUp(): void { @@ -59,19 +53,13 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testAuthRequiredIsTrue() + public function testAuthRequiredIsTrue(): void { $res = $this->callMethod($this->obj, 'authRequired'); $this->assertTrue($res); } - /** - * @return void - */ - public function testRunWithUnauthenticatedUser() + public function testRunWithUnauthenticatedUser(): void { $this->createUser('foo@bar.com'); @@ -85,10 +73,7 @@ public function testRunWithUnauthenticatedUser() $this->assertFalse($results['success']); } - /** - * @return void - */ - public function testRunWithAuthenticatedUser() + public function testRunWithAuthenticatedUser(): void { $user = $this->createUser('foo@bar.com'); $this->getAuthenticator()->setUser($user); @@ -105,12 +90,10 @@ public function testRunWithAuthenticatedUser() /** * Set up the service container. - * - * @return Container */ - protected function container() + protected function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerActionDependencies($container); diff --git a/packages/admin/tests/Charcoal/Admin/Action/Object/DeleteActionTest.php b/packages/admin/tests/Charcoal/Admin/Action/Object/DeleteActionTest.php index ea41c1900..5e0b62206 100644 --- a/packages/admin/tests/Charcoal/Admin/Action/Object/DeleteActionTest.php +++ b/packages/admin/tests/Charcoal/Admin/Action/Object/DeleteActionTest.php @@ -29,22 +29,16 @@ class DeleteActionTest extends AbstractTestCase /** * Tested Class. - * - * @var DeleteAction */ - private $obj; + private \Charcoal\Admin\Action\Object\DeleteAction $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ public function setUp(): void { @@ -56,19 +50,13 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testAuthRequiredIsTrue() + public function testAuthRequiredIsTrue(): void { $res = $this->callMethod($this->obj, 'authRequired'); $this->assertTrue($res); } - /** - * @return void - */ - public function testRunWithoutObjTypeIs400() + public function testRunWithoutObjTypeIs400(): void { $request = Request::createFromEnvironment(Environment::mock()); $response = new Response(); @@ -80,10 +68,7 @@ public function testRunWithoutObjTypeIs400() $this->assertFalse($results['success']); } - /** - * @return void - */ - public function testRunWithoutObjIdIs400() + public function testRunWithoutObjIdIs400(): void { $request = Request::createFromEnvironment(Environment::mock([ 'QUERY_STRING' => 'obj_type=charcoal/admin/user' @@ -97,13 +82,10 @@ public function testRunWithoutObjIdIs400() $this->assertFalse($results['success']); } - /** - * @return void - */ - public function testRunWithInvalidObject() + public function testRunWithInvalidObject(): void { $email = 'foobar@foo.bar'; - $user = $this->createUser($email); + $this->createUser($email); $this->assertTrue($this->userExists($email)); $request = Request::createFromEnvironment(Environment::mock([ @@ -120,10 +102,7 @@ public function testRunWithInvalidObject() $this->assertTrue($this->userExists($email)); } - /** - * @return void - */ - public function testRunWithObjectDelete() + public function testRunWithObjectDelete(): void { $email = 'foobar@foo.bar'; $user = $this->createUser($email); @@ -145,12 +124,10 @@ public function testRunWithObjectDelete() /** * Set up the service container. - * - * @return Container */ - protected function container() + protected function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerActionDependencies($container); diff --git a/packages/admin/tests/Charcoal/Admin/Action/Object/ExportActionTest.php b/packages/admin/tests/Charcoal/Admin/Action/Object/ExportActionTest.php index 6fc3c0a81..12789dbcc 100644 --- a/packages/admin/tests/Charcoal/Admin/Action/Object/ExportActionTest.php +++ b/packages/admin/tests/Charcoal/Admin/Action/Object/ExportActionTest.php @@ -28,22 +28,16 @@ class ExportActionTest extends AbstractTestCase /** * Tested Class. - * - * @var ExportAction */ - private $obj; + private \Charcoal\Admin\Action\Object\ExportAction $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ public function setUp(): void { @@ -57,19 +51,13 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testAuthRequiredIsTrue() + public function testAuthRequiredIsTrue(): void { $res = $this->callMethod($this->obj, 'authRequired'); $this->assertTrue($res); } - /** - * @return void - */ - public function testRunWithoutObjTypeIs400() + public function testRunWithoutObjTypeIs400(): void { $request = Request::createFromEnvironment(Environment::mock()); $response = new Response(); @@ -83,12 +71,10 @@ public function testRunWithoutObjTypeIs400() /** * Set up the service container. - * - * @return Container */ - protected function container() + protected function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerAdminServices($container); diff --git a/packages/admin/tests/Charcoal/Admin/Action/Object/LoadActionTest.php b/packages/admin/tests/Charcoal/Admin/Action/Object/LoadActionTest.php index e43192b2b..ff137c54d 100644 --- a/packages/admin/tests/Charcoal/Admin/Action/Object/LoadActionTest.php +++ b/packages/admin/tests/Charcoal/Admin/Action/Object/LoadActionTest.php @@ -29,22 +29,16 @@ class LoadActionTest extends AbstractTestCase /** * Tested Class. - * - * @var LoadAction */ - private $obj; + private \Charcoal\Admin\Action\Object\LoadAction $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ public function setUp(): void { @@ -56,19 +50,13 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testAuthRequiredIsTrue() + public function testAuthRequiredIsTrue(): void { $res = $this->callMethod($this->obj, 'authRequired'); $this->assertTrue($res); } - /** - * @return void - */ - public function testRunWithoutObjTypeIs400() + public function testRunWithoutObjTypeIs400(): void { $request = Request::createFromEnvironment(Environment::mock()); $response = new Response(); @@ -80,10 +68,7 @@ public function testRunWithoutObjTypeIs400() $this->assertFalse($results['success']); } - /** - * @return void - */ - public function testRun() + public function testRun(): void { $user = $this->createUser('foo@bar.com'); @@ -103,12 +88,10 @@ public function testRun() /** * Set up the service container. - * - * @return Container */ - protected function container() + protected function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerActionDependencies($container); diff --git a/packages/admin/tests/Charcoal/Admin/Action/Object/ReorderActionTest.php b/packages/admin/tests/Charcoal/Admin/Action/Object/ReorderActionTest.php index 03e60d274..eaf87608c 100644 --- a/packages/admin/tests/Charcoal/Admin/Action/Object/ReorderActionTest.php +++ b/packages/admin/tests/Charcoal/Admin/Action/Object/ReorderActionTest.php @@ -32,36 +32,26 @@ class ReorderActionTest extends AbstractTestCase /** * The primary model to test with. - * - * @var string */ - private $model = Model::class; + private string $model = Model::class; /** * Store the tested instance. - * - * @var ReorderAction */ - private $action; + private \Charcoal\Admin\Action\Object\ReorderAction $action; /** * Store the object collection loader. - * - * @var CollectionLoader */ - private $collectionLoader; + private ?\Charcoal\Loader\CollectionLoader $collectionLoader = null; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ public function setUp(): void { @@ -78,7 +68,7 @@ public function setUp(): void /** * @return array */ - public function setUpObjects() + public function setUpObjects(): \ArrayAccess|array { $container = $this->container(); @@ -110,9 +100,9 @@ public function setUpObjects() /** * @return Collection */ - public function getObjects() + public function getObjects(): \ArrayAccess|array { - if ($this->collectionLoader === null) { + if (!$this->collectionLoader instanceof \Charcoal\Loader\CollectionLoader) { $container = $this->container(); $loader = new CollectionLoader([ @@ -129,24 +119,20 @@ public function getObjects() return $this->collectionLoader->load(); } - /** - * @return void - */ - public function testAuthRequiredIsTrue() + public function testAuthRequiredIsTrue(): void { $res = $this->callMethod($this->action, 'authRequired'); $this->assertTrue($res); } /** - * @dataProvider runRequestProvider * * @param integer $status An HTTP status code. * @param string $success Whether the action was successful. * @param array $mock The request parameters to test. - * @return void */ - public function testRun($status, $success, array $mock) + #[\PHPUnit\Framework\Attributes\DataProvider('runRequestProvider')] + public function testRun(int $status, bool $success, array $mock): void { if ($status === 200) { $this->setUpObjects(); @@ -167,29 +153,25 @@ public function testRun($status, $success, array $mock) } } - /** - * @return array - */ - public function runRequestProvider() + public static function runRequestProvider(): array { + $model = Model::class; return [ [ 400, false, [] ], - [ 400, false, [ 'QUERY_STRING' => 'obj_type='.$this->model ] ], - [ 400, false, [ 'QUERY_STRING' => 'obj_type='.$this->model.'&order_property=5' ] ], - [ 400, false, [ 'QUERY_STRING' => 'obj_type='.$this->model.'&order_property=foobar' ] ], - [ 500, false, [ 'QUERY_STRING' => 'obj_type='.$this->model.'&obj_orders[]=xyzzy&obj_orders[]=qwerty' ] ], - [ 200, true, [ 'QUERY_STRING' => 'obj_type='.$this->model.'&obj_orders[]=baz&obj_orders[]=bar&obj_orders[]=qux&obj_orders[]=foo' ] ], + [ 400, false, [ 'QUERY_STRING' => 'obj_type='.$model ] ], + [ 400, false, [ 'QUERY_STRING' => 'obj_type='.$model.'&order_property=5' ] ], + [ 400, false, [ 'QUERY_STRING' => 'obj_type='.$model.'&order_property=foobar' ] ], + [ 500, false, [ 'QUERY_STRING' => 'obj_type='.$model.'&obj_orders[]=xyzzy&obj_orders[]=qwerty' ] ], + [ 200, true, [ 'QUERY_STRING' => 'obj_type='.$model.'&obj_orders[]=baz&obj_orders[]=bar&obj_orders[]=qux&obj_orders[]=foo' ] ], ]; } /** * Set up the service container. - * - * @return Container */ - protected function container() + protected function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerAdminServices($container); diff --git a/packages/admin/tests/Charcoal/Admin/Action/Object/SaveActionTest.php b/packages/admin/tests/Charcoal/Admin/Action/Object/SaveActionTest.php index 7b3ee7c43..17c05fb2a 100644 --- a/packages/admin/tests/Charcoal/Admin/Action/Object/SaveActionTest.php +++ b/packages/admin/tests/Charcoal/Admin/Action/Object/SaveActionTest.php @@ -28,22 +28,16 @@ class SaveActionTest extends AbstractTestCase /** * Tested Class. - * - * @var SaveAction */ - private $obj; + private \Charcoal\Admin\Action\Object\SaveAction $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ public function setUp(): void { @@ -55,19 +49,13 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testAuthRequiredIsTrue() + public function testAuthRequiredIsTrue(): void { $res = $this->callMethod($this->obj, 'authRequired'); $this->assertTrue($res); } - /** - * @return void - */ - public function testRunWithoutObjTypeIs400() + public function testRunWithoutObjTypeIs400(): void { $request = Request::createFromEnvironment(Environment::mock()); $response = new Response(); @@ -81,12 +69,10 @@ public function testRunWithoutObjTypeIs400() /** * Set up the service container. - * - * @return Container */ - protected function container() + protected function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerActionDependencies($container); diff --git a/packages/admin/tests/Charcoal/Admin/Action/Object/UpdateActionTest.php b/packages/admin/tests/Charcoal/Admin/Action/Object/UpdateActionTest.php index a412fee17..6bc82a358 100644 --- a/packages/admin/tests/Charcoal/Admin/Action/Object/UpdateActionTest.php +++ b/packages/admin/tests/Charcoal/Admin/Action/Object/UpdateActionTest.php @@ -28,22 +28,16 @@ class UpdateActionTest extends AbstractTestCase /** * Tested Class. - * - * @var UpdateAction */ - private $obj; + private \Charcoal\Admin\Action\Object\UpdateAction $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ public function setUp(): void { @@ -55,19 +49,13 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testAuthRequiredIsTrue() + public function testAuthRequiredIsTrue(): void { $res = $this->callMethod($this->obj, 'authRequired'); $this->assertTrue($res); } - /** - * @return void - */ - public function testRunWithoutObjTypeIs400() + public function testRunWithoutObjTypeIs400(): void { $request = Request::createFromEnvironment(Environment::mock()); $response = new Response(); @@ -81,12 +69,10 @@ public function testRunWithoutObjTypeIs400() /** * Set up the service container. - * - * @return Container */ - protected function container() + protected function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerActionDependencies($container); diff --git a/packages/admin/tests/Charcoal/Admin/Action/System/ClearCacheActionTest.php b/packages/admin/tests/Charcoal/Admin/Action/System/ClearCacheActionTest.php index b818971e9..e7d751412 100644 --- a/packages/admin/tests/Charcoal/Admin/Action/System/ClearCacheActionTest.php +++ b/packages/admin/tests/Charcoal/Admin/Action/System/ClearCacheActionTest.php @@ -29,22 +29,16 @@ class ClearCacheActionTest extends AbstractTestCase /** * Tested Class. - * - * @var ClearCacheAction */ - private $obj; + private \Charcoal\Admin\Action\System\ClearCacheAction $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ public function setUp(): void { @@ -56,19 +50,13 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testAuthRequiredIsTrue() + public function testAuthRequiredIsTrue(): void { $res = $this->callMethod($this->obj, 'authRequired'); $this->assertTrue($res); } - /** - * @return void - */ - public function testRun() + public function testRun(): void { $request = Request::createFromEnvironment(Environment::mock()); $response = new Response(); @@ -82,12 +70,10 @@ public function testRun() /** * Set up the service container. - * - * @return Container */ - protected function container() + protected function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerActionDependencies($container); diff --git a/packages/admin/tests/Charcoal/Admin/Action/System/StaticWebsite/ActivateActionTest.php b/packages/admin/tests/Charcoal/Admin/Action/System/StaticWebsite/ActivateActionTest.php index 82238774e..89f8d7c6b 100644 --- a/packages/admin/tests/Charcoal/Admin/Action/System/StaticWebsite/ActivateActionTest.php +++ b/packages/admin/tests/Charcoal/Admin/Action/System/StaticWebsite/ActivateActionTest.php @@ -29,22 +29,16 @@ class ActivateActionTest extends AbstractTestCase /** * Tested Class. - * - * @var ActivateAction */ - private $obj; + private \Charcoal\Admin\Action\System\StaticWebsite\ActivateAction $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ public function setUp(): void { @@ -56,19 +50,13 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testAuthRequiredIsTrue() + public function testAuthRequiredIsTrue(): void { $res = $this->callMethod($this->obj, 'authRequired'); $this->assertTrue($res); } - /** - * @return void - */ - public function testRun() + public function testRun(): void { $request = Request::createFromEnvironment(Environment::mock()); $response = new Response(); @@ -82,12 +70,10 @@ public function testRun() /** * Set up the service container. - * - * @return Container */ - protected function container() + protected function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerActionDependencies($container); diff --git a/packages/admin/tests/Charcoal/Admin/Action/System/StaticWebsite/AddActionTest.php b/packages/admin/tests/Charcoal/Admin/Action/System/StaticWebsite/AddActionTest.php index f84930ea9..2c7e7fd7a 100644 --- a/packages/admin/tests/Charcoal/Admin/Action/System/StaticWebsite/AddActionTest.php +++ b/packages/admin/tests/Charcoal/Admin/Action/System/StaticWebsite/AddActionTest.php @@ -29,22 +29,16 @@ class AddActionTest extends AbstractTestCase /** * Tested Class. - * - * @var AddAction */ - private $obj; + private \Charcoal\Admin\Action\System\StaticWebsite\AddAction $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ public function setUp(): void { @@ -56,19 +50,13 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testAuthRequiredIsTrue() + public function testAuthRequiredIsTrue(): void { $res = $this->callMethod($this->obj, 'authRequired'); $this->assertTrue($res); } - /** - * @return void - */ - public function testRun() + public function testRun(): void { $request = Request::createFromEnvironment(Environment::mock()); $response = new Response(); @@ -82,12 +70,10 @@ public function testRun() /** * Set up the service container. - * - * @return Container */ - protected function container() + protected function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerActionDependencies($container); diff --git a/packages/admin/tests/Charcoal/Admin/Action/System/StaticWebsite/DeactivateActionTest.php b/packages/admin/tests/Charcoal/Admin/Action/System/StaticWebsite/DeactivateActionTest.php index 800c641ec..d7b7b3eef 100644 --- a/packages/admin/tests/Charcoal/Admin/Action/System/StaticWebsite/DeactivateActionTest.php +++ b/packages/admin/tests/Charcoal/Admin/Action/System/StaticWebsite/DeactivateActionTest.php @@ -29,22 +29,16 @@ class DeactivateActionTest extends AbstractTestCase /** * Tested Class. - * - * @var DeactivateAction */ - private $obj; + private \Charcoal\Admin\Action\System\StaticWebsite\DeactivateAction $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ public function setUp(): void { @@ -56,19 +50,13 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testAuthRequiredIsTrue() + public function testAuthRequiredIsTrue(): void { $res = $this->callMethod($this->obj, 'authRequired'); $this->assertTrue($res); } - /** - * @return void - */ - public function testRun() + public function testRun(): void { $request = Request::createFromEnvironment(Environment::mock()); $response = new Response(); @@ -82,12 +70,10 @@ public function testRun() /** * Set up the service container. - * - * @return Container */ - protected function container() + protected function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerActionDependencies($container); diff --git a/packages/admin/tests/Charcoal/Admin/Action/System/StaticWebsite/DeleteActionTest.php b/packages/admin/tests/Charcoal/Admin/Action/System/StaticWebsite/DeleteActionTest.php index 3107cc470..bb2849be6 100644 --- a/packages/admin/tests/Charcoal/Admin/Action/System/StaticWebsite/DeleteActionTest.php +++ b/packages/admin/tests/Charcoal/Admin/Action/System/StaticWebsite/DeleteActionTest.php @@ -29,22 +29,16 @@ class DeleteActionTest extends AbstractTestCase /** * Tested Class. - * - * @var DeleteAction */ - private $obj; + private \Charcoal\Admin\Action\System\StaticWebsite\DeleteAction $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ public function setUp(): void { @@ -56,19 +50,13 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testAuthRequiredIsTrue() + public function testAuthRequiredIsTrue(): void { $res = $this->callMethod($this->obj, 'authRequired'); $this->assertTrue($res); } - /** - * @return void - */ - public function testRun() + public function testRun(): void { $request = Request::createFromEnvironment(Environment::mock()); $response = new Response(); @@ -82,12 +70,10 @@ public function testRun() /** * Set up the service container. - * - * @return Container */ - protected function container() + protected function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerActionDependencies($container); diff --git a/packages/admin/tests/Charcoal/Admin/Action/System/StaticWebsite/DeleteAllActionTest.php b/packages/admin/tests/Charcoal/Admin/Action/System/StaticWebsite/DeleteAllActionTest.php index dfdaaf235..df1de19ca 100644 --- a/packages/admin/tests/Charcoal/Admin/Action/System/StaticWebsite/DeleteAllActionTest.php +++ b/packages/admin/tests/Charcoal/Admin/Action/System/StaticWebsite/DeleteAllActionTest.php @@ -29,22 +29,16 @@ class DeleteAllActionTest extends AbstractTestCase /** * Tested Class. - * - * @var DeleteAllAction */ - private $obj; + private \Charcoal\Admin\Action\System\StaticWebsite\DeleteAllAction $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ public function setUp(): void { @@ -56,19 +50,13 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testAuthRequiredIsTrue() + public function testAuthRequiredIsTrue(): void { $res = $this->callMethod($this->obj, 'authRequired'); $this->assertTrue($res); } - /** - * @return void - */ - public function testRun() + public function testRun(): void { $request = Request::createFromEnvironment(Environment::mock()); $response = new Response(); @@ -82,12 +70,10 @@ public function testRun() /** * Set up the service container. - * - * @return Container */ - protected function container() + protected function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerActionDependencies($container); diff --git a/packages/admin/tests/Charcoal/Admin/Action/System/StaticWebsite/PreviewActionTest.php b/packages/admin/tests/Charcoal/Admin/Action/System/StaticWebsite/PreviewActionTest.php index bb7506d81..1245fa77d 100644 --- a/packages/admin/tests/Charcoal/Admin/Action/System/StaticWebsite/PreviewActionTest.php +++ b/packages/admin/tests/Charcoal/Admin/Action/System/StaticWebsite/PreviewActionTest.php @@ -29,22 +29,16 @@ class PreviewActionTest extends AbstractTestCase /** * Tested Class. - * - * @var PreviewAction */ - private $obj; + private \Charcoal\Admin\Action\System\StaticWebsite\PreviewAction $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ public function setUp(): void { @@ -56,19 +50,13 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testAuthRequiredIsTrue() + public function testAuthRequiredIsTrue(): void { $res = $this->callMethod($this->obj, 'authRequired'); $this->assertTrue($res); } - /** - * @return void - */ - public function testRun() + public function testRun(): void { $request = Request::createFromEnvironment(Environment::mock()); $response = new Response(); @@ -82,12 +70,10 @@ public function testRun() /** * Set up the service container. - * - * @return Container */ - protected function container() + protected function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerActionDependencies($container); diff --git a/packages/admin/tests/Charcoal/Admin/Action/System/StaticWebsite/UpdateActionTest.php b/packages/admin/tests/Charcoal/Admin/Action/System/StaticWebsite/UpdateActionTest.php index c121e60d4..92c646a3e 100644 --- a/packages/admin/tests/Charcoal/Admin/Action/System/StaticWebsite/UpdateActionTest.php +++ b/packages/admin/tests/Charcoal/Admin/Action/System/StaticWebsite/UpdateActionTest.php @@ -29,22 +29,16 @@ class UpdateActionTest extends AbstractTestCase /** * Tested Class. - * - * @var UpdateAction */ - private $obj; + private \Charcoal\Admin\Action\System\StaticWebsite\UpdateAction $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ public function setUp(): void { @@ -56,19 +50,13 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testAuthRequiredIsTrue() + public function testAuthRequiredIsTrue(): void { $res = $this->callMethod($this->obj, 'authRequired'); $this->assertTrue($res); } - /** - * @return void - */ - public function testRun() + public function testRun(): void { $request = Request::createFromEnvironment(Environment::mock()); $response = new Response(); @@ -82,12 +70,10 @@ public function testRun() /** * Set up the service container. - * - * @return Container */ - protected function container() + protected function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerActionDependencies($container); diff --git a/packages/admin/tests/Charcoal/Admin/Action/System/StaticWebsite/UpdateAllActionTest.php b/packages/admin/tests/Charcoal/Admin/Action/System/StaticWebsite/UpdateAllActionTest.php index 6f2a53054..2e86a6fa3 100644 --- a/packages/admin/tests/Charcoal/Admin/Action/System/StaticWebsite/UpdateAllActionTest.php +++ b/packages/admin/tests/Charcoal/Admin/Action/System/StaticWebsite/UpdateAllActionTest.php @@ -29,22 +29,16 @@ class UpdateAllActionTest extends AbstractTestCase /** * Tested Class. - * - * @var UpdateAllAction */ - private $obj; + private \Charcoal\Admin\Action\System\StaticWebsite\UpdateAllAction $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ public function setUp(): void { @@ -56,19 +50,13 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testAuthRequiredIsTrue() + public function testAuthRequiredIsTrue(): void { $res = $this->callMethod($this->obj, 'authRequired'); $this->assertTrue($res); } - /** - * @return void - */ - public function testRun() + public function testRun(): void { $request = Request::createFromEnvironment(Environment::mock()); $response = new Response(); @@ -82,12 +70,10 @@ public function testRun() /** * Set up the service container. - * - * @return Container */ - protected function container() + protected function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerActionDependencies($container); diff --git a/packages/admin/tests/Charcoal/Admin/AdminActionTest.php b/packages/admin/tests/Charcoal/Admin/AdminActionTest.php index 9444e06a0..d38f650eb 100644 --- a/packages/admin/tests/Charcoal/Admin/AdminActionTest.php +++ b/packages/admin/tests/Charcoal/Admin/AdminActionTest.php @@ -27,22 +27,16 @@ class AdminActionTest extends AbstractTestCase /** * Tested Class. - * - * @var AdminAction */ - private $obj; + private \Charcoal\Admin\AdminAction $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ public function setUp(): void { @@ -63,10 +57,8 @@ public function setUp(): void * - success can be set by ArrayAccess * - success can be set with get() * - success can be accessed by ArrayAccess - * - * @return void */ - public function testSuccess() + public function testSuccess(): void { $this->assertFalse($this->obj->success()); $ret = $this->obj->setSuccess(true); @@ -83,10 +75,7 @@ public function testSuccess() $this->assertFalse($this->obj['success']); } - /** - * @return void - */ - public function testFeedback() + public function testFeedback(): void { $this->assertFalse($this->obj->hasFeedbacks()); $this->assertEquals([], $this->obj->feedbacks()); @@ -105,18 +94,12 @@ public function testFeedback() $this->assertEquals(1, $this->obj->numFeedbacks()); } - /** - * @return void - */ - public function testAdminUrl() + public function testAdminUrl(): void { $this->assertEquals('/admin/', $this->obj->adminUrl()); } - /** - * @return void - */ - public function testAuthRequiredIsTrue() + public function testAuthRequiredIsTrue(): void { $res = $this->callMethod($this->obj, 'authRequired'); $this->assertTrue($res); @@ -124,12 +107,10 @@ public function testAuthRequiredIsTrue() /** * Set up the service container. - * - * @return Container */ - protected function container() + protected function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerActionDependencies($container); diff --git a/packages/admin/tests/Charcoal/Admin/AdminTemplateTest.php b/packages/admin/tests/Charcoal/Admin/AdminTemplateTest.php index 555eb2eeb..1dd3ff197 100644 --- a/packages/admin/tests/Charcoal/Admin/AdminTemplateTest.php +++ b/packages/admin/tests/Charcoal/Admin/AdminTemplateTest.php @@ -23,22 +23,16 @@ class AdminTemplateTest extends AbstractTestCase /** * Tested Class. - * - * @var AdminTemplate */ - private $obj; + private \Charcoal\Admin\AdminTemplate $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ public function setUp(): void { @@ -50,10 +44,7 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testSetIdent() + public function testSetIdent(): void { $this->assertNull($this->obj->ident()); $ret = $this->obj->setIdent('foobar'); @@ -61,10 +52,7 @@ public function testSetIdent() $this->assertEquals('foobar', $this->obj->ident()); } - /** - * @return void - */ - public function testSetLabel() + public function testSetLabel(): void { $this->assertNull($this->obj->label()); $ret = $this->obj->setLabel('foobar'); @@ -72,10 +60,7 @@ public function testSetLabel() $this->assertEquals('foobar', (string)$this->obj->label()); } - /** - * @return void - */ - public function testAuthRequiredIsTrue() + public function testAuthRequiredIsTrue(): void { $res = $this->callMethod($this->obj, 'authRequired'); $this->assertTrue($res); @@ -83,17 +68,15 @@ public function testAuthRequiredIsTrue() /** * Set up the service container. - * - * @return Container */ - protected function container() + protected function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerTemplateDependencies($container); - $container['widget/factory'] = $this->createMock('\Charcoal\Factory\FactoryInterface'); + $container['widget/factory'] = $this->createMock(\Charcoal\Factory\FactoryInterface::class); $this->container = $container; } diff --git a/packages/admin/tests/Charcoal/Admin/AdminWidgetTest.php b/packages/admin/tests/Charcoal/Admin/AdminWidgetTest.php index a534248bf..9617919d0 100644 --- a/packages/admin/tests/Charcoal/Admin/AdminWidgetTest.php +++ b/packages/admin/tests/Charcoal/Admin/AdminWidgetTest.php @@ -17,22 +17,16 @@ class AdminWidgetTest extends AbstractTestCase { /** * Tested Class. - * - * @var AdminWidget */ - private $obj; + private \Charcoal\Admin\AdminWidget $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ public function setUp(): void { @@ -44,10 +38,7 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testSetData() + public function testSetData(): void { $obj = $this->obj; $ret = $obj->setData([ @@ -64,10 +55,7 @@ public function testSetData() $this->assertNotTrue($obj->showActions()); } - /** - * @return void - */ - public function testSetType() + public function testSetType(): void { $obj = $this->obj; $this->assertEquals(null, $obj->type()); @@ -80,12 +68,8 @@ public function testSetType() $obj->setType(1); } - /** - * @return void - */ - public function testSetLabel() + public function testSetLabel(): void { - $obj = $this->obj; //$this->assertEquals(null, $obj->label()); $obj = $this->obj; @@ -101,12 +85,10 @@ public function testSetLabel() /** * Set up the service container. - * - * @return Container */ - protected function container() + protected function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerWidgetDependencies($container); diff --git a/packages/admin/tests/Charcoal/Admin/ConfigTest.php b/packages/admin/tests/Charcoal/Admin/ConfigTest.php index 3bf16bd6f..9244ba664 100644 --- a/packages/admin/tests/Charcoal/Admin/ConfigTest.php +++ b/packages/admin/tests/Charcoal/Admin/ConfigTest.php @@ -11,10 +11,7 @@ */ class ConfigTest extends AbstractTestCase { - /** - * @return void - */ - public function testSetData() + public function testSetData(): void { $obj = new Config(); $ret = $obj->merge([ @@ -24,10 +21,7 @@ public function testSetData() $this->assertEquals('foo', $obj->basePath()); } - /** - * @return void - */ - public function testSetBasePath() + public function testSetBasePath(): void { $obj = new Config(); $this->assertEquals('admin', $obj->basePath()); @@ -40,10 +34,7 @@ public function testSetBasePath() $obj->setBasePath([]); } - /** - * @return void - */ - public function testSetBasePathEmptyParamThrowsException() + public function testSetBasePathEmptyParamThrowsException(): void { $obj = new Config(); diff --git a/packages/admin/tests/Charcoal/Admin/ContainerProvider.php b/packages/admin/tests/Charcoal/Admin/ContainerProvider.php index fea6cebe0..58400598d 100644 --- a/packages/admin/tests/Charcoal/Admin/ContainerProvider.php +++ b/packages/admin/tests/Charcoal/Admin/ContainerProvider.php @@ -74,9 +74,8 @@ class ContainerProvider * Register the unit tests required services. * * @param Container $container A DI container. - * @return void */ - public function registerDebug(Container $container) + public function registerDebug(Container $container): void { if (!isset($container['debug'])) { $container['debug'] = false; @@ -87,9 +86,8 @@ public function registerDebug(Container $container) * Register the unit tests required services. * * @param Container $container A DI container. - * @return void */ - public function registerBaseServices(Container $container) + public function registerBaseServices(Container $container): void { $this->registerDebug($container); $this->registerConfig($container); @@ -102,9 +100,8 @@ public function registerBaseServices(Container $container) * Register the admin services. * * @param Container $container A DI container. - * @return void */ - public function registerAdminServices(Container $container) + public function registerAdminServices(Container $container): void { $this->registerBaseServices($container); $this->registerBaseUrl($container); @@ -115,67 +112,57 @@ public function registerAdminServices(Container $container) * Setup the application's base URI. * * @param Container $container A DI container. - * @return void */ - public function registerBaseUrl(Container $container) + public function registerBaseUrl(Container $container): void { - $container['base-url'] = function () { - return Uri::createFromString(''); - }; + $container['base-url'] = (fn() => Uri::createFromString('')); - $container['admin/base-url'] = function () { - return Uri::createFromString('admin'); - }; + $container['admin/base-url'] = (fn() => Uri::createFromString('admin')); } /** * Setup the application configset. * * @param Container $container A DI container. - * @return void */ - public function registerConfig(Container $container) + public function registerConfig(Container $container): void { - $container['config'] = function () { - return new AppConfig([ - 'base_path' => realpath(__DIR__.'/../../..'), - 'apis' => [ - 'google' => [ - 'recaptcha' => [ - 'public_key' => 'foobar', - 'private_key' => 'bazqux', - ], + $container['config'] = (fn(): \Charcoal\App\AppConfig => new AppConfig([ + 'base_path' => realpath(__DIR__.'/../../..'), + 'apis' => [ + 'google' => [ + 'recaptcha' => [ + 'public_key' => 'foobar', + 'private_key' => 'bazqux', ], ], - 'locales' => [ - 'en' => [ - 'locale' => 'en-US', - ], + ], + 'locales' => [ + 'en' => [ + 'locale' => 'en-US', ], - 'translator' => [ - 'paths' => [], + ], + 'translator' => [ + 'paths' => [], + ], + 'metadata' => [ + 'paths' => [ + 'metadata', + // Standalone + 'vendor/charcoal/object/metadata', + 'vendor/charcoal/user/metadata', + // Monorepo + '/../object/metadata', + '/../user/metadata', ], - 'metadata' => [ - 'paths' => [ - 'metadata', - // Standalone - 'vendor/charcoal/object/metadata', - 'vendor/charcoal/user/metadata', - // Monorepo - '/../object/metadata', - '/../user/metadata', - ], - ], - ]); - }; + ], + ])); /** * List of Charcoal module classes. * * Explicitly defined in case of a version mismatch with dependencies. This parameter * is normally defined by {@see \Charcoal\App\ServiceProvider\AppServiceProvider}. - * - * @var array */ $container['module/classes'] = []; } @@ -184,134 +171,110 @@ public function registerConfig(Container $container) * Setup the admin module configset. * * @param Container $container A DI container. - * @return void */ - public function registerAdminConfig(Container $container) + public function registerAdminConfig(Container $container): void { $this->registerConfig($container); - $container['admin/config'] = function () { - return new AdminConfig(); - }; + $container['admin/config'] = (fn(): \Charcoal\Admin\Config => new AdminConfig()); } /** * @param Container $container A DI container. - * @return void */ - public function registerElfinderConfig(Container $container) + public function registerElfinderConfig(Container $container): void { - $container['elfinder/config'] = function () { - return []; - }; + $container['elfinder/config'] = (fn(): array => []); } /** * @param Container $container A DI container. - * @return void */ - public function registerLayoutFactory(Container $container) + public function registerLayoutFactory(Container $container): void { - $container['layout/factory'] = function () { - $layoutFactory = new LayoutFactory(); - return $layoutFactory; - }; + $container['layout/factory'] = (fn(): \Charcoal\Ui\Layout\LayoutFactory => new LayoutFactory()); } /** * @param Container $container A DI container. - * @return void */ - public function registerLayoutBuilder(Container $container) + public function registerLayoutBuilder(Container $container): void { $this->registerLayoutFactory($container); - $container['layout/builder'] = function (Container $container) { + $container['layout/builder'] = function (Container $container): \Charcoal\Ui\Layout\LayoutBuilder { $layoutFactory = $container['layout/factory']; - $layoutBuilder = new LayoutBuilder($layoutFactory, $container); - return $layoutBuilder; + return new LayoutBuilder($layoutFactory, $container); }; } /** * @param Container $container A DI container. - * @return void */ - public function registerDashboardFactory(Container $container) + public function registerDashboardFactory(Container $container): void { $this->registerLogger($container); $this->registerWidgetBuilder($container); $this->registerLayoutBuilder($container); - $container['dashboard/factory'] = function (Container $container) { - return new Factory([ - 'arguments' => [[ - 'container' => $container, - 'logger' => $container['logger'], - 'widget_builder' => $container['widget/builder'], - 'layout_builder' => $container['layout/builder'] - ]], - 'resolver_options' => [ - 'suffix' => 'Dashboard' - ] - ]); - }; + $container['dashboard/factory'] = (fn(Container $container): \Charcoal\Factory\GenericFactory => new Factory([ + 'arguments' => [[ + 'container' => $container, + 'logger' => $container['logger'], + 'widget_builder' => $container['widget/builder'], + 'layout_builder' => $container['layout/builder'] + ]], + 'resolver_options' => [ + 'suffix' => 'Dashboard' + ] + ])); } /** * @param Container $container A DI container. - * @return void */ - public function registerDashboardBuilder(Container $container) + public function registerDashboardBuilder(Container $container): void { $this->registerDashboardFactory($container); - $container['dashboard/builder'] = function (Container $container) { + $container['dashboard/builder'] = function (Container $container): \Charcoal\Ui\Dashboard\DashboardBuilder { $dashboardFactory = $container['dashboard/factory']; - $dashboardBuilder = new DashboardBuilder($dashboardFactory, $container); - return $dashboardBuilder; + return new DashboardBuilder($dashboardFactory, $container); }; } /** * @param Container $container A DI container. - * @return void */ - public function registerWidgetFactory(Container $container) + public function registerWidgetFactory(Container $container): void { $this->registerLogger($container); - $container['widget/factory'] = function (Container $container) { - return new Factory([ - 'resolver_options' => [ - 'suffix' => 'Widget' - ], - 'arguments' => [[ - 'container' => $container, - 'logger' => $container['logger'] - ]] - ]); - }; + $container['widget/factory'] = (fn(Container $container): \Charcoal\Factory\GenericFactory => new Factory([ + 'resolver_options' => [ + 'suffix' => 'Widget' + ], + 'arguments' => [[ + 'container' => $container, + 'logger' => $container['logger'] + ]] + ])); } /** * @param Container $container A DI container. - * @return void */ - public function registerWidgetBuilder(Container $container) + public function registerWidgetBuilder(Container $container): void { $this->registerWidgetFactory($container); - $container['widget/builder'] = function (Container $container) { - return new WidgetBuilder($container['widget/factory'], $container); - }; + $container['widget/builder'] = (fn(Container $container): \Charcoal\App\Template\WidgetBuilder => new WidgetBuilder($container['widget/factory'], $container)); } /** * @param Container $container A DI container. - * @return void */ - public function registerClimate(Container $container) + public function registerClimate(Container $container): void { $container['climate/system'] = function () { $system = Mockery::mock(Linux::class); @@ -338,11 +301,9 @@ public function registerClimate(Container $container) return $reader; }; - $container['climate/util'] = function (Container $container) { - return new UtilFactory($container['climate/system']); - }; + $container['climate/util'] = (fn(Container $container): \League\CLImate\Util\UtilFactory => new UtilFactory($container['climate/system'])); - $container['climate'] = function (Container $container) { + $container['climate'] = function (Container $container): \League\CLImate\CLImate { $climate = new CLImate(); $climate->setOutput($container['climate/output']); @@ -357,35 +318,28 @@ public function registerClimate(Container $container) * Setup the application's logging interface. * * @param Container $container A DI container. - * @return void */ - public function registerLogger(Container $container) + public function registerLogger(Container $container): void { - $container['logger'] = function () { - return new NullLogger(); - }; + $container['logger'] = (fn(): \Psr\Log\NullLogger => new NullLogger()); } /** * Setup the application's caching interface. * * @param Container $container A DI container. - * @return void */ - public function registerCache(Container $container) + public function registerCache(Container $container): void { - $container['cache'] = function () { - return new Pool(); - }; + $container['cache'] = (fn(): \Stash\Pool => new Pool()); } /** * @param Container $container A DI container. - * @return void */ - public function registerDatabase(Container $container) + public function registerDatabase(Container $container): void { - $container['database'] = function () { + $container['database'] = function (): \PDO { $pdo = new PDO('sqlite::memory:'); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); return $pdo; @@ -394,9 +348,8 @@ public function registerDatabase(Container $container) /** * @param Container $container A DI container. - * @return void */ - public function registerModelServiceProvider(Container $container) + public function registerModelServiceProvider(Container $container): void { static $provider = null; @@ -409,9 +362,8 @@ public function registerModelServiceProvider(Container $container) /** * @param Container $container A DI container. - * @return void */ - public function registerTranslatorServiceProvider(Container $container) + public function registerTranslatorServiceProvider(Container $container): void { static $provider = null; @@ -424,9 +376,8 @@ public function registerTranslatorServiceProvider(Container $container) /** * @param Container $container A DI container. - * @return void */ - public function registerViewServiceProvider(Container $container) + public function registerViewServiceProvider(Container $container): void { static $provider = null; @@ -439,107 +390,85 @@ public function registerViewServiceProvider(Container $container) /** * @param Container $container A DI container. - * @return void */ - public function registerAcl(Container $container) + public function registerAcl(Container $container): void { - $container['admin/acl'] = function () { - return new Acl(); - }; + $container['admin/acl'] = (fn(): \Laminas\Permissions\Acl\Acl => new Acl()); - $container['authorizer/acl'] = function ($container) { - return $container['admin/acl']; - }; + $container['authorizer/acl'] = (fn($container) => $container['admin/acl']); } /** * @param Container $container A DI container. - * @return void */ - public function registerAuthenticator(Container $container) + public function registerAuthenticator(Container $container): void { $this->registerLogger($container); $this->registerModelServiceProvider($container); - $container['admin/authenticator'] = function (Container $container) { - return new Authenticator([ - 'logger' => $container['logger'], - 'user_type' => AdminUser::class, - 'user_factory' => $container['model/factory'], - 'token_type' => AdminAuthToken::class, - 'token_factory' => $container['model/factory'], - ]); - }; + $container['admin/authenticator'] = (fn(Container $container): \Charcoal\User\Authenticator => new Authenticator([ + 'logger' => $container['logger'], + 'user_type' => AdminUser::class, + 'user_factory' => $container['model/factory'], + 'token_type' => AdminAuthToken::class, + 'token_factory' => $container['model/factory'], + ])); - $container['authenticator'] = function (Container $container) { - return $container['admin/authenticator']; - }; + $container['authenticator'] = (fn(Container $container): mixed => $container['admin/authenticator']); } /** * @param Container $container A DI container. - * @return void */ - public function registerAuthorizer(Container $container) + public function registerAuthorizer(Container $container): void { $this->registerLogger($container); $this->registerAcl($container); - $container['admin/authorizer'] = function (Container $container) { - return new Authorizer([ - 'logger' => $container['logger'], - 'acl' => $container['admin/acl'], - 'resource' => 'admin', - ]); - }; + $container['admin/authorizer'] = (fn(Container $container): \Charcoal\User\Authorizer => new Authorizer([ + 'logger' => $container['logger'], + 'acl' => $container['admin/acl'], + 'resource' => 'admin', + ])); - $container['authorizer'] = function (Container $container) { - return $container['admin/authorizer']; - }; + $container['authorizer'] = (fn(Container $container): mixed => $container['admin/authorizer']); } /** * @param Container $container A DI container. - * @return void */ - public function registerPropertyDisplayFactory(Container $container) + public function registerPropertyDisplayFactory(Container $container): void { $this->registerDatabase($container); $this->registerLogger($container); - $container['property/display/factory'] = function (Container $container) { - return new Factory([ - 'resolver_options' => [ - 'suffix' => 'Display' - ], - 'arguments' => [[ - 'container' => $container, - 'logger' => $container['logger'] - ]] - ]); - }; + $container['property/display/factory'] = (fn(Container $container): \Charcoal\Factory\GenericFactory => new Factory([ + 'resolver_options' => [ + 'suffix' => 'Display' + ], + 'arguments' => [[ + 'container' => $container, + 'logger' => $container['logger'] + ]] + ])); } /** * @param Container $container A DI container. - * @return void */ - public function registerEmailFactory(Container $container) + public function registerEmailFactory(Container $container): void { - $container['email/factory'] = function () { - return new Factory([ - 'map' => [ - 'email' => Email::class, - ], - ]); - }; + $container['email/factory'] = (fn(): \Charcoal\Factory\GenericFactory => new Factory([ + 'map' => [ + 'email' => Email::class, + ], + ])); } /** * @param Container $container A DI container. - * @return void */ - public function registerActionDependencies(Container $container) + public function registerActionDependencies(Container $container): void { $this->registerDebug($container); $this->registerLogger($container); @@ -559,9 +488,8 @@ public function registerActionDependencies(Container $container) /** * @param Container $container A DI container. - * @return void */ - public function registerTemplateDependencies(Container $container) + public function registerTemplateDependencies(Container $container): void { $this->registerDebug($container); $this->registerLogger($container); @@ -584,9 +512,8 @@ public function registerTemplateDependencies(Container $container) /** * @param Container $container A DI container. - * @return void */ - public function registerWidgetDependencies(Container $container) + public function registerWidgetDependencies(Container $container): void { $this->registerDebug($container); $this->registerLogger($container); @@ -606,9 +533,8 @@ public function registerWidgetDependencies(Container $container) /** * @param Container $container A DI container. - * @return void */ - public function registerInputDependencies(Container $container) + public function registerInputDependencies(Container $container): void { $this->registerDebug($container); $this->registerLogger($container); @@ -628,9 +554,8 @@ public function registerInputDependencies(Container $container) /** * @param Container $container A DI container. - * @return void */ - public function registerScriptDependencies(Container $container) + public function registerScriptDependencies(Container $container): void { $this->registerDebug($container); $this->registerLogger($container); diff --git a/packages/admin/tests/Charcoal/Admin/Mock/AuthToken.php b/packages/admin/tests/Charcoal/Admin/Mock/AuthToken.php index 88b634a31..19258cbfd 100644 --- a/packages/admin/tests/Charcoal/Admin/Mock/AuthToken.php +++ b/packages/admin/tests/Charcoal/Admin/Mock/AuthToken.php @@ -1,5 +1,7 @@ isEnabled()) { - return false; - } - - return true; + return $this->isEnabled(); } /** * @return boolean */ + #[\Override] public function deleteCookie() { - if (!$this->isEnabled()) { - return false; - } - - return true; + return $this->isEnabled(); } } diff --git a/packages/admin/tests/Charcoal/Admin/Mock/SortableModel.php b/packages/admin/tests/Charcoal/Admin/Mock/SortableModel.php index 61678f63c..46d140d03 100644 --- a/packages/admin/tests/Charcoal/Admin/Mock/SortableModel.php +++ b/packages/admin/tests/Charcoal/Admin/Mock/SortableModel.php @@ -1,5 +1,7 @@ [ diff --git a/packages/admin/tests/Charcoal/Admin/Mock/UserProviderTrait.php b/packages/admin/tests/Charcoal/Admin/Mock/UserProviderTrait.php index 78997e386..8dcb61b01 100644 --- a/packages/admin/tests/Charcoal/Admin/Mock/UserProviderTrait.php +++ b/packages/admin/tests/Charcoal/Admin/Mock/UserProviderTrait.php @@ -56,14 +56,14 @@ protected function createUser( * @param string $email The email to lookup. * @return User */ - protected function userExists($email) + protected function userExists($email): bool { $container = $this->container(); $user = $container['model/factory']->create($this->userClass); $user->loadFrom('email', $email); - return !!$user->id(); + return (bool) $user->id(); } /** diff --git a/packages/admin/tests/Charcoal/Admin/Property/AbstractInputTest.php b/packages/admin/tests/Charcoal/Admin/Property/AbstractInputTest.php index 9a231a339..d491e72fd 100644 --- a/packages/admin/tests/Charcoal/Admin/Property/AbstractInputTest.php +++ b/packages/admin/tests/Charcoal/Admin/Property/AbstractInputTest.php @@ -22,14 +22,9 @@ class AbstractInputTest extends AbstractTestCase /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; - /** - * @return void - */ public function setUp(): void { $container = $this->container(); @@ -42,10 +37,7 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testSetData() + public function testSetData(): void { $obj = $this->obj; $ret = $obj->setData([ @@ -63,12 +55,10 @@ public function testSetData() /** * Set up the service container. - * - * @return Container */ - protected function container() + protected function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerInputDependencies($container); diff --git a/packages/admin/tests/Charcoal/Admin/Property/Input/TextInputTest.php b/packages/admin/tests/Charcoal/Admin/Property/Input/TextInputTest.php index 78154afe5..876fb56a5 100644 --- a/packages/admin/tests/Charcoal/Admin/Property/Input/TextInputTest.php +++ b/packages/admin/tests/Charcoal/Admin/Property/Input/TextInputTest.php @@ -15,14 +15,8 @@ */ class TextInputTest extends AbstractTestCase { - /** - * @var TextInput - */ - private $obj; - - /** - * @return void - */ + private \Charcoal\Admin\Property\Input\TextInput $obj; + public function setUp(): void { $container = new Container(); @@ -36,10 +30,7 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testSetData() + public function testSetData(): void { $obj = $this->obj; $ret = $obj->setData([ @@ -53,14 +44,11 @@ public function testSetData() $this->assertEquals(42, $obj->size()); $this->assertEquals(10, $obj->minLength()); $this->assertEquals(100, $obj->maxLength()); - $this->assertEquals('foo', (string)$obj->pattern()); + $this->assertEquals('foo', $obj->pattern()); $this->assertEquals('bar', (string)$obj->placeholder()); } - /** - * @return void - */ - public function testSetSize() + public function testSetSize(): void { $obj = $this->obj; $ret = $obj->setSize(42); @@ -71,10 +59,7 @@ public function testSetSize() $obj->setSize(false); } - /** - * @return void - */ - public function testSetMinLength() + public function testSetMinLength(): void { $obj = $this->obj; $ret = $obj->setMinLength(42); @@ -85,10 +70,7 @@ public function testSetMinLength() $obj->setMinLength(false); } - /** - * @return void - */ - public function testSetMaxLength() + public function testSetMaxLength(): void { $obj = $this->obj; $ret = $obj->setMaxLength(42); @@ -99,10 +81,7 @@ public function testSetMaxLength() $obj->setMaxLength(false); } - /** - * @return void - */ - public function testSetPattern() + public function testSetPattern(): void { $obj = $this->obj; $ret = $obj->setPattern('foo'); @@ -113,10 +92,7 @@ public function testSetPattern() $obj->setPattern(false); } - /** - * @return void - */ - public function testSetPlaceholder() + public function testSetPlaceholder(): void { $obj = $this->obj; $ret = $obj->setPlaceholder('foo'); diff --git a/packages/admin/tests/Charcoal/Admin/Property/Input/TextareaInputTest.php b/packages/admin/tests/Charcoal/Admin/Property/Input/TextareaInputTest.php index 12fce74e1..0a27b823e 100644 --- a/packages/admin/tests/Charcoal/Admin/Property/Input/TextareaInputTest.php +++ b/packages/admin/tests/Charcoal/Admin/Property/Input/TextareaInputTest.php @@ -17,21 +17,14 @@ class TextareaInputTest extends AbstractTestCase { /** * Tested Class. - * - * @var TextareaInput */ - private $obj; + private \Charcoal\Admin\Property\Input\TextareaInput $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; - /** - * @return void - */ public function setUp(): void { $container = $this->container(); @@ -42,10 +35,7 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testSetData() + public function testSetData(): void { $obj = $this->obj; $ret = $obj->setData([ @@ -57,10 +47,7 @@ public function testSetData() $this->assertEquals(84, $obj->rows()); } - /** - * @return void - */ - public function testSetCols() + public function testSetCols(): void { $obj = $this->obj; $ret = $obj->setCols(42); @@ -72,10 +59,7 @@ public function testSetCols() $obj->setCols('foo'); } - /** - * @return void - */ - public function testSetRows() + public function testSetRows(): void { $obj = $this->obj; $ret = $obj->setRows(42); @@ -89,12 +73,10 @@ public function testSetRows() /** * Set up the service container. - * - * @return Container */ - protected function container() + protected function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerInputDependencies($container); diff --git a/packages/admin/tests/Charcoal/Admin/Script/Notification/ProcessDailyScriptTest.php b/packages/admin/tests/Charcoal/Admin/Script/Notification/ProcessDailyScriptTest.php index 5fb93807f..96a5beb6d 100644 --- a/packages/admin/tests/Charcoal/Admin/Script/Notification/ProcessDailyScriptTest.php +++ b/packages/admin/tests/Charcoal/Admin/Script/Notification/ProcessDailyScriptTest.php @@ -21,36 +21,25 @@ class ProcessDailyScriptTest extends AbstractTestCase { use ReflectionsTrait; - /** - * @var Container - */ - private $container; + private \Pimple\Container $container; /** * Instance of class under test * @var CreateScript */ - private $obj; + private \Charcoal\Admin\Script\Notification\ProcessDailyScript $obj; - /** - * @return Container - */ - private function getContainer() + private function getContainer(): \Pimple\Container { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerScriptDependencies($container); - $container['email/factory'] = function(Container $container) { - return $container['model/factory']; - }; + $container['email/factory'] = (fn(Container $container): mixed => $container['model/factory']); return $container; } - /** - * @return void - */ public function setUp(): void { $this->container = $this->getContainer(); @@ -66,19 +55,13 @@ public function setUp(): void } - /** - * @return void - */ - public function testDefaultArguments() + public function testDefaultArguments(): void { $args = $this->obj->defaultArguments(); $this->assertArrayHasKey('now', $args); } - /** - * @return void - */ - public function testFrequency() + public function testFrequency(): void { $this->assertEquals('daily', $this->callMethod($this->obj, 'frequency')); } diff --git a/packages/admin/tests/Charcoal/Admin/Script/Notification/ProcessHourlyScriptTest.php b/packages/admin/tests/Charcoal/Admin/Script/Notification/ProcessHourlyScriptTest.php index 398cbdf37..4c4b78a96 100644 --- a/packages/admin/tests/Charcoal/Admin/Script/Notification/ProcessHourlyScriptTest.php +++ b/packages/admin/tests/Charcoal/Admin/Script/Notification/ProcessHourlyScriptTest.php @@ -21,36 +21,25 @@ class ProcessHourlyScriptTest extends AbstractTestCase { use ReflectionsTrait; - /** - * @var Container - */ - private $container; + private \Pimple\Container $container; /** * Instance of class under test * @var CreateScript */ - private $obj; + private \Charcoal\Admin\Script\Notification\ProcessHourlyScript $obj; - /** - * @return Container - */ - private function getContainer() + private function getContainer(): \Pimple\Container { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerScriptDependencies($container); - $container['email/factory'] = function(Container $container) { - return $container['model/factory']; - }; + $container['email/factory'] = (fn(Container $container): mixed => $container['model/factory']); return $container; } - /** - * @return void - */ public function setUp(): void { $this->container = $this->getContainer(); @@ -66,19 +55,13 @@ public function setUp(): void } - /** - * @return void - */ - public function testDefaultArguments() + public function testDefaultArguments(): void { $args = $this->obj->defaultArguments(); $this->assertArrayHasKey('now', $args); } - /** - * @return void - */ - public function testFrequency() + public function testFrequency(): void { $this->assertEquals('hourly', $this->callMethod($this->obj, 'frequency')); } diff --git a/packages/admin/tests/Charcoal/Admin/Script/Notification/ProcessMinuteScriptTest.php b/packages/admin/tests/Charcoal/Admin/Script/Notification/ProcessMinuteScriptTest.php index 9e45bc5ed..216af9ff4 100644 --- a/packages/admin/tests/Charcoal/Admin/Script/Notification/ProcessMinuteScriptTest.php +++ b/packages/admin/tests/Charcoal/Admin/Script/Notification/ProcessMinuteScriptTest.php @@ -21,36 +21,25 @@ class ProcessMinuteScriptTest extends AbstractTestCase { use ReflectionsTrait; - /** - * @var Container - */ - private $container; + private \Pimple\Container $container; /** * Instance of class under test * @var CreateScript */ - private $obj; + private \Charcoal\Admin\Script\Notification\ProcessMinuteScript $obj; - /** - * @return Container - */ - private function getContainer() + private function getContainer(): \Pimple\Container { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerScriptDependencies($container); - $container['email/factory'] = function(Container $container) { - return $container['model/factory']; - }; + $container['email/factory'] = (fn(Container $container): mixed => $container['model/factory']); return $container; } - /** - * @return void - */ public function setUp(): void { $this->container = $this->getContainer(); @@ -66,19 +55,13 @@ public function setUp(): void } - /** - * @return void - */ - public function testDefaultArguments() + public function testDefaultArguments(): void { $args = $this->obj->defaultArguments(); $this->assertArrayHasKey('now', $args); } - /** - * @return void - */ - public function testFrequency() + public function testFrequency(): void { $this->assertEquals('minute', $this->callMethod($this->obj, 'frequency')); } diff --git a/packages/admin/tests/Charcoal/Admin/Script/Notification/ProcessMonthlyScriptTest.php b/packages/admin/tests/Charcoal/Admin/Script/Notification/ProcessMonthlyScriptTest.php index 4e4e934a5..e9a14f4f8 100644 --- a/packages/admin/tests/Charcoal/Admin/Script/Notification/ProcessMonthlyScriptTest.php +++ b/packages/admin/tests/Charcoal/Admin/Script/Notification/ProcessMonthlyScriptTest.php @@ -21,36 +21,25 @@ class ProcessMonthlyScriptTest extends AbstractTestCase { use ReflectionsTrait; - /** - * @var Container - */ - private $container; + private \Pimple\Container $container; /** * Instance of class under test * @var CreateScript */ - private $obj; + private \Charcoal\Admin\Script\Notification\ProcessMonthlyScript $obj; - /** - * @return Container - */ - private function getContainer() + private function getContainer(): \Pimple\Container { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerScriptDependencies($container); - $container['email/factory'] = function(Container $container) { - return $container['model/factory']; - }; + $container['email/factory'] = (fn(Container $container): mixed => $container['model/factory']); return $container; } - /** - * @return void - */ public function setUp(): void { $this->container = $this->getContainer(); @@ -66,19 +55,13 @@ public function setUp(): void } - /** - * @return void - */ - public function testDefaultArguments() + public function testDefaultArguments(): void { $args = $this->obj->defaultArguments(); $this->assertArrayHasKey('now', $args); } - /** - * @return void - */ - public function testFrequency() + public function testFrequency(): void { $this->assertEquals('monthly', $this->callMethod($this->obj, 'frequency')); } diff --git a/packages/admin/tests/Charcoal/Admin/Script/Notification/ProcessWeeklyScriptTest.php b/packages/admin/tests/Charcoal/Admin/Script/Notification/ProcessWeeklyScriptTest.php index f53a98581..d43177a70 100644 --- a/packages/admin/tests/Charcoal/Admin/Script/Notification/ProcessWeeklyScriptTest.php +++ b/packages/admin/tests/Charcoal/Admin/Script/Notification/ProcessWeeklyScriptTest.php @@ -21,36 +21,25 @@ class ProcessWeeklyScriptTest extends AbstractTestCase { use ReflectionsTrait; - /** - * @var Container - */ - private $container; + private \Pimple\Container $container; /** * Instance of class under test * @var CreateScript */ - private $obj; + private \Charcoal\Admin\Script\Notification\ProcessWeeklyScript $obj; - /** - * @return Container - */ - private function getContainer() + private function getContainer(): \Pimple\Container { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerScriptDependencies($container); - $container['email/factory'] = function(Container $container) { - return $container['model/factory']; - }; + $container['email/factory'] = (fn(Container $container): mixed => $container['model/factory']); return $container; } - /** - * @return void - */ public function setUp(): void { $this->container = $this->getContainer(); @@ -66,19 +55,13 @@ public function setUp(): void } - /** - * @return void - */ - public function testDefaultArguments() + public function testDefaultArguments(): void { $args = $this->obj->defaultArguments(); $this->assertArrayHasKey('now', $args); } - /** - * @return void - */ - public function testFrequency() + public function testFrequency(): void { $this->assertEquals('weekly', $this->callMethod($this->obj, 'frequency')); } diff --git a/packages/admin/tests/Charcoal/Admin/Script/Object/Table/CreateScriptTest.php b/packages/admin/tests/Charcoal/Admin/Script/Object/Table/CreateScriptTest.php index aea0c9675..c9889c991 100644 --- a/packages/admin/tests/Charcoal/Admin/Script/Object/Table/CreateScriptTest.php +++ b/packages/admin/tests/Charcoal/Admin/Script/Object/Table/CreateScriptTest.php @@ -31,21 +31,14 @@ */ class CreateScriptTest extends AbstractTestCase { - /** - * @var Container - */ - private $container; + private \Pimple\Container $container; /** * Instance of class under test - * @var CreateScript */ - private $obj; + private \Charcoal\Admin\Script\Object\Table\CreateScript $obj; - /** - * @return Container - */ - private function getContainer() + private function getContainer(): \Pimple\Container { $container = new Container(); $containerProvider = new ContainerProvider(); @@ -53,9 +46,6 @@ private function getContainer() return $container; } - /** - * @return void - */ public function setUp(): void { $this->container = $this->getContainer(); @@ -71,10 +61,7 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testDefaultArguments() + public function testDefaultArguments(): void { $args = $this->obj->defaultArguments(); diff --git a/packages/admin/tests/Charcoal/Admin/Script/User/CreateScriptTest.php b/packages/admin/tests/Charcoal/Admin/Script/User/CreateScriptTest.php index 18e44d639..d93bcc516 100644 --- a/packages/admin/tests/Charcoal/Admin/Script/User/CreateScriptTest.php +++ b/packages/admin/tests/Charcoal/Admin/Script/User/CreateScriptTest.php @@ -31,21 +31,14 @@ */ class CreateScriptTest extends AbstractTestCase { - /** - * @var Container - */ - private $container; + private \Pimple\Container $container; /** * Instance of class under test - * @var CreateScript */ - private $obj; + private \Charcoal\Admin\Script\User\CreateScript $obj; - /** - * @return Container - */ - private function getContainer() + private function getContainer(): \Pimple\Container { $container = new Container(); $containerProvider = new ContainerProvider(); @@ -53,9 +46,6 @@ private function getContainer() return $container; } - /** - * @return void - */ public function setUp(): void { $this->container = $this->getContainer(); @@ -68,10 +58,7 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testDefaultArguments() + public function testDefaultArguments(): void { $args = $this->obj->defaultArguments(); @@ -80,10 +67,7 @@ public function testDefaultArguments() $this->assertArrayHasKey('roles', $args); } - /** - * @return void - */ - public function testArguments() + public function testArguments(): void { $args = $this->obj->arguments(); @@ -92,20 +76,6 @@ public function testArguments() $this->assertArrayHasKey('roles', $args); } - /** - * @return integer - */ - private function numAdminUsersInSource() - { - $source = $this->container['model/factory']->create('charcoal/admin/user')->source(); - $source->createTable(); - - $table = $source->table(); - $q = 'select count(`email`) as num from `'.$table.'`'; - $req = $this->container['database']->query($q); - return $req->fetchColumn(0); - } - // public function testInvoke() // { // // Ensure that no admin user exists in test database diff --git a/packages/admin/tests/Charcoal/Admin/Script/User/ResetPasswordScriptTest.php b/packages/admin/tests/Charcoal/Admin/Script/User/ResetPasswordScriptTest.php index fbf9f9140..0caa45b52 100644 --- a/packages/admin/tests/Charcoal/Admin/Script/User/ResetPasswordScriptTest.php +++ b/packages/admin/tests/Charcoal/Admin/Script/User/ResetPasswordScriptTest.php @@ -31,21 +31,15 @@ */ class ResetPasswordScriptTest extends AbstractTestCase { - /** - * @var Container - */ - private $container; + private \Pimple\Container $container; /** * Instance of class under test * @var CreateScript */ - private $obj; + private \Charcoal\Admin\Script\User\ResetPasswordScript $obj; - /** - * @return Container - */ - private function getContainer() + private function getContainer(): \Pimple\Container { $container = new Container(); $containerProvider = new ContainerProvider(); @@ -53,9 +47,6 @@ private function getContainer() return $container; } - /** - * @return void - */ public function setUp(): void { $this->container = $this->getContainer(); @@ -70,10 +61,7 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testDefaultArguments() + public function testDefaultArguments(): void { $args = $this->obj->defaultArguments(); @@ -81,10 +69,7 @@ public function testDefaultArguments() $this->assertArrayHasKey('password', $args); } - /** - * @return void - */ - public function testArguments() + public function testArguments(): void { $args = $this->obj->arguments(); diff --git a/packages/admin/tests/Charcoal/Admin/Service/ExporterTest.php b/packages/admin/tests/Charcoal/Admin/Service/ExporterTest.php index 49551abe9..b3a7eb8d8 100644 --- a/packages/admin/tests/Charcoal/Admin/Service/ExporterTest.php +++ b/packages/admin/tests/Charcoal/Admin/Service/ExporterTest.php @@ -21,51 +21,27 @@ */ class ExporterTest extends AbstractTestCase { - /** - * @var Exporter - */ - private $obj; - /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; - /** - * @return void - */ public function setUp(): void { - $container = $this->container(); - - $this->obj = new Exporter([ - 'logger' => $container['logger'], - 'factory' => $container['model/factory'], - 'propertyFactory' => $container['property/factory'], - 'translator' => $container['translator'], - 'obj_type' => 'charcoal/admin/user', - 'export_ident' => 'y', - ]); + $this->container(); } - /** - * @return void - */ - public function testExport() + public function testExport(): void { $this->assertTrue(true); } /** * Set up the service container. - * - * @return Container */ - protected function container() + protected function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerBaseServices($container); @@ -73,7 +49,7 @@ protected function container() $containerProvider->registerModelServiceProvider($container); $containerProvider->registerTranslatorServiceProvider($container); - $container['view'] = $this->createMock('\Charcoal\View\ViewInterface'); + $container['view'] = $this->createMock(\Charcoal\View\ViewInterface::class); $this->container = $container; } diff --git a/packages/admin/tests/Charcoal/Admin/ServiceProvider/AclServiceProviderTest.php b/packages/admin/tests/Charcoal/Admin/ServiceProvider/AclServiceProviderTest.php index fd87dc892..b53310ee0 100644 --- a/packages/admin/tests/Charcoal/Admin/ServiceProvider/AclServiceProviderTest.php +++ b/packages/admin/tests/Charcoal/Admin/ServiceProvider/AclServiceProviderTest.php @@ -14,10 +14,7 @@ */ class AclServiceProviderTest extends AbstractTestCase { - /** - * @return void - */ - public function testProvider() + public function testProvider(): void { $container = new Container([ 'config' => [] diff --git a/packages/admin/tests/Charcoal/Admin/Template/Account/LostPasswordTemplateTest.php b/packages/admin/tests/Charcoal/Admin/Template/Account/LostPasswordTemplateTest.php index 6960e7dd1..d48415a92 100644 --- a/packages/admin/tests/Charcoal/Admin/Template/Account/LostPasswordTemplateTest.php +++ b/packages/admin/tests/Charcoal/Admin/Template/Account/LostPasswordTemplateTest.php @@ -23,11 +23,8 @@ class LostPasswordTemplateTest extends AbstractTestCase * Instance of object under test * @var LoginTemplate */ - private $obj; + private \Charcoal\Admin\Template\Account\LostPasswordTemplate $obj; - /** - * @return void - */ public function setUp(): void { $this->obj = new LostPasswordTemplate([ @@ -35,10 +32,7 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testAuthRequiredIsFalse() + public function testAuthRequiredIsFalse(): void { $res = $this->callMethod($this->obj, 'authRequired'); $this->assertNotTrue($res); diff --git a/packages/admin/tests/Charcoal/Admin/Template/Account/ResetPasswordTemplateTest.php b/packages/admin/tests/Charcoal/Admin/Template/Account/ResetPasswordTemplateTest.php index 23f551637..69cb2eb62 100644 --- a/packages/admin/tests/Charcoal/Admin/Template/Account/ResetPasswordTemplateTest.php +++ b/packages/admin/tests/Charcoal/Admin/Template/Account/ResetPasswordTemplateTest.php @@ -23,11 +23,8 @@ class ResetPasswordTemplateTest extends AbstractTestCase * Instance of object under test * @var LoginTemplate */ - private $obj; + private \Charcoal\Admin\Template\Account\ResetPasswordTemplate $obj; - /** - * @return void - */ public function setUp(): void { $this->obj = new ResetPasswordTemplate([ @@ -35,10 +32,7 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testAuthRequiredIsFalse() + public function testAuthRequiredIsFalse(): void { $res = $this->callMethod($this->obj, 'authRequired'); $this->assertNotTrue($res); diff --git a/packages/admin/tests/Charcoal/Admin/Template/ElfinderTemplateTest.php b/packages/admin/tests/Charcoal/Admin/Template/ElfinderTemplateTest.php index 750206148..ac56a0043 100644 --- a/packages/admin/tests/Charcoal/Admin/Template/ElfinderTemplateTest.php +++ b/packages/admin/tests/Charcoal/Admin/Template/ElfinderTemplateTest.php @@ -19,22 +19,16 @@ class ElfinderTemplateTest extends AbstractTestCase { /** * Tested Class. - * - * @var ElfinderTemplate */ - private $obj; + private \Charcoal\Admin\Template\ElfinderTemplate $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ public function setUp(): void { @@ -46,10 +40,7 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testAdminAssertsUrl() + public function testAdminAssertsUrl(): void { $ret = $this->obj->adminAssetsUrl(); $this->assertEquals('/assets/admin/', $ret); @@ -57,17 +48,15 @@ public function testAdminAssertsUrl() /** * Set up the service container. - * - * @return Container */ - protected function container() + protected function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerTemplateDependencies($container); $containerProvider->registerElfinderConfig($container); - $container['widget/factory'] = $this->createMock('\Charcoal\Factory\FactoryInterface'); + $container['widget/factory'] = $this->createMock(\Charcoal\Factory\FactoryInterface::class); $this->container = $container; } diff --git a/packages/admin/tests/Charcoal/Admin/Template/LoginTemplateTest.php b/packages/admin/tests/Charcoal/Admin/Template/LoginTemplateTest.php index a216acde6..6869de520 100644 --- a/packages/admin/tests/Charcoal/Admin/Template/LoginTemplateTest.php +++ b/packages/admin/tests/Charcoal/Admin/Template/LoginTemplateTest.php @@ -21,13 +21,9 @@ class LoginTemplateTest extends AbstractTestCase /** * Instance of object under test - * @var LoginTemplate */ - private $obj; + private \Charcoal\Admin\Template\LoginTemplate $obj; - /** - * @return void - */ public function setUp(): void { $this->obj = new LoginTemplate([ @@ -35,10 +31,7 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testAuthRequiredIsFalse() + public function testAuthRequiredIsFalse(): void { $res = $this->callMethod($this->obj, 'authRequired'); $this->assertNotTrue($res); diff --git a/packages/admin/tests/Charcoal/Admin/Template/LogoutTemplateTest.php b/packages/admin/tests/Charcoal/Admin/Template/LogoutTemplateTest.php index 8dacfb792..5d3731c6f 100644 --- a/packages/admin/tests/Charcoal/Admin/Template/LogoutTemplateTest.php +++ b/packages/admin/tests/Charcoal/Admin/Template/LogoutTemplateTest.php @@ -22,9 +22,6 @@ class LogoutTemplateTest extends AbstractTestCase */ public $obj; - /** - * @return void - */ public function setUp(): void { $this->obj = new LogoutTemplate([ @@ -32,10 +29,7 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testAuthRequiredIsFalse() + public function testAuthRequiredIsFalse(): void { $res = $this->callMethod($this->obj, 'authRequired'); $this->assertNotTrue($res); diff --git a/packages/admin/tests/Charcoal/Admin/Template/Object/CollectionTemplateTest.php b/packages/admin/tests/Charcoal/Admin/Template/Object/CollectionTemplateTest.php index 2281d714b..f1c44b1f9 100644 --- a/packages/admin/tests/Charcoal/Admin/Template/Object/CollectionTemplateTest.php +++ b/packages/admin/tests/Charcoal/Admin/Template/Object/CollectionTemplateTest.php @@ -22,22 +22,16 @@ class CollectionTemplateTest extends AbstractTestCase /** * Tested Class. - * - * @var CollectionTemplate */ - private $obj; + private \Charcoal\Admin\Template\Object\CollectionTemplate $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ public function setUp(): void { @@ -50,28 +44,19 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testAuthRequiredIsTrue() + public function testAuthRequiredIsTrue(): void { $res = $this->callMethod($this->obj, 'authRequired'); $this->assertTrue($res); } - /** - * @return void - */ - public function testInit() + public function testInit(): void { //$ret = $this->obj->init(); $this->assertTrue(true); } - /** - * @return void - */ - public function testTitle() + public function testTitle(): void { $this->obj->setObjType('charcoal/admin/user'); $ret = $this->obj->title(); @@ -82,12 +67,10 @@ public function testTitle() /** * Set up the service container. - * - * @return Container */ - protected function container() + protected function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerTemplateDependencies($container); diff --git a/packages/admin/tests/Charcoal/Admin/Template/Object/CreateTemplateTest.php b/packages/admin/tests/Charcoal/Admin/Template/Object/CreateTemplateTest.php index c392c1d3e..4860ebd7c 100644 --- a/packages/admin/tests/Charcoal/Admin/Template/Object/CreateTemplateTest.php +++ b/packages/admin/tests/Charcoal/Admin/Template/Object/CreateTemplateTest.php @@ -21,22 +21,16 @@ class CreateTemplateTest extends AbstractTestCase /** * Tested Class. - * - * @var CreateTemplate */ - private $obj; + private \Charcoal\Admin\Template\Object\CreateTemplate $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ public function setUp(): void { @@ -50,19 +44,13 @@ public function setUp(): void //$this->obj->setDependencies($container); } - /** - * @return void - */ - public function testAuthRequiredIsTrue() + public function testAuthRequiredIsTrue(): void { $res = $this->callMethod($this->obj, 'authRequired'); $this->assertTrue($res); } - /** - * @return void - */ - public function testTitle() + public function testTitle(): void { $this->obj->setObjType('charcoal/admin/user'); $ret = $this->obj->title(); @@ -73,12 +61,10 @@ public function testTitle() /** * Set up the service container. - * - * @return Container */ - protected function container() + protected function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerTemplateDependencies($container); diff --git a/packages/admin/tests/Charcoal/Admin/Template/Object/EditTemplateTest.php b/packages/admin/tests/Charcoal/Admin/Template/Object/EditTemplateTest.php index 0bae749e0..f26fe68b6 100644 --- a/packages/admin/tests/Charcoal/Admin/Template/Object/EditTemplateTest.php +++ b/packages/admin/tests/Charcoal/Admin/Template/Object/EditTemplateTest.php @@ -21,22 +21,16 @@ class EditTemplateTest extends AbstractTestCase /** * Tested Class. - * - * @var EditTemplate */ - private $obj; + private \Charcoal\Admin\Template\Object\EditTemplate $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ public function setUp(): void { @@ -50,19 +44,13 @@ public function setUp(): void //$this->obj->setDependencies($container); } - /** - * @return void - */ - public function testAuthRequiredIsTrue() + public function testAuthRequiredIsTrue(): void { $res = $this->callMethod($this->obj, 'authRequired'); $this->assertTrue($res); } - /** - * @return void - */ - public function testTitle() + public function testTitle(): void { $this->obj->setObjType('charcoal/admin/user'); $ret = $this->obj->title(); @@ -73,12 +61,10 @@ public function testTitle() /** * Set up the service container. - * - * @return Container */ - protected function container() + protected function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerTemplateDependencies($container); diff --git a/packages/admin/tests/Charcoal/Admin/Widget/CollectionMapWidgetTest.php b/packages/admin/tests/Charcoal/Admin/Widget/CollectionMapWidgetTest.php index 55fa2f365..d01e02845 100644 --- a/packages/admin/tests/Charcoal/Admin/Widget/CollectionMapWidgetTest.php +++ b/packages/admin/tests/Charcoal/Admin/Widget/CollectionMapWidgetTest.php @@ -19,9 +19,6 @@ class CollectionMapWidgetTest extends AbstractTestCase */ public $obj; - /** - * @return void - */ public function setUp(): void { $logger = new NullLogger(); @@ -30,40 +27,28 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testSetLatProperty() + public function testSetLatProperty(): void { $ret = $this->obj->setLatProperty('foo'); $this->assertSame($ret, $this->obj); $this->assertEquals('foo', $this->obj->latProperty()); } - /** - * @return void - */ - public function testSetLonProperty() + public function testSetLonProperty(): void { $ret = $this->obj->setLonProperty('foo'); $this->assertSame($ret, $this->obj); $this->assertEquals('foo', $this->obj->lonProperty()); } - /** - * @return void - */ - public function testSetPolygonProperty() + public function testSetPolygonProperty(): void { $ret = $this->obj->setPolygonProperty('foo'); $this->assertSame($ret, $this->obj); $this->assertEquals('foo', $this->obj->polygonProperty()); } - /** - * @return void - */ - public function testSetInfoboxTemplate() + public function testSetInfoboxTemplate(): void { $ret = $this->obj->setInfoboxTemplate('foo'); $this->assertSame($ret, $this->obj); diff --git a/packages/admin/tests/Charcoal/Admin/Widget/FormGroupWidgetTest.php b/packages/admin/tests/Charcoal/Admin/Widget/FormGroupWidgetTest.php index d44421d2a..cce0be26f 100644 --- a/packages/admin/tests/Charcoal/Admin/Widget/FormGroupWidgetTest.php +++ b/packages/admin/tests/Charcoal/Admin/Widget/FormGroupWidgetTest.php @@ -18,9 +18,7 @@ */ class FormGroupWidgetTest extends AbstractTestCase { - /** - * @return void - */ + public $obj; public function setUp(): void { $container = new Container(); @@ -31,7 +29,7 @@ public function setUp(): void $containerProvider->registerAuthenticator($container); - $container['form/input/builder'] = $this->createMock(\Charcoal\Ui\FormInput\FormInputBuilder::class, ''); + $container['form/input/builder'] = $this->createMock(\Charcoal\Ui\FormInput\FormInputBuilder::class); $container['authorizer'] = $container['admin/authorizer']; $container['authenticator'] = $container['admin/authenticator']; @@ -42,10 +40,7 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testConstructor() + public function testConstructor(): void { $this->assertInstanceOf(FormGroupWidget::class, $this->obj); } diff --git a/packages/admin/tests/Charcoal/Admin/Widget/FormPropertyWidgetTest.php b/packages/admin/tests/Charcoal/Admin/Widget/FormPropertyWidgetTest.php index 7ed853709..dc4d8aca9 100644 --- a/packages/admin/tests/Charcoal/Admin/Widget/FormPropertyWidgetTest.php +++ b/packages/admin/tests/Charcoal/Admin/Widget/FormPropertyWidgetTest.php @@ -18,9 +18,7 @@ */ class FormPropertyWidgetTest extends AbstractTestCase { - /** - * @return void - */ + public $obj; public function setUp(): void { $container = new Container(); @@ -36,18 +34,12 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testConstructor() + public function testConstructor(): void { $this->assertInstanceOf(FormPropertyWidget::class, $this->obj); } - /** - * @return void - */ - public function testSetOutputType() + public function testSetOutputType(): void { //$this->assertEquals(FormPropertyWidget::DEFAULT_OUTPUT, $this->obj->outputType()); @@ -68,10 +60,7 @@ public function testSetOutputType() $this->obj->setOutputType('foobar'); } - /** - * @return void - */ - public function testPropertyType() + public function testPropertyType(): void { $this->assertNull($this->obj->propertyType()); diff --git a/packages/admin/tests/Charcoal/Admin/Widget/FormSidebarWidgetTest.php b/packages/admin/tests/Charcoal/Admin/Widget/FormSidebarWidgetTest.php index ad78b17d4..7712df3de 100644 --- a/packages/admin/tests/Charcoal/Admin/Widget/FormSidebarWidgetTest.php +++ b/packages/admin/tests/Charcoal/Admin/Widget/FormSidebarWidgetTest.php @@ -18,9 +18,7 @@ */ class FormSidebarWidgetTest extends AbstractTestCase { - /** - * @return void - */ + public $obj; public function setUp(): void { $container = new Container(); @@ -36,10 +34,7 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testConstructor() + public function testConstructor(): void { $this->assertInstanceOf(FormSidebarWidget::class, $this->obj); } diff --git a/packages/admin/tests/Charcoal/Admin/Widget/FormWidgetTest.php b/packages/admin/tests/Charcoal/Admin/Widget/FormWidgetTest.php index 1f9665814..422c4274b 100644 --- a/packages/admin/tests/Charcoal/Admin/Widget/FormWidgetTest.php +++ b/packages/admin/tests/Charcoal/Admin/Widget/FormWidgetTest.php @@ -17,13 +17,9 @@ class FormWidgetTest extends AbstractTestCase { /** * Object under test - * @var FormWidget */ - private $obj; + private \Charcoal\Admin\Widget\FormWidget $obj; - /** - * @return void - */ public function setUp(): void { $logger = new NullLogger(); @@ -32,21 +28,7 @@ public function setUp(): void ]); } - /** - * @return FormSidebarWidget - */ - private function sidebarWidget() - { - $logger = new NullLogger(); - return new FormSidebarWidget([ - 'logger' => $logger, - ]); - } - - /** - * @return void - */ - public function testConstructor() + public function testConstructor(): void { $this->assertInstanceOf(FormWidget::class, $this->obj); } diff --git a/packages/admin/tests/Charcoal/Admin/Widget/GraphWidgetTest.php b/packages/admin/tests/Charcoal/Admin/Widget/GraphWidgetTest.php index 679ee6017..2d92f5f74 100644 --- a/packages/admin/tests/Charcoal/Admin/Widget/GraphWidgetTest.php +++ b/packages/admin/tests/Charcoal/Admin/Widget/GraphWidgetTest.php @@ -14,21 +14,16 @@ */ class GraphWidgetTest extends AbstractTestCase { - /** - * @return void - */ + public $obj; public function setUp(): void { $logger = new NullLogger(); - $this->obj = $this->getMockForAbstractClass('\Charcoal\Admin\Widget\Graph\AbstractGraphWidget', [[ + $this->obj = $this->getMockForAbstractClass(\Charcoal\Admin\Widget\Graph\AbstractGraphWidget::class, [[ 'logger'=>$logger ]]); } - /** - * @return void - */ - public function testSetData() + public function testSetData(): void { $obj = $this->obj; $ret = $obj->setData([ @@ -40,10 +35,7 @@ public function testSetData() $this->assertEquals(['#ff0000', '#0000ff'], $obj->colors()); } - /** - * @return void - */ - public function testSetHeight() + public function testSetHeight(): void { $obj = $this->obj; $this->assertEquals('400px', $obj->height()); @@ -56,10 +48,7 @@ public function testSetHeight() //$obj->setHeight(false); } - /** - * @return void - */ - public function testSetColors() + public function testSetColors(): void { $obj = $this->obj; $this->assertEquals($obj->defaultColors(), $obj->colors()); diff --git a/packages/admin/tests/Charcoal/Admin/Widget/LayoutWidgetTest.php b/packages/admin/tests/Charcoal/Admin/Widget/LayoutWidgetTest.php index e336da11b..1071b1390 100644 --- a/packages/admin/tests/Charcoal/Admin/Widget/LayoutWidgetTest.php +++ b/packages/admin/tests/Charcoal/Admin/Widget/LayoutWidgetTest.php @@ -14,9 +14,7 @@ */ class LayoutWidgetTest extends AbstractTestCase { - /** - * @return void - */ + public $obj; public function setUp(): void { $logger = new NullLogger(); @@ -25,27 +23,18 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testDefaultPosition() + public function testDefaultPosition(): void { $obj = $this->obj; - $this->assertInstanceOf('\Charcoal\Admin\Widget\LayoutWidget', $obj); + $this->assertInstanceOf(\Charcoal\Admin\Widget\LayoutWidget::class, $obj); $this->assertEquals(0, $obj->position()); } - /** - * @return void - */ - public function testSetData() + public function testSetData(): void { $struct = [[ 'columns'=>[1] ]]; - $computed = [ - 'columns'=>[1] - ]; $obj = $this->obj; $ret = $obj->setData([ @@ -55,10 +44,7 @@ public function testSetData() //$this->assertEquals($computed, $obj->structure()); } - /** - * @return void - */ - public function testSetStructure() + public function testSetStructure(): void { $obj = $this->obj; $this->assertEquals([], $obj->structure()); @@ -83,10 +69,7 @@ public function testSetStructure() //$this->assertEquals($struct, $obj->structure()); } - /** - * @return void - */ - public function testNumRows() + public function testNumRows(): void { $obj = $this->obj; $this->assertEquals(0, $obj->numRows()); @@ -98,10 +81,7 @@ public function testNumRows() $this->assertEquals(3, $obj->numRows()); } - /** - * @return void - */ - public function testRowIndex() + public function testRowIndex(): void { $obj = $this->obj; $this->assertNull($obj->rowIndex()); @@ -118,10 +98,7 @@ public function testRowIndex() $this->assertEquals(0, $obj->rowIndex(5)); } - /** - * @return void - */ - public function testRowData() + public function testRowData(): void { $obj = $this->obj; $this->assertNull($obj->rowData()); @@ -138,10 +115,7 @@ public function testRowData() $this->assertNull($obj->rowData(5)); } - /** - * @return void - */ - public function testRowNumColumns() + public function testRowNumColumns(): void { $obj = $this->obj; $this->assertNull($obj->rowNumColumns()); @@ -158,10 +132,7 @@ public function testRowNumColumns() $this->assertNull($obj->rowNumColumns(5)); } - /** - * @return void - */ - public function testRowNumCells() + public function testRowNumCells(): void { $obj = $this->obj; $this->assertNull($obj->rowNumCells()); @@ -178,10 +149,7 @@ public function testRowNumCells() $this->assertNull($obj->rowNumCells(5)); } - /** - * @return void - */ - public function testRowFirstCellIndex() + public function testRowFirstCellIndex(): void { $obj = $this->obj; $this->assertNull($obj->rowFirstCellIndex()); @@ -198,10 +166,7 @@ public function testRowFirstCellIndex() //$this->assertNull($obj->rowFirstCellIndex(5)); } - /** - * @return void - */ - public function testCellRowIndex() + public function testCellRowIndex(): void { $obj = $this->obj; //$this->assertNull($obj->cellRowIndex()); @@ -218,10 +183,7 @@ public function testCellRowIndex() //$this->assertNull($obj->cellRowIndex(5)); } - /** - * @return void - */ - public function testNumCellsTotal() + public function testNumCellsTotal(): void { $obj = $this->obj; $this->assertEquals(0, $obj->numCellsTotal()); @@ -233,10 +195,7 @@ public function testNumCellsTotal() $this->assertEquals(5, $obj->numCellsTotal()); } - /** - * @return void - */ - public function testNumCellSpan() + public function testNumCellSpan(): void { $obj = $this->obj; $this->assertNull($obj->cellSpan()); @@ -253,10 +212,7 @@ public function testNumCellSpan() $this->assertNull($obj->cellSpan(5)); } - /** - * @return void - */ - public function testNumCellSpanBy12() + public function testNumCellSpanBy12(): void { $obj = $this->obj; $this->assertNull($obj->cellSpanBy12()); @@ -273,10 +229,7 @@ public function testNumCellSpanBy12() $this->assertNull($obj->cellSpanBy12(5)); } - /** - * @return void - */ - public function testCellStartsRow() + public function testCellStartsRow(): void { $obj = $this->obj; //$this->assertNull($obj->cellStartsRow()); @@ -293,10 +246,7 @@ public function testCellStartsRow() //$this->assertNull($obj->cellStartsRow(5)); } - /** - * @return void - */ - public function testCellEndsRow() + public function testCellEndsRow(): void { $obj = $this->obj; //$this->assertNull($obj->cellStartsRow()); @@ -313,19 +263,13 @@ public function testCellEndsRow() //$this->assertNull($obj->cellEndsRow(5)); } - /** - * @return void - */ - public function testStart() + public function testStart(): void { $obj = $this->obj; $this->assertEquals('', $obj->start()); } - /** - * @return void - */ - public function testEnd() + public function testEnd(): void { $obj = $this->obj; $this->assertEquals(0, $obj->position()); diff --git a/packages/admin/tests/Charcoal/Admin/Widget/ObjectFormWidgetTest.php b/packages/admin/tests/Charcoal/Admin/Widget/ObjectFormWidgetTest.php index 3d9f9a50a..215a6454b 100644 --- a/packages/admin/tests/Charcoal/Admin/Widget/ObjectFormWidgetTest.php +++ b/packages/admin/tests/Charcoal/Admin/Widget/ObjectFormWidgetTest.php @@ -19,9 +19,6 @@ class ObjectFormWidgetTest extends AbstractTestCase */ public $obj; - /** - * @return void - */ public function setUp(): void { $logger = new NullLogger(); @@ -30,10 +27,7 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testSetFormIdent() + public function testSetFormIdent(): void { $ret = $this->obj->setFormIdent('foobar'); $this->assertSame($ret, $this->obj); diff --git a/packages/admin/tests/Charcoal/Admin/Widget/PaginationWidgetTest.php b/packages/admin/tests/Charcoal/Admin/Widget/PaginationWidgetTest.php index 3f9185543..f9b84d499 100644 --- a/packages/admin/tests/Charcoal/Admin/Widget/PaginationWidgetTest.php +++ b/packages/admin/tests/Charcoal/Admin/Widget/PaginationWidgetTest.php @@ -21,9 +21,6 @@ class PaginationWidgetTest extends AbstractTestCase */ public $obj; - /** - * @return void - */ public function setUp(): void { $this->obj = new PaginationWidget([ @@ -31,10 +28,7 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testPageLogic() + public function testPageLogic(): void { $this->obj->setData([ 'page' => 3, @@ -67,10 +61,7 @@ public function testPageLogic() $this->assertEquals(1, $this->obj->pageNext()); } - /** - * @return void - */ - public function testSetNumTotal() + public function testSetNumTotal(): void { $ret = $this->obj->setNumTotal(42); $this->assertSame($ret, $this->obj); diff --git a/packages/admin/tests/Charcoal/Admin/Widget/SearchWidgetTest.php b/packages/admin/tests/Charcoal/Admin/Widget/SearchWidgetTest.php index f473a6251..9430596dc 100644 --- a/packages/admin/tests/Charcoal/Admin/Widget/SearchWidgetTest.php +++ b/packages/admin/tests/Charcoal/Admin/Widget/SearchWidgetTest.php @@ -14,9 +14,7 @@ */ class SearchWidgetTest extends AbstractTestCase { - /** - * @return void - */ + public $obj; public function setUp(): void { $logger = new NullLogger(); @@ -25,10 +23,7 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testConstructor() + public function testConstructor(): void { $this->assertInstanceOf(SearchWidget::class, $this->obj); } diff --git a/packages/admin/tests/Charcoal/Admin/Widget/SecondaryMenuWidgetTest.php b/packages/admin/tests/Charcoal/Admin/Widget/SecondaryMenuWidgetTest.php index 5c2793e4b..208f3a217 100644 --- a/packages/admin/tests/Charcoal/Admin/Widget/SecondaryMenuWidgetTest.php +++ b/packages/admin/tests/Charcoal/Admin/Widget/SecondaryMenuWidgetTest.php @@ -25,9 +25,6 @@ class SecondaryMenuWidgetTest extends AbstractTestCase */ public $obj; - /** - * @return void - */ public function setUp(): void { $container = new Container(); @@ -44,10 +41,7 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testConstructor() + public function testConstructor(): void { $this->assertInstanceOf(SecondaryMenuWidget::class, $this->obj); } diff --git a/packages/admin/tests/Charcoal/Admin/Widget/TableWidgetTest.php b/packages/admin/tests/Charcoal/Admin/Widget/TableWidgetTest.php index a69847d83..b7cf8895f 100644 --- a/packages/admin/tests/Charcoal/Admin/Widget/TableWidgetTest.php +++ b/packages/admin/tests/Charcoal/Admin/Widget/TableWidgetTest.php @@ -21,22 +21,16 @@ class TableWidgetTest extends AbstractTestCase { /** * Tested Class. - * - * @var TableWidget */ - private $obj; + private \Charcoal\Admin\Widget\TableWidget|array $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ public function setUp(): void { @@ -49,10 +43,7 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testSetSortable() + public function testSetSortable(): void { $ret = $this->obj->setSortable(true); $this->assertSame($ret, $this->obj); @@ -65,10 +56,7 @@ public function testSetSortable() $this->assertTrue($this->obj['sortable']); } - /** - * @return void - */ - public function testShowTableHeader() + public function testShowTableHeader(): void { $this->assertTrue($this->obj->showTableHeader()); $ret = $this->obj->setShowTableHeader(false); @@ -82,10 +70,7 @@ public function testShowTableHeader() $this->assertFalse($this->obj['show_table_header']); } - /** - * @return void - */ - public function testShowTableHead() + public function testShowTableHead(): void { $this->assertTrue($this->obj->showTableHead()); $ret = $this->obj->setShowTableHead(false); @@ -99,10 +84,7 @@ public function testShowTableHead() $this->assertFalse($this->obj['show_table_head']); } - /** - * @return void - */ - public function testShowTableFoot() + public function testShowTableFoot(): void { $this->assertFalse($this->obj->showTableFoot()); $ret = $this->obj->setShowTableFoot(false); @@ -118,19 +100,17 @@ public function testShowTableFoot() /** * Set up the service container. - * - * @return Container */ - protected function container() + protected function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerWidgetDependencies($container); $containerProvider->registerWidgetFactory($container); $containerProvider->registerPropertyDisplayFactory($container); - $container['view'] = $this->createMock('\Charcoal\View\ViewInterface'); + $container['view'] = $this->createMock(\Charcoal\View\ViewInterface::class); $this->container = $container; } diff --git a/packages/admin/tests/Charcoal/Admin/Widget/TextWidgetTest.php b/packages/admin/tests/Charcoal/Admin/Widget/TextWidgetTest.php index a9e49a6e0..a77e4d22d 100644 --- a/packages/admin/tests/Charcoal/Admin/Widget/TextWidgetTest.php +++ b/packages/admin/tests/Charcoal/Admin/Widget/TextWidgetTest.php @@ -18,9 +18,7 @@ */ class TextWidgetTest extends AbstractTestCase { - /** - * @return void - */ + public $obj; public function setUp(): void { $container = new Container(); @@ -33,10 +31,7 @@ public function setUp(): void ]); } - /** - * @return void - */ - public function testSetShowTitle() + public function testSetShowTitle(): void { $this->assertFalse($this->obj->showTitle()); $ret = $this->obj->setShowTitle(false); @@ -48,10 +43,7 @@ public function testSetShowTitle() $this->assertTrue($this->obj->showTitle()); } - /** - * @return void - */ - public function testSetShowSubtitle() + public function testSetShowSubtitle(): void { $this->assertFalse($this->obj->showSubtitle()); $ret = $this->obj->setShowSubtitle(false); @@ -63,10 +55,7 @@ public function testSetShowSubtitle() $this->assertTrue($this->obj->showSubtitle()); } - /** - * @return void - */ - public function testSetShowDescription() + public function testSetShowDescription(): void { $this->assertFalse($this->obj->showDescription()); $ret = $this->obj->setShowDescription(false); @@ -78,10 +67,7 @@ public function testSetShowDescription() $this->assertTrue($this->obj->showDescription()); } - /** - * @return void - */ - public function testSetShowNotes() + public function testSetShowNotes(): void { $this->assertFalse($this->obj->showNotes()); $ret = $this->obj->setShowNotes(false); @@ -93,40 +79,28 @@ public function testSetShowNotes() $this->assertTrue($this->obj->showNotes()); } - /** - * @return void - */ - public function testSetTitle() + public function testSetTitle(): void { $ret = $this->obj->setTitle('Fôö title'); $this->assertSame($ret, $this->obj); $this->assertEquals('Fôö title', (string)$this->obj->title()); } - /** - * @return void - */ - public function testSetSubtitle() + public function testSetSubtitle(): void { $ret = $this->obj->setSubtitle('Fôö subtitle'); $this->assertSame($ret, $this->obj); $this->assertEquals('Fôö subtitle', (string)$this->obj->subtitle()); } - /** - * @return void - */ - public function testSetDescription() + public function testSetDescription(): void { $ret = $this->obj->setDescription('Fôö description'); $this->assertSame($ret, $this->obj); $this->assertEquals('Fôö description', (string)$this->obj->description()); } - /** - * @return void - */ - public function testSetNotes() + public function testSetNotes(): void { $ret = $this->obj->setNotes('Fôö notes'); $this->assertSame($ret, $this->obj); diff --git a/packages/admin/tests/Charcoal/AssertionsTrait.php b/packages/admin/tests/Charcoal/AssertionsTrait.php index 21b2a53f6..0670b2627 100644 --- a/packages/admin/tests/Charcoal/AssertionsTrait.php +++ b/packages/admin/tests/Charcoal/AssertionsTrait.php @@ -15,9 +15,8 @@ trait AssertionsTrait * @param array $expected The expected haystack. * @param array $haystack The actual haystack. * @param string $message The error to report. - * @return void */ - public function assertArrayEquals(array $expected, array $haystack, $message = '') + public function assertArrayEquals(array $expected, array $haystack, $message = ''): void { $this->assertCount(count($expected), $haystack, $message); $this->assertEquals($expected, $haystack, $message); @@ -29,9 +28,8 @@ public function assertArrayEquals(array $expected, array $haystack, $message = ' * @param array $expected The expected haystack. * @param array $haystack The actual haystack. * @param string $message The error to report. - * @return void */ - public function assertArrayContains(array $expected, array $haystack, $message = '') + public function assertArrayContains(array $expected, array $haystack, $message = ''): void { foreach ($expected as $item) { $this->assertContains($item, $haystack, $message); @@ -44,9 +42,8 @@ public function assertArrayContains(array $expected, array $haystack, $message = * @param array $expected The expected haystack. * @param array $haystack The actual haystack. * @param string $message The error to report. - * @return void */ - public function assertArrayHasKeys(array $expected, array $haystack, $message = '') + public function assertArrayHasKeys(array $expected, array $haystack, $message = ''): void { foreach ($expected as $item) { $this->assertArrayHasKey($item, $haystack, $message); @@ -60,14 +57,13 @@ public function assertArrayHasKeys(array $expected, array $haystack, $message = * @param array $haystack The actual haystack. * @param boolean $strict Whether to check for object identity. * @param string $message The error to report. - * @return void */ public function assertArraySubsets( array $expected, array $haystack, $strict = false, $message = '' - ) { + ): void { foreach ($expected as $key => $val) { $this->assertArraySubset([ $key => $val ], $haystack, $strict, $message); } @@ -84,17 +80,16 @@ public function assertArraySubsets( * @param boolean $checkForObjectIdentity Unused. * @param string $message The error to report. * @throws InvalidArgumentException - * @return void */ public function assertArraySubset($subset, $array, $checkForObjectIdentity = false, $message = ''): void { - if (!(is_array($subset) || $subset instanceof ArrayAccess)) { + if (!is_array($subset) && !$subset instanceof ArrayAccess) { throw InvalidArgumentException::create( 1, 'array or ArrayAccess' ); } - if (!(is_array($array) || $array instanceof ArrayAccess)) { + if (!is_array($array) && !$array instanceof ArrayAccess) { throw InvalidArgumentException::create( 2, 'array or ArrayAccess' diff --git a/packages/admin/tests/Charcoal/ReflectionsTrait.php b/packages/admin/tests/Charcoal/ReflectionsTrait.php index 3a63ff2b1..047d2c172 100644 --- a/packages/admin/tests/Charcoal/ReflectionsTrait.php +++ b/packages/admin/tests/Charcoal/ReflectionsTrait.php @@ -18,13 +18,10 @@ trait ReflectionsTrait * * @param mixed $class The class name or object that contains the method. * @param string $name The method name to reflect. - * @return ReflectionMethod */ - public function getMethod($class, $name) + public function getMethod($class, $name): \ReflectionMethod { - $reflected = new ReflectionMethod($class, $name); - $reflected->setAccessible(true); - return $reflected; + return new ReflectionMethod($class, $name); } /** @@ -38,7 +35,7 @@ public function getMethod($class, $name) public function callMethod($object, $name, array $args = []) { $method = $this->getMethod($object, $name); - if (empty($args)) { + if ($args === []) { return $method->invoke($object); } else { return $method->invokeArgs($object, $args); @@ -65,13 +62,10 @@ public function callMethodWith($object, $name, ...$args) * * @param mixed $class The class name or object that contains the property. * @param string $name The property name to reflect. - * @return ReflectionProperty */ - public function getProperty($class, $name) + public function getProperty($class, $name): \ReflectionProperty { - $reflected = new ReflectionProperty($class, $name); - $reflected->setAccessible(true); - return $reflected; + return new ReflectionProperty($class, $name); } /** @@ -92,9 +86,8 @@ public function getPropertyValue($object, $name) * @param mixed $object The object to access. * @param string $name The property name to affect. * @param mixed $value The new value. - * @return void */ - public function setPropertyValue($object, $name, $value) + public function setPropertyValue($object, $name, $value): void { $this->getProperty($object, $name)->setValue($object, $value); } diff --git a/packages/app/composer.json b/packages/app/composer.json index 2e762c53e..87149de9e 100644 --- a/packages/app/composer.json +++ b/packages/app/composer.json @@ -1,8 +1,12 @@ { - "type": "library", "name": "charcoal/app", "description": "Charcoal application, based on Slim 3", - "keywords": ["charcoal", "framework", "slim", "cms"], + "keywords": [ + "charcoal", + "framework", + "slim", + "cms" + ], "homepage": "https://charcoal.locomotive.ca", "license": "MIT", "authors": [ @@ -15,24 +19,17 @@ "homepage": "https://locomotive.ca" } ], - "config": { - "sort-packages": true - }, - "extra": { - "branch-alias": { - "dev-main": "5.x-dev" - } - }, + "type": "library", "require": { - "php": "^7.4 || ^8.0", + "php": "^8.3", "ext-pdo": "*", - "league/climate": "^3.2", - "league/flysystem": "^1.0", "charcoal/cache": "^5.1", "charcoal/config": "^5.1", "charcoal/factory": "^5.1", "charcoal/translator": "^5.1", "charcoal/view": "^5.1", + "league/climate": "^3.2", + "league/flysystem": "^1.0", "monolog/monolog": "^1.17", "psr/http-message": "^1.0", "psr/log": "^1.0", @@ -46,8 +43,8 @@ "mockery/mockery": "^1.0", "mustache/mustache": "^2.11", "php-coveralls/php-coveralls": "^2.2", - "phpstan/phpstan": "^1.6", - "phpunit/phpunit": "^9.5", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^12.5", "squizlabs/php_codesniffer": "^3.5", "tedivm/stash": "~0.16" }, @@ -61,8 +58,10 @@ "Charcoal\\Tests\\": "tests/Charcoal" } }, - "replace": { - "locomotivemtl/charcoal-app": "*" + "extra": { + "branch-alias": { + "dev-main": "5.x-dev" + } }, "bin": [ "bin/charcoal" @@ -83,6 +82,12 @@ "phpstan": "php vendor/bin/phpstan analyze -l1 src/", "phpunit": "php vendor/bin/phpunit --coverage-text" }, + "config": { + "sort-packages": true + }, + "replace": { + "locomotivemtl/charcoal-app": "*" + }, "minimum-stability": "dev", "prefer-stable": true } diff --git a/packages/app/src/Charcoal/App/Action/AbstractAction.php b/packages/app/src/Charcoal/App/Action/AbstractAction.php index 842eb7325..6f5d1f52d 100644 --- a/packages/app/src/Charcoal/App/Action/AbstractAction.php +++ b/packages/app/src/Charcoal/App/Action/AbstractAction.php @@ -44,25 +44,13 @@ abstract class AbstractAction extends AbstractEntity implements public const MODE_EVENT_STREAM = 'event-stream'; public const DEFAULT_MODE = self::MODE_JSON; - /** - * @var string $mode - */ - private $mode = self::DEFAULT_MODE; + private string $mode = self::DEFAULT_MODE; - /** - * @var boolean $success - */ - private $success = false; + private bool $success = false; - /** - * @var string|null $successUrl - */ - private $successUrl; + private ?string $successUrl = null; - /** - * @var string|null $failureUrl - */ - private $failureUrl; + private ?string $failureUrl = null; /** * @param array|\ArrayAccess $data The dependencies (app and logger). @@ -116,9 +104,7 @@ final public function __invoke(RequestInterface $request, ResponseInterface $res break; case self::MODE_EVENT_STREAM: - $output = new CallbackStream(function () { - return $this->results(); - }); + $output = new CallbackStream(fn(): mixed => $this->results()); $response = $response ->withHeader('Content-Type', 'text/event-stream') @@ -162,7 +148,7 @@ public function mode() */ public function setSuccess($success) { - $this->success = !!$success; + $this->success = (bool) $success; return $this; } @@ -248,13 +234,7 @@ public function failureUrl() */ public function redirectUrl() { - if ($this->success() === true) { - $url = $this->successUrl(); - } else { - $url = $this->failureUrl(); - } - - return $url; + return $this->success() === true ? $this->successUrl() : $this->failureUrl(); } /** diff --git a/packages/app/src/Charcoal/App/Action/ActionInterface.php b/packages/app/src/Charcoal/App/Action/ActionInterface.php index ec5251915..909e0ac36 100644 --- a/packages/app/src/Charcoal/App/Action/ActionInterface.php +++ b/packages/app/src/Charcoal/App/Action/ActionInterface.php @@ -1,5 +1,7 @@ setup(); @@ -116,10 +117,8 @@ public function run($silent = false) /** * Registers the default services and features that Charcoal needs to work. - * - * @return void */ - private function setup() + private function setup(): void { $config = $this->config(); date_default_timezone_set($config['timezone']); @@ -150,9 +149,9 @@ private function setup() */ private function routeManager() { - if (!isset($this->routeManager)) { + if ($this->routeManager === null) { $config = $this->config(); - $routesConfig = (isset($config['routes']) ? $config['routes'] : [] ); + $routesConfig = ($config['routes'] ?? [] ); $this->routeManager = new RouteManager([ 'config' => $routesConfig, @@ -163,10 +162,7 @@ private function routeManager() return $this->routeManager; } - /** - * @return void - */ - private function setupModules() + private function setupModules(): void { $container = $this->getContainer(); $modules = $container['config']['modules']; @@ -180,10 +176,8 @@ private function setupModules() * Setup the application's "global" routables. * * Routables can only be defined globally (app-level) for now. - * - * @return void */ - private function setupRoutables() + private function setupRoutables(): void { $app = $this; @@ -198,7 +192,7 @@ function ( $config = $app->config(); $routables = $config['routables']; - if (is_array($routables) && !empty($routables)) { + if (is_array($routables) && $routables !== []) { $routeFactory = $this['route/factory']; foreach ($routables as $routableType => $routableOptions) { $route = $routeFactory->create($routableType, [ @@ -222,9 +216,8 @@ function ( /** * @throws RuntimeException If the middleware was not set properly on the container. - * @return void */ - private function setupMiddlewares() + private function setupMiddlewares(): void { $container = $this->getContainer(); $middlewaresConfig = $container['config']['middlewares']; @@ -258,29 +251,26 @@ private function setupMiddlewares() /** * @throws LogicException If trying to clone an instance of a singleton. - * @return void */ final public function __clone() { throw new LogicException( sprintf( 'Cloning "%s" is not allowed.', - get_called_class() + static::class ) ); } /** * @throws LogicException If trying to unserialize an instance of a singleton. - * @return void */ - final public function __wakeup() + final public function __unserialize(array $data): void { - throw new LogicException( - sprintf( - 'Unserializing "%s" is not allowed.', - get_called_class() - ) - ); + foreach ($data as $property => $value) { + if (property_exists($this, $property)) { + $this->{$property} = $value; + } + } } } diff --git a/packages/app/src/Charcoal/App/AppAwareInterface.php b/packages/app/src/Charcoal/App/AppAwareInterface.php index 17fa8770f..58f89a4ae 100644 --- a/packages/app/src/Charcoal/App/AppAwareInterface.php +++ b/packages/app/src/Charcoal/App/AppAwareInterface.php @@ -1,5 +1,7 @@ '', @@ -188,11 +150,10 @@ public function defaults() /** * @param array $values Array of values to resolve. - * @return array */ public function resolveValues(array $values): array { - return array_map([$this, 'resolveValue'], $values); + return array_map($this->resolveValue(...), $values); } /** @@ -213,7 +174,7 @@ public function resolveValue($value) ]; if (is_string($value)) { - return preg_replace_callback('/%%|%([^%\s]+)%/', function ($match) use ($tags, $value) { + return preg_replace_callback('/%%|%([^%\s]+)%/', function (array $match) use ($tags, $value): string|float|int { // skip escaped %% if (!isset($match[1])) { return '%%'; @@ -224,7 +185,7 @@ public function resolveValue($value) $resolved = ($tags[$tag] ?? null); if (!is_string($resolved) && !is_numeric($resolved)) { - $resolvedType = (is_object($resolved) ? get_class($resolved) : gettype($resolved)); + $resolvedType = (get_debug_type($resolved)); throw new UnexpectedValueException(sprintf( 'Invalid config parameter "%s" inside string value "%s";' . @@ -251,6 +212,7 @@ public function resolveValue($value) * @param string $path The file to load and add. * @return self */ + #[\Override] public function addFile($path) { $path = $this->resolveValue($path); @@ -265,9 +227,8 @@ public function addFile($path) * * @param string $path The absolute path to the application's root directory. * @throws InvalidArgumentException If the argument is not a string. - * @return self */ - public function setBasePath($path) + public function setBasePath($path): static { if ($path === null) { throw new InvalidArgumentException( @@ -290,7 +251,7 @@ public function setBasePath($path) * * @return string|null The absolute path to the application's root directory. */ - public function basePath() + public function basePath(): ?string { return $this->basePath; } @@ -300,9 +261,8 @@ public function basePath() * * @param string $path The path to the application's public directory. * @throws InvalidArgumentException If the argument is not a string. - * @return self */ - public function setPublicPath($path) + public function setPublicPath($path): static { if ($path === null) { $this->publicPath = null; @@ -324,7 +284,7 @@ public function setPublicPath($path) * * @return string The absolute path to the application's public directory. */ - public function publicPath() + public function publicPath(): string { if ($this->publicPath === null) { $this->publicPath = $this->basePath() . DIRECTORY_SEPARATOR . 'www'; @@ -338,9 +298,8 @@ public function publicPath() * * @param string $path The path to the application's cache directory. * @throws InvalidArgumentException If the argument is not a string. - * @return self */ - public function setCachePath($path) + public function setCachePath($path): static { if ($path === null) { $this->cachePath = null; @@ -362,7 +321,7 @@ public function setCachePath($path) * * @return string The absolute path to the application's cache directory. */ - public function cachePath() + public function cachePath(): string { if ($this->cachePath === null) { $this->cachePath = $this->basePath() . DIRECTORY_SEPARATOR . 'var' . DIRECTORY_SEPARATOR . 'cache'; @@ -376,9 +335,8 @@ public function cachePath() * * @param string $path The path to the application's logs directory. * @throws InvalidArgumentException If the argument is not a string. - * @return self */ - public function setLogsPath($path) + public function setLogsPath($path): static { if ($path === null) { $this->logsPath = null; @@ -400,7 +358,7 @@ public function setLogsPath($path) * * @return string The absolute path to the application's logs directory. */ - public function logsPath() + public function logsPath(): string { if ($this->logsPath === null) { $this->logsPath = $this->basePath() . DIRECTORY_SEPARATOR . 'var' . DIRECTORY_SEPARATOR . 'logs'; @@ -413,15 +371,10 @@ public function logsPath() * Set the application's fully qualified base URL to the public web directory. * * @param UriInterface|string $uri The base URI to the application's web directory. - * @return self */ - public function setBaseUrl($uri) + public function setBaseUrl($uri): static { - if (is_string($uri)) { - $this->baseUrl = Uri::createFromString($uri); - } else { - $this->baseUrl = $uri; - } + $this->baseUrl = is_string($uri) ? Uri::createFromString($uri) : $uri; return $this; } @@ -440,9 +393,8 @@ public function baseUrl() * * @param string $timezone The timezone string. * @throws InvalidArgumentException If the argument is not a string. - * @return self */ - public function setTimezone($timezone) + public function setTimezone($timezone): static { if (!is_string($timezone)) { throw new InvalidArgumentException( @@ -458,12 +410,10 @@ public function setTimezone($timezone) * Retrieve the application's default timezone. * * Will be used by the PHP date and date-time functions. - * - * @return string */ - public function timezone() + public function timezone(): string { - if (isset($this->timezone)) { + if ($this->timezone !== null) { return $this->timezone; } else { return 'UTC'; @@ -475,9 +425,8 @@ public function timezone() * * @param string|null $projectName The project name. * @throws InvalidArgumentException If the project argument is not a string (or null). - * @return self */ - public function setProjectName($projectName) + public function setProjectName($projectName): static { if ($projectName === null) { $this->projectName = null; @@ -510,20 +459,16 @@ public function projectName() /** * @param boolean $devMode The "dev mode" flag. - * @return self */ - public function setDevMode($devMode) + public function setDevMode($devMode): static { - $this->devMode = !!$devMode; + $this->devMode = (bool) $devMode; return $this; } - /** - * @return boolean - */ - public function devMode() + public function devMode(): bool { - return !!$this->devMode; + return $this->devMode; } /** @@ -531,9 +476,8 @@ public function devMode() * * @param array $view The global configset for the application's view service. * @throws InvalidArgumentException If the argument is not a configset. - * @return self */ - public function setView(array $view) + public function setView(array $view): static { $this->view = $view; return $this; @@ -553,18 +497,14 @@ public function view() * Parse the application's API configuration. * * @param array $apis The API configuration structure to set. - * @return self */ - public function setApis(array $apis) + public function setApis(array $apis): static { $this->apis = $apis; return $this; } - /** - * @return array - */ - public function apis() + public function apis(): array { return $this->apis; } @@ -574,42 +514,32 @@ public function apis() * * @see \Charcoal\Admin\Config::setRoutes() For a similar implementation. * @param array $routes The route configuration structure to set. - * @return self */ - public function setRoutes(array $routes) + public function setRoutes(array $routes): static { $this->routes = $routes; return $this; } - /** - * @return array - */ - public function routes() + public function routes(): array { return $this->routes; } /** * @param array|boolean $routables The routable configuration structure to set or FALSE to disable dynamic routing. - * @return self */ - public function setRoutables($routables) + public function setRoutables($routables): static { - if ($routables !== false) { - if (!is_array($routables) || empty($routables)) { - $routables = []; - } + if ($routables !== false && (!is_array($routables) || $routables === [])) { + $routables = []; } $this->routables = $routables; return $this; } - /** - * @return array|boolean - */ - public function routables() + public function routables(): bool|array { return $this->routables; } @@ -618,18 +548,14 @@ public function routables() * Parse the application's HTTP middleware. * * @param array $middlewares The middleware configuration structure to set. - * @return self */ - public function setMiddlewares(array $middlewares) + public function setMiddlewares(array $middlewares): static { $this->middlewares = $middlewares; return $this; } - /** - * @return array - */ - public function middlewares() + public function middlewares(): array { return $this->middlewares; } @@ -645,18 +571,14 @@ public function middlewares() * - "phpErrorHandler" * * @param array $handlers The handlers configuration structure to set. - * @return self */ - public function setHandlers(array $handlers) + public function setHandlers(array $handlers): static { $this->handlers = $handlers; return $this; } - /** - * @return array - */ - public function handlers() + public function handlers(): array { return $this->handlers; } @@ -665,18 +587,14 @@ public function handlers() * Set the configuration modules. * * @param array $modules The module configuration structure to set. - * @return self */ - public function setModules(array $modules) + public function setModules(array $modules): static { $this->modules = $modules; return $this; } - /** - * @return array - */ - public function modules() + public function modules(): array { return $this->modules; } @@ -686,9 +604,8 @@ public function modules() * * @param array $cache The global config for the application's cache service. * @throws InvalidArgumentException If the argument is not a configset. - * @return self */ - public function setCache(array $cache) + public function setCache(array $cache): static { $this->cache = $cache; return $this; @@ -699,7 +616,7 @@ public function setCache(array $cache) * * @return array */ - public function cache() + public function cache(): ?array { return $this->cache; } @@ -709,9 +626,8 @@ public function cache() * * @param array $logger The global config for the application's logger service. * @throws InvalidArgumentException If the argument is not a configset. - * @return self */ - public function setLogger(array $logger) + public function setLogger(array $logger): static { $this->logger = $logger; return $this; @@ -722,16 +638,15 @@ public function setLogger(array $logger) * * @return array */ - public function logger() + public function logger(): ?array { return $this->logger; } /** * @param array $databases The avaiable databases config. - * @return self */ - public function setDatabases(array $databases) + public function setDatabases(array $databases): static { $this->databases = $databases; return $this; @@ -739,9 +654,8 @@ public function setDatabases(array $databases) /** * @throws Exception If trying to access this method and no databases were set. - * @return array */ - public function databases() + public function databases(): array { if ($this->databases === null) { throw new Exception( @@ -776,9 +690,8 @@ public function databaseConfig($ident) /** * @param string $defaultDatabase The default database ident. * @throws InvalidArgumentException If the argument is not a string. - * @return self */ - public function setDefaultDatabase($defaultDatabase) + public function setDefaultDatabase($defaultDatabase): static { if (!is_string($defaultDatabase)) { throw new InvalidArgumentException( @@ -793,9 +706,8 @@ public function setDefaultDatabase($defaultDatabase) * @param string $ident The database ident. * @param array $config The database options. * @throws InvalidArgumentException If the arguments are invalid. - * @return self */ - public function addDatabase($ident, array $config) + public function addDatabase($ident, array $config): static { if (!is_string($ident)) { throw new InvalidArgumentException( @@ -812,9 +724,8 @@ public function addDatabase($ident, array $config) /** * @throws Exception If trying to access this method before a setter. - * @return mixed */ - public function defaultDatabase() + public function defaultDatabase(): string { if ($this->defaultDatabase === null) { throw new Exception( @@ -829,9 +740,8 @@ public function defaultDatabase() * * @param array $filesystem The global config for the application's file system. * @throws InvalidArgumentException If the argument is not a configset. - * @return self */ - public function setFilesystem(array $filesystem) + public function setFilesystem(array $filesystem): static { $this->filesystem = $filesystem; return $this; @@ -842,7 +752,7 @@ public function setFilesystem(array $filesystem) * * @return array */ - public function filesystem() + public function filesystem(): ?array { return $this->filesystem; } diff --git a/packages/app/src/Charcoal/App/AppContainer.php b/packages/app/src/Charcoal/App/AppContainer.php index 7b5b3d635..fa494869e 100644 --- a/packages/app/src/Charcoal/App/AppContainer.php +++ b/packages/app/src/Charcoal/App/AppContainer.php @@ -28,7 +28,7 @@ public function __construct(array $values = []) parent::__construct($values); // Ensure "config" is set - $this['config'] = (isset($values['config']) ? $values['config'] : new AppConfig()); + $this['config'] = ($values['config'] ?? new AppConfig()); $this->register(new AppServiceProvider()); @@ -36,30 +36,22 @@ public function __construct(array $values = []) $this->registerConfigProviders(); } - /** - * @return void - */ - private function registerProviderFactory() + private function registerProviderFactory(): void { /** * @return Factory */ if (!isset($this['provider/factory'])) { - $this['provider/factory'] = function () { - return new Factory([ - 'base_class' => ServiceProviderInterface::class, - 'resolver_options' => [ - 'suffix' => 'ServiceProvider' - ] - ]); - }; + $this['provider/factory'] = (fn(): \Charcoal\Factory\GenericFactory => new Factory([ + 'base_class' => ServiceProviderInterface::class, + 'resolver_options' => [ + 'suffix' => 'ServiceProvider' + ] + ])); } } - /** - * @return void - */ - private function registerConfigProviders() + private function registerConfigProviders(): void { if (empty($this['config']['service_providers'])) { return; diff --git a/packages/app/src/Charcoal/App/CallableResolverAwareTrait.php b/packages/app/src/Charcoal/App/CallableResolverAwareTrait.php index de76534ca..44fa5b724 100644 --- a/packages/app/src/Charcoal/App/CallableResolverAwareTrait.php +++ b/packages/app/src/Charcoal/App/CallableResolverAwareTrait.php @@ -72,7 +72,7 @@ protected function resolveCallable($callable, $context = null) { if (!isset($this->callableResolver)) { throw new RuntimeException( - sprintf('Callable Resolver is not defined for "%s"', get_class($this)) + sprintf('Callable Resolver is not defined for "%s"', $this::class) ); } diff --git a/packages/app/src/Charcoal/App/Config/DatabaseConfig.php b/packages/app/src/Charcoal/App/Config/DatabaseConfig.php index c5533aa49..152791535 100644 --- a/packages/app/src/Charcoal/App/Config/DatabaseConfig.php +++ b/packages/app/src/Charcoal/App/Config/DatabaseConfig.php @@ -1,5 +1,7 @@ 'mysql', @@ -61,9 +43,8 @@ public function defaults() * * @param string $type The database type. * @throws InvalidArgumentException If parameter is not a string. - * @return self */ - public function setType($type) + public function setType($type): static { if (!is_string($type)) { throw new InvalidArgumentException( @@ -79,7 +60,7 @@ public function setType($type) * * @return string */ - public function type() + public function type(): ?string { return $this->type; } @@ -89,9 +70,8 @@ public function type() * * @param string $hostname The database server hostname. * @throws InvalidArgumentException If hostname is not a string. - * @return self */ - public function setHostname($hostname) + public function setHostname($hostname): static { if (!is_string($hostname)) { throw new InvalidArgumentException( @@ -107,7 +87,7 @@ public function setHostname($hostname) * * @return string */ - public function hostname() + public function hostname(): ?string { return $this->hostname; } @@ -117,9 +97,8 @@ public function hostname() * * @param string $username The username. * @throws InvalidArgumentException If username is not a string. - * @return self */ - public function setUsername($username) + public function setUsername($username): static { if (!is_string($username)) { throw new InvalidArgumentException( @@ -135,7 +114,7 @@ public function setUsername($username) * * @return string */ - public function username() + public function username(): ?string { return $this->username; } @@ -145,9 +124,8 @@ public function username() * * @param string $password The password. * @throws InvalidArgumentException If password is not a string. - * @return self */ - public function setPassword($password) + public function setPassword($password): static { if (!is_string($password)) { throw new InvalidArgumentException( @@ -163,7 +141,7 @@ public function setPassword($password) * * @return string */ - public function password() + public function password(): ?string { return $this->password; } @@ -173,9 +151,8 @@ public function password() * * @param string $database The database name. * @throws InvalidArgumentException If database is not a string. - * @return self */ - public function setDatabase($database) + public function setDatabase($database): static { if (!is_string($database)) { throw new InvalidArgumentException( @@ -191,7 +168,7 @@ public function setDatabase($database) * * @return string */ - public function database() + public function database(): ?string { return $this->database; } @@ -200,11 +177,10 @@ public function database() * Set whether to disable UTF-8 compatibility or not. * * @param boolean $disableUtf8 The disable flag. - * @return self */ - public function setDisableUtf8($disableUtf8) + public function setDisableUtf8($disableUtf8): static { - $this->disableUtf8 = !!$disableUtf8; + $this->disableUtf8 = (bool) $disableUtf8; return $this; } @@ -213,7 +189,7 @@ public function setDisableUtf8($disableUtf8) * * @return boolean */ - public function disableUtf8() + public function disableUtf8(): ?bool { return $this->disableUtf8; } diff --git a/packages/app/src/Charcoal/App/Config/FilesystemConfig.php b/packages/app/src/Charcoal/App/Config/FilesystemConfig.php index 57a96f37a..e8eace876 100644 --- a/packages/app/src/Charcoal/App/Config/FilesystemConfig.php +++ b/packages/app/src/Charcoal/App/Config/FilesystemConfig.php @@ -20,22 +20,19 @@ class FilesystemConfig extends AbstractConfig */ public $defaultConnection; - /** - * @return array - */ - public function defaultConnections() + public function defaultConnections(): array { return [ 'public' => [ 'public' => true, 'type' => 'local', - 'path' => '%app.public_path%', + 'path' => './', 'label' => 'Public', ], 'private' => [ 'public' => false, 'type' => 'local', - 'path' => '%app.base_path%', + 'path' => '../', 'label' => 'Private', ], ]; @@ -43,10 +40,8 @@ public function defaultConnections() /** * Ensure connections always return the default connections. - * - * @return array */ - public function connections() + public function connections(): array { return array_merge($this->defaultConnections(), $this->connections); } diff --git a/packages/app/src/Charcoal/App/Config/LoggerConfig.php b/packages/app/src/Charcoal/App/Config/LoggerConfig.php index 800314189..5d40f6971 100644 --- a/packages/app/src/Charcoal/App/Config/LoggerConfig.php +++ b/packages/app/src/Charcoal/App/Config/LoggerConfig.php @@ -15,42 +15,33 @@ class LoggerConfig extends AbstractConfig /** * Whether to enable or disable the logger service. - * - * @var boolean */ - private $active; + private ?bool $active = null; /** * Record handler(s) to use. * * Whenever you add a record to the logger, it traverses the handler stack. - * - * @var array */ - private $handlers; + private ?array $handlers = null; /** * Record processor(s) to use. * * For customizing records added to the logger. - * - * @var array */ - private $processors; + private ?array $processors = null; /** * Channel name. - * - * @var string */ - private $channel = self::DEFAULT_CHANNEL; + private string $channel = self::DEFAULT_CHANNEL; /** * Retrieve the default values. - * - * @return array */ - public function defaults() + #[\Override] + public function defaults(): array { return [ 'active' => true, @@ -88,9 +79,9 @@ public function defaults() * TRUE to enable, FALSE to disable. * @return LoggerConfig Chainable */ - public function setActive($active) + public function setActive($active): static { - $this->active = !!$active; + $this->active = (bool) $active; return $this; } @@ -99,7 +90,7 @@ public function setActive($active) * * @return boolean TRUE if enabled, FALSE if disabled. */ - public function active() + public function active(): ?bool { return $this->active; } @@ -108,9 +99,8 @@ public function active() * Set the record handler(s) to use. * * @param array $handlers One or more (Monolog) record handlers; used as a stack. - * @return self */ - public function setHandlers(array $handlers) + public function setHandlers(array $handlers): static { $this->handlers = []; $this->addHandlers($handlers); @@ -121,9 +111,8 @@ public function setHandlers(array $handlers) * Add record handler(s) to use. * * @param string[] $handlers One or more (Monolog) handlers to stack. - * @return self */ - public function addHandlers(array $handlers) + public function addHandlers(array $handlers): static { foreach ($handlers as $key => $handler) { $this->addHandler($handler, $key); @@ -137,9 +126,8 @@ public function addHandlers(array $handlers) * @param array $handler The record handler structure. * @param string|null $key The handler's key. * @throws InvalidArgumentException If the handler is invalid. - * @return self */ - public function addHandler(array $handler, $key = null) + public function addHandler(array $handler, $key = null): static { if (!isset($handler['type'])) { throw new InvalidArgumentException( @@ -161,7 +149,7 @@ public function addHandler(array $handler, $key = null) * * @return array */ - public function handlers() + public function handlers(): ?array { return $this->handlers; } @@ -170,9 +158,8 @@ public function handlers() * Set the record processor(s) to use. * * @param array $processors One or more (Monolog) record processors; used as a stack. - * @return self */ - public function setProcessors(array $processors) + public function setProcessors(array $processors): static { $this->processors = []; $this->addProcessors($processors); @@ -183,9 +170,8 @@ public function setProcessors(array $processors) * Add record processor(s) to use. * * @param string[] $processors One or more (Monolog) processors to stack. - * @return self */ - public function addProcessors(array $processors) + public function addProcessors(array $processors): static { foreach ($processors as $key => $processor) { $this->addProcessor($processor, $key); @@ -199,9 +185,8 @@ public function addProcessors(array $processors) * @param array $processor The record processor structure. * @param string|null $key The processor's key. * @throws InvalidArgumentException If the processor is invalid. - * @return self */ - public function addProcessor(array $processor, $key = null) + public function addProcessor(array $processor, $key = null): static { if (!isset($processor['type'])) { throw new InvalidArgumentException( @@ -223,7 +208,7 @@ public function addProcessor(array $processor, $key = null) * * @return array */ - public function processors() + public function processors(): ?array { return $this->processors; } @@ -233,9 +218,8 @@ public function processors() * * @param string $name The channe name (namespace). * @throws InvalidArgumentException If the channel name is not a string. - * @return self */ - public function setChannel($name) + public function setChannel($name): static { if (!is_string($name)) { throw new InvalidArgumentException( @@ -249,10 +233,8 @@ public function setChannel($name) /** * Retrieve the cache namespace. - * - * @return string */ - public function channel() + public function channel(): string { return $this->channel; } diff --git a/packages/app/src/Charcoal/App/DebugAwareTrait.php b/packages/app/src/Charcoal/App/DebugAwareTrait.php index 44914d556..7798b20bf 100644 --- a/packages/app/src/Charcoal/App/DebugAwareTrait.php +++ b/packages/app/src/Charcoal/App/DebugAwareTrait.php @@ -22,7 +22,7 @@ trait DebugAwareTrait */ protected function setDebug($debug) { - $this->debug = !!$debug; + $this->debug = (bool) $debug; } /** diff --git a/packages/app/src/Charcoal/App/Handler/AbstractError.php b/packages/app/src/Charcoal/App/Handler/AbstractError.php index 654257bf1..1ca62534e 100644 --- a/packages/app/src/Charcoal/App/Handler/AbstractError.php +++ b/packages/app/src/Charcoal/App/Handler/AbstractError.php @@ -21,10 +21,8 @@ abstract class AbstractError extends AbstractHandler { /** * Whether to output the error's details. - * - * @var boolean $displayErrorDetails */ - private $displayErrorDetails; + private ?bool $displayErrorDetails = null; /** * The caught throwable. @@ -50,7 +48,7 @@ public function displayErrorDetails() */ public function hasThrown() { - return !!$this->thrown; + return (bool) $this->thrown; } /** @@ -86,6 +84,7 @@ public function renderHtmlErrorDetails() * * @return integer */ + #[\Override] public function getCode() { return 500; @@ -125,6 +124,7 @@ public function getMessage() * @param Container $container A service locator. * @return self */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -143,7 +143,7 @@ protected function setDependencies(Container $container) */ protected function setDisplayErrorDetails($state) { - $this->displayErrorDetails = !!$state; + $this->displayErrorDetails = (bool) $state; return $this; } @@ -171,7 +171,7 @@ protected function setThrown($throwable) */ protected function renderThrowableAsText($throwable) { - $text = sprintf('Type: %s' . PHP_EOL, get_class($throwable)); + $text = sprintf('Type: %s' . PHP_EOL, $throwable::class); $code = $throwable->getCode(); if (!empty($code)) { @@ -285,7 +285,7 @@ protected function renderJsonMessage($throwable) do { $json['error'][] = [ - 'type' => get_class($throwable), + 'type' => $throwable::class, 'code' => $throwable->getCode(), 'message' => $throwable->getMessage(), 'file' => $throwable->getFile(), @@ -310,7 +310,7 @@ protected function renderXmlMessage($throwable) if ($this->displayErrorDetails()) { do { $xml .= " \n"; - $xml .= ' ' . get_class($throwable) . "\n"; + $xml .= ' ' . $throwable::class . "\n"; $xml .= ' ' . $throwable->getCode() . "\n"; $xml .= ' ' . $this->createCdataSection($throwable->getMessage()) . "\n"; $xml .= ' ' . $throwable->getFile() . "\n"; @@ -319,9 +319,8 @@ protected function renderXmlMessage($throwable) $xml .= " \n"; } while ($throwable = $throwable->getPrevious()); } - $xml .= ''; - return $xml; + return $xml . ''; } /** @@ -349,7 +348,7 @@ protected function renderHtmlError($throwable) $line = $throwable->getLine(); $trace = $throwable->getTraceAsString(); - $html = sprintf('
Type: %s
', get_class($throwable)); + $html = sprintf('
Type: %s
', $throwable::class); if ($code) { $html .= sprintf('
Code: %s
', $code); @@ -381,6 +380,7 @@ protected function renderHtmlError($throwable) * @param array|\ArrayAccess $data Raw template data. * @return array|\ArrayAccess Expanded and processed template data. */ + #[\Override] protected function parseTemplateData($data = []) { $error = $this->getThrown(); diff --git a/packages/app/src/Charcoal/App/Handler/AbstractHandler.php b/packages/app/src/Charcoal/App/Handler/AbstractHandler.php index ddc0a31c9..1f7cc831e 100644 --- a/packages/app/src/Charcoal/App/Handler/AbstractHandler.php +++ b/packages/app/src/Charcoal/App/Handler/AbstractHandler.php @@ -98,8 +98,6 @@ public function __construct(Container $container, $config = null) /** * String representation of the handler. - * - * @return string */ public function __toString(): string { @@ -183,7 +181,7 @@ final protected function templateFactory() if ($this->templateFactory === null) { throw new RuntimeException(sprintf( 'Template Factory is not defined for [%s]', - get_class($this) + static::class )); } @@ -216,7 +214,7 @@ final protected function httpRequest() if ($this->httpRequest === null) { throw new RuntimeException(sprintf( 'HTTP Request is not defined for "%s"', - get_class($this) + static::class )); } @@ -276,9 +274,8 @@ protected function respondWith(ResponseInterface $response, $contentType, $outpu * Set an template factory. * * @param FactoryInterface $factory The factory to create templates. - * @return void */ - private function setTemplateFactory(FactoryInterface $factory) + private function setTemplateFactory(FactoryInterface $factory): void { $this->templateFactory = $factory; } @@ -287,9 +284,8 @@ private function setTemplateFactory(FactoryInterface $factory) * Set container for use with the template controller * * @param Container $container A dependencies container instance. - * @return void */ - private function setContainer(Container $container) + private function setContainer(Container $container): void { $this->container = $container; } diff --git a/packages/app/src/Charcoal/App/Handler/Error.php b/packages/app/src/Charcoal/App/Handler/Error.php index 0f439da48..7bb3df614 100644 --- a/packages/app/src/Charcoal/App/Handler/Error.php +++ b/packages/app/src/Charcoal/App/Handler/Error.php @@ -37,30 +37,16 @@ public function __invoke( $this->setThrown($error); $contentType = $this->determineContentType($request); - switch ($contentType) { - case 'application/json': - $output = $this->renderJsonOutput(); - break; - - case 'text/xml': - case 'application/xml': - $output = $this->renderXmlOutput(); - break; - - case 'text/html': - $output = $this->renderHtmlOutput(); - break; - - case 'text/plain': - $output = $this->renderPlainOutput(); - break; - - default: - throw new UnexpectedValueException(sprintf( - 'Cannot render unknown content type: %s', - $contentType - )); - } + $output = match ($contentType) { + 'application/json' => $this->renderJsonOutput(), + 'text/xml', 'application/xml' => $this->renderXmlOutput(), + 'text/html' => $this->renderHtmlOutput(), + 'text/plain' => $this->renderPlainOutput(), + default => throw new UnexpectedValueException(sprintf( + 'Cannot render unknown content type: %s', + $contentType + )), + }; $this->writeToErrorLog($error); @@ -73,10 +59,8 @@ public function __invoke( /** * Render Text Error - * - * @return string */ - protected function renderPlainOutput() + protected function renderPlainOutput(): string { $message = $this->renderTextMessage($this->getThrown()); @@ -85,10 +69,8 @@ protected function renderPlainOutput() /** * Render JSON Error - * - * @return string */ - protected function renderJsonOutput() + protected function renderJsonOutput(): string { $message = $this->renderJsonMessage($this->getThrown()); @@ -97,10 +79,8 @@ protected function renderJsonOutput() /** * Render XML Error - * - * @return string */ - protected function renderXmlOutput() + protected function renderXmlOutput(): string { $message = $this->renderXmlMessage($this->getThrown()); diff --git a/packages/app/src/Charcoal/App/Handler/HandlerConfig.php b/packages/app/src/Charcoal/App/Handler/HandlerConfig.php index a4944bceb..d34f8e6ed 100644 --- a/packages/app/src/Charcoal/App/Handler/HandlerConfig.php +++ b/packages/app/src/Charcoal/App/Handler/HandlerConfig.php @@ -16,26 +16,20 @@ class HandlerConfig extends AbstractConfig { /** * The view (to load). - * - * @var string|null */ - private $template; + private ?string $template = null; /** * The view engine ident to use. * * For example: "mustache" - * - * @var string|null */ - private $engine; + private ?string $engine = null; /** * The view controller. - * - * @var string|null */ - private $controller; + private ?string $controller = null; /** * Dynamic views to register. @@ -48,31 +42,23 @@ class HandlerConfig extends AbstractConfig /** * Additional view data. - * - * @var array */ - private $templateData = []; + private array $templateData = []; /** * Enable handler-level caching for the view. - * - * @var boolean $cache */ - private $cache = false; + private bool $cache = false; /** * Time-to-live, in seconds, of the cache. (0 = no limit). - * - * @var integer */ - private $cacheTtl = 0; + private int $cacheTtl = 0; /** * Retrieve the default handler types. - * - * @return array */ - public static function defaultHandlerTypes() + public static function defaultHandlerTypes(): array { return [ 'maintenance', @@ -90,7 +76,7 @@ public static function defaultHandlerTypes() * @throws InvalidArgumentException If the template view is invalid. * @return HandlerConfig Chainable */ - public function setTemplate($template) + public function setTemplate($template): static { if (empty($template)) { $this->template = null; @@ -109,10 +95,8 @@ public function setTemplate($template) /** * Retrieve the template view. - * - * @return string */ - public function template() + public function template(): string { if ($this->template === null) { return 'charcoal/app/handler/layout'; @@ -126,9 +110,8 @@ public function template() * * @param string|null $controller Handler controller name. * @throws InvalidArgumentException If the template controller is invalid. - * @return self */ - public function setController($controller) + public function setController($controller): static { if (empty($controller)) { $this->controller = null; @@ -183,9 +166,8 @@ public function defaultController() * * @param string|null $engine The view engine identifier. * @throws InvalidArgumentException If the engine is invalid. - * @return self */ - public function setEngine($engine) + public function setEngine($engine): static { if (empty($engine)) { $this->engine = null; @@ -235,9 +217,8 @@ public function defaultEngine() * Set the "handlerMessage" view ident. * * @param string $templateIdent A template identifier. - * @return self */ - public function setPartial($templateIdent) + public function setPartial($templateIdent): static { $this->partials['handlerMessage'] = $templateIdent; return $this; @@ -259,7 +240,7 @@ public function partial() * @param array $partials Dynamic templates. * @return HandlerConfig Chainable */ - public function setPartials(array $partials) + public function setPartials(array $partials): static { $this->partials = array_replace($this->partials, $partials); return $this; @@ -281,7 +262,7 @@ public function partials() * @param array $data Additional template data. * @return HandlerConfig Chainable */ - public function setTemplateData(array $data) + public function setTemplateData(array $data): static { $this->templateData = array_merge($this->templateData, $data); return $this; @@ -289,10 +270,8 @@ public function setTemplateData(array $data) /** * Retrieve the template data for the view. - * - * @return array */ - public function templateData() + public function templateData(): array { return $this->templateData; } @@ -303,18 +282,16 @@ public function templateData() * @param boolean $cache The cache flag. * @return HandlerConfig Chainable */ - public function setCache($cache) + public function setCache($cache): static { - $this->cache = !!$cache; + $this->cache = (bool) $cache; return $this; } /** * Determine if the cache is enabled or disabled. - * - * @return boolean */ - public function cache() + public function cache(): bool { return $this->cache; } @@ -325,7 +302,7 @@ public function cache() * @param integer $ttl The time-to-live, in seconds. * @return HandlerConfig Chainable */ - public function setCacheTtl($ttl) + public function setCacheTtl($ttl): static { $this->cacheTtl = intval($ttl); return $this; @@ -333,10 +310,8 @@ public function setCacheTtl($ttl) /** * Retrieve the time-to-live for the cached template. - * - * @return integer */ - public function cacheTtl() + public function cacheTtl(): int { return $this->cacheTtl; } diff --git a/packages/app/src/Charcoal/App/Handler/HandlerInterface.php b/packages/app/src/Charcoal/App/Handler/HandlerInterface.php index f371301c2..9c6f69d02 100644 --- a/packages/app/src/Charcoal/App/Handler/HandlerInterface.php +++ b/packages/app/src/Charcoal/App/Handler/HandlerInterface.php @@ -1,5 +1,7 @@ renderPlainOutput(); } else { $contentType = $this->determineContentType($request); - switch ($contentType) { - case 'application/json': - $output = $this->renderJsonOutput(); - break; - - case 'text/xml': - case 'application/xml': - $output = $this->renderXmlOutput(); - break; - - case 'text/html': - $output = $this->renderHtmlOutput(); - break; - - case 'text/plain': - $output = $this->renderPlainOutput(); - break; - - default: - throw new UnexpectedValueException(sprintf( - 'Cannot render unknown content type: %s', - $contentType - )); - } + $output = match ($contentType) { + 'application/json' => $this->renderJsonOutput(), + 'text/xml', 'application/xml' => $this->renderXmlOutput(), + 'text/html' => $this->renderHtmlOutput(), + 'text/plain' => $this->renderPlainOutput(), + default => throw new UnexpectedValueException(sprintf( + 'Cannot render unknown content type: %s', + $contentType + )), + }; } return $this->respondWith( @@ -74,10 +60,8 @@ public function __invoke( /** * Render Text Error - * - * @return string */ - protected function renderPlainOutput() + protected function renderPlainOutput(): string { $message = $this->translator()->translate('Service Unavailable', [], 'charcoal'); @@ -86,10 +70,8 @@ protected function renderPlainOutput() /** * Render JSON Error - * - * @return string */ - protected function renderJsonOutput() + protected function renderJsonOutput(): string { $message = $this->translator()->translate( 'The server is currently unavailable. We will be right back.', @@ -103,10 +85,8 @@ protected function renderJsonOutput() /** * Render XML Error - * - * @return string */ - protected function renderXmlOutput() + protected function renderXmlOutput(): string { $message = $this->translator()->translate( 'The server is currently unavailable. We will be right back.', @@ -129,10 +109,9 @@ protected function renderHtmlOutput() /** * Retrieve the response's HTTP code. - * - * @return integer */ - public function getCode() + #[\Override] + public function getCode(): int { return 503; } diff --git a/packages/app/src/Charcoal/App/Handler/NotAllowed.php b/packages/app/src/Charcoal/App/Handler/NotAllowed.php index d4a3287c4..62822a185 100644 --- a/packages/app/src/Charcoal/App/Handler/NotAllowed.php +++ b/packages/app/src/Charcoal/App/Handler/NotAllowed.php @@ -49,30 +49,16 @@ public function __invoke( } else { $status = 405; $contentType = $this->determineContentType($request); - switch ($contentType) { - case 'application/json': - $output = $this->renderJsonOutput(); - break; - - case 'text/xml': - case 'application/xml': - $output = $this->renderXmlOutput(); - break; - - case 'text/html': - $output = $this->renderHtmlOutput(); - break; - - case 'text/plain': - $output = $this->renderPlainOutput(); - break; - - default: - throw new UnexpectedValueException(sprintf( - 'Cannot render unknown content type: %s', - $contentType - )); - } + $output = match ($contentType) { + 'application/json' => $this->renderJsonOutput(), + 'text/xml', 'application/xml' => $this->renderXmlOutput(), + 'text/html' => $this->renderHtmlOutput(), + 'text/plain' => $this->renderPlainOutput(), + default => throw new UnexpectedValueException(sprintf( + 'Cannot render unknown content type: %s', + $contentType + )), + }; } return $this->respondWith( @@ -87,9 +73,8 @@ public function __invoke( * Set the HTTP methods allowed by the current request. * * @param array $methods Case-sensitive array of methods. - * @return self */ - protected function setMethods(array $methods) + protected function setMethods(array $methods): static { $this->methods = implode(', ', $methods); @@ -108,10 +93,8 @@ public function getMethods() /** * Render Text Error - * - * @return string */ - protected function renderPlainOutput() + protected function renderPlainOutput(): string { $message = $this->translator()->translate('Allowed methods: {{ methods }}', [ '{{ methods }}' => $this->getMethods() @@ -122,10 +105,8 @@ protected function renderPlainOutput() /** * Render JSON Error - * - * @return string */ - protected function renderJsonOutput() + protected function renderJsonOutput(): string { $message = $this->translator()->translate('Method not allowed. Must be one of: {{ methods }}', [ '{{ methods }}' => $this->getMethods() @@ -137,10 +118,8 @@ protected function renderJsonOutput() /** * Render XML Error - * - * @return string */ - protected function renderXmlOutput() + protected function renderXmlOutput(): string { $message = $this->translator()->translate('Method not allowed. Must be one of: {{ methods }}', [ '{{ methods }}' => $this->getMethods() @@ -165,6 +144,7 @@ protected function renderHtmlOutput() * @param array|\ArrayAccess $data Raw template data. * @return array|\ArrayAccess Expanded and processed template data. */ + #[\Override] protected function parseTemplateData($data = []) { $data['allowedMethods'] = $this->getMethods(); @@ -174,10 +154,9 @@ protected function parseTemplateData($data = []) /** * Retrieve the response's HTTP code. - * - * @return integer */ - public function getCode() + #[\Override] + public function getCode(): int { return 405; } @@ -194,10 +173,8 @@ public function getSummary() /** * Retrieve the handler's message. - * - * @return string */ - public function getMessage() + public function getMessage(): string { return $this->renderPlainOutput(); } diff --git a/packages/app/src/Charcoal/App/Handler/NotFound.php b/packages/app/src/Charcoal/App/Handler/NotFound.php index f9d75ec19..3dec54fcd 100644 --- a/packages/app/src/Charcoal/App/Handler/NotFound.php +++ b/packages/app/src/Charcoal/App/Handler/NotFound.php @@ -33,30 +33,16 @@ public function __invoke( $this->setHttpRequest($request); $contentType = $this->determineContentType($request); - switch ($contentType) { - case 'application/json': - $output = $this->renderJsonOutput(); - break; - - case 'text/xml': - case 'application/xml': - $output = $this->renderXmlOutput(); - break; - - case 'text/html': - $output = $this->renderHtmlOutput(); - break; - - case 'text/plain': - $output = $this->renderPlainOutput(); - break; - - default: - throw new UnexpectedValueException(sprintf( - 'Cannot render unknown content type: %s', - $contentType - )); - } + $output = match ($contentType) { + 'application/json' => $this->renderJsonOutput(), + 'text/xml', 'application/xml' => $this->renderXmlOutput(), + 'text/html' => $this->renderHtmlOutput(), + 'text/plain' => $this->renderPlainOutput(), + default => throw new UnexpectedValueException(sprintf( + 'Cannot render unknown content type: %s', + $contentType + )), + }; return $this->respondWith( $response->withStatus(404), @@ -67,10 +53,8 @@ public function __invoke( /** * Render Text Error - * - * @return string */ - protected function renderPlainOutput() + protected function renderPlainOutput(): string { $message = $this->translator()->translate('Not Found', [], 'charcoal'); @@ -79,10 +63,8 @@ protected function renderPlainOutput() /** * Render JSON Error - * - * @return string */ - protected function renderJsonOutput() + protected function renderJsonOutput(): string { $message = $this->translator()->translate('Not Found', [], 'charcoal'); $message = htmlspecialchars($message, ENT_QUOTES, 'UTF-8'); @@ -92,10 +74,8 @@ protected function renderJsonOutput() /** * Render XML Error - * - * @return string */ - protected function renderXmlOutput() + protected function renderXmlOutput(): string { $message = $this->translator()->translate('Not Found', [], 'charcoal'); @@ -114,10 +94,9 @@ protected function renderHtmlOutput() /** * Retrieve the response's HTTP code. - * - * @return integer */ - public function getCode() + #[\Override] + public function getCode(): int { return 404; } diff --git a/packages/app/src/Charcoal/App/Handler/PhpError.php b/packages/app/src/Charcoal/App/Handler/PhpError.php index f2af5a186..59673cff7 100644 --- a/packages/app/src/Charcoal/App/Handler/PhpError.php +++ b/packages/app/src/Charcoal/App/Handler/PhpError.php @@ -37,30 +37,16 @@ public function __invoke( $this->setThrown($error); $contentType = $this->determineContentType($request); - switch ($contentType) { - case 'application/json': - $output = $this->renderJsonOutput(); - break; - - case 'text/xml': - case 'application/xml': - $output = $this->renderXmlOutput(); - break; - - case 'text/html': - $output = $this->renderHtmlOutput(); - break; - - case 'text/plain': - $output = $this->renderPlainOutput(); - break; - - default: - throw new UnexpectedValueException(sprintf( - 'Cannot render unknown content type: %s', - $contentType - )); - } + $output = match ($contentType) { + 'application/json' => $this->renderJsonOutput(), + 'text/xml', 'application/xml' => $this->renderXmlOutput(), + 'text/html' => $this->renderHtmlOutput(), + 'text/plain' => $this->renderPlainOutput(), + default => throw new UnexpectedValueException(sprintf( + 'Cannot render unknown content type: %s', + $contentType + )), + }; $this->writeToErrorLog($error); @@ -73,10 +59,8 @@ public function __invoke( /** * Render Text Error - * - * @return string */ - protected function renderPlainOutput() + protected function renderPlainOutput(): string { $message = $this->renderTextMessage($this->getThrown()); @@ -85,10 +69,8 @@ protected function renderPlainOutput() /** * Render JSON Error - * - * @return string */ - protected function renderJsonOutput() + protected function renderJsonOutput(): string { $message = $this->renderJsonMessage($this->getThrown()); @@ -97,10 +79,8 @@ protected function renderJsonOutput() /** * Render XML Error - * - * @return string */ - protected function renderXmlOutput() + protected function renderXmlOutput(): string { $message = $this->renderXmlMessage($this->getThrown()); diff --git a/packages/app/src/Charcoal/App/Helper/CallbackStream.php b/packages/app/src/Charcoal/App/Helper/CallbackStream.php index 384da2e90..82200c928 100644 --- a/packages/app/src/Charcoal/App/Helper/CallbackStream.php +++ b/packages/app/src/Charcoal/App/Helper/CallbackStream.php @@ -19,9 +19,8 @@ class CallbackStream implements StreamInterface /** * Whether or not the callback has been previously invoked. - * @var boolean */ - private $called = false; + private bool $called = false; /** * CallbackStream constructor. @@ -32,12 +31,9 @@ public function __construct(callable $callback) $this->callback = $callback; } - /** - * @return string - */ - public function __toString() + public function __toString(): string { - return $this->output(); + return (string) $this->output(); } /** diff --git a/packages/app/src/Charcoal/App/Middleware/IpMiddleware.php b/packages/app/src/Charcoal/App/Middleware/IpMiddleware.php index 3e5678455..633318127 100644 --- a/packages/app/src/Charcoal/App/Middleware/IpMiddleware.php +++ b/packages/app/src/Charcoal/App/Middleware/IpMiddleware.php @@ -61,8 +61,6 @@ public function __construct(array $data) /** * Default middleware options. - * - * @return array */ public function defaults(): array { @@ -97,7 +95,7 @@ public function defaults(): array public function __invoke(RequestInterface $request, ResponseInterface $response, callable $next) { $ip = $this->getClientIp(); - if (!$ip) { + if ($ip === '' || $ip === '0') { if ((!empty($this->disallowed) || !empty($this->allowed)) && $this->failOnInvalidIp === true) { if ($this->errorMessage) { $response->getBody()->write($this->errorMessage); @@ -109,7 +107,7 @@ public function __invoke(RequestInterface $request, ResponseInterface $response, } // Check disallowed. - if ($this->isIpDisallowed($ip) === true) { + if ($this->isIpDisallowed($ip)) { if ($this->disallowedRedirect !== '') { return $response ->withStatus(302) @@ -150,9 +148,8 @@ public function __invoke(RequestInterface $request, ResponseInterface $response, * Note: this method only performs an exact string match on IP address, no IP masking / range features. * * @param string $ip The IP address to check against the disallowed. - * @return boolean */ - private function isIpDisallowed($ip): bool + private function isIpDisallowed(string $ip): bool { if (empty($this->disallowed)) { return false; @@ -168,9 +165,8 @@ private function isIpDisallowed($ip): bool * Note; This method only performs an exact string match on IP address, no IP masking / range features. * * @param string $ip The IP address to check against the allowed. - * @return boolean */ - private function isIpAllowed($ip): bool + private function isIpAllowed(string $ip): bool { if (empty($this->allowed)) { return true; @@ -181,18 +177,17 @@ private function isIpAllowed($ip): bool /** * @param string $ip The IP to check. * @param string[] $cidrs The array of IPs/CIDRs to validate against. "/32" netmask is assumed. - * @return boolean */ private function isIpInRange(string $ip, array $cidrs): bool { foreach ($cidrs as $range) { - if (strpos($range, '/') === false) { + if (!str_contains($range, '/')) { $range .= '/32'; } // $range is in IP/CIDR format eg 127.0.0.1/24 - list($subnet, $netmask) = explode('/', $range, 2); - $netmask = ~(pow(2, (32 - (int)$netmask)) - 1); - if ((ip2long($ip) & $netmask) == (ip2long($subnet) & $netmask)) { + [$subnet, $netmask] = explode('/', $range, 2); + $netmask = ~(2 ** (32 - (int)$netmask) - 1); + if ((ip2long($ip) & $netmask) === (ip2long($subnet) & $netmask)) { return true; } } @@ -200,9 +195,6 @@ private function isIpInRange(string $ip, array $cidrs): bool return false; } - /** - * @return string - */ private function getClientIp(): string { if (isset($_SERVER['REMOTE_ADDR'])) { diff --git a/packages/app/src/Charcoal/App/Module/AbstractModule.php b/packages/app/src/Charcoal/App/Module/AbstractModule.php index d45da11d3..bfc5e3000 100644 --- a/packages/app/src/Charcoal/App/Module/AbstractModule.php +++ b/packages/app/src/Charcoal/App/Module/AbstractModule.php @@ -105,9 +105,9 @@ public function setUp() */ public function setupRoutes() { - if (!isset($this->routeManager)) { + if ($this->routeManager === null) { $config = $this->config(); - $routes = (isset($config['routes']) ? $config['routes'] : [] ); + $routes = ($config['routes'] ?? [] ); $this->routeManager = new RouteManager([ 'config' => $routes, diff --git a/packages/app/src/Charcoal/App/Module/ModuleInterface.php b/packages/app/src/Charcoal/App/Module/ModuleInterface.php index bdd2c30ac..e6683ad96 100644 --- a/packages/app/src/Charcoal/App/Module/ModuleInterface.php +++ b/packages/app/src/Charcoal/App/Module/ModuleInterface.php @@ -1,5 +1,7 @@ createConfig() * * @param mixed|null $data Optional config data. - * @return ActionRouteConfig */ - public function createConfig($data = null) + public function createConfig($data = null): \Charcoal\App\Route\ActionRouteConfig { return new ActionRouteConfig($data); } diff --git a/packages/app/src/Charcoal/App/Route/ActionRouteConfig.php b/packages/app/src/Charcoal/App/Route/ActionRouteConfig.php index e4a7f29d6..aa5510850 100644 --- a/packages/app/src/Charcoal/App/Route/ActionRouteConfig.php +++ b/packages/app/src/Charcoal/App/Route/ActionRouteConfig.php @@ -1,5 +1,7 @@ actionData = $actionData; return $this; @@ -29,10 +28,8 @@ public function setActionData(array $actionData) /** * Get the action data. - * - * @return array */ - public function actionData() + public function actionData(): array { return $this->actionData; } diff --git a/packages/app/src/Charcoal/App/Route/RouteConfig.php b/packages/app/src/Charcoal/App/Route/RouteConfig.php index 97b287c93..eef7e3481 100644 --- a/packages/app/src/Charcoal/App/Route/RouteConfig.php +++ b/packages/app/src/Charcoal/App/Route/RouteConfig.php @@ -13,54 +13,44 @@ class RouteConfig extends AbstractConfig { /** * Route identifier/name - * - * @var string */ - private $ident; + private ?string $ident = null; /** * Route pattern - * - * @var string */ - private $route; + private ?string $route = null; /** * HTTP methods supported by this route * * @var string[] */ - private $methods = [ 'GET' ]; + private array $methods = [ 'GET' ]; /** * Response controller classname * * Should be the class-ident of an action, a script or a template controller. - * - * @var string */ - private $controller; + private ?string $controller = null; /** * Parent route groups * * @var string[] */ - private $groups = []; + private array $groups = []; /** * Optional headers to set on response. - * - * @var array */ - private $headers = []; + private array $headers = []; /** * Retrieve the default route types. - * - * @return array */ - public static function defaultRouteTypes() + public static function defaultRouteTypes(): array { return [ 'templates', @@ -74,9 +64,8 @@ public static function defaultRouteTypes() * * @param string $ident Route identifier. * @throws InvalidArgumentException If the identifier is not a string. - * @return self */ - public function setIdent($ident) + public function setIdent($ident): static { if (!is_string($ident)) { throw new InvalidArgumentException( @@ -94,7 +83,7 @@ public function setIdent($ident) * * @return string */ - public function ident() + public function ident(): ?string { return $this->ident; } @@ -104,9 +93,8 @@ public function ident() * * @param string $pattern Route pattern. * @throws InvalidArgumentException If the pattern argument is not a string. - * @return self */ - public function setRoute($pattern) + public function setRoute($pattern): static { if (!is_string($pattern)) { throw new InvalidArgumentException( @@ -124,7 +112,7 @@ public function setRoute($pattern) * * @return string */ - public function route() + public function route(): ?string { return $this->route; } @@ -133,9 +121,8 @@ public function route() * Set parent route groups * * @param string[] $groups The parent route groups. - * @return self */ - public function setGroups(array $groups) + public function setGroups(array $groups): static { $this->groups = []; @@ -151,9 +138,8 @@ public function setGroups(array $groups) * * @param string $group The parent route group. * @throws InvalidArgumentException If the group is invalid. - * @return self */ - public function addGroup($group) + public function addGroup($group): static { if (!is_string($group)) { throw new InvalidArgumentException( @@ -168,10 +154,8 @@ public function addGroup($group) /** * Get parent route groups - * - * @return array */ - public function groups() + public function groups(): array { return $this->groups; } @@ -180,9 +164,8 @@ public function groups() * Add custom headers * * @param array $headers The custom headers, in key=>val pairs. - * @return self */ - public function setHeaders(array $headers) + public function setHeaders(array $headers): static { $this->headers = []; foreach ($headers as $name => $val) { @@ -197,7 +180,7 @@ public function setHeaders(array $headers) * @throws InvalidArgumentException If the header name or value is not a string. * @return $this */ - public function addHeader($name, $val) + public function addHeader($name, $val): static { if (!is_string($name)) { throw new InvalidArgumentException( @@ -215,10 +198,8 @@ public function addHeader($name, $val) /** * Get custom route headers. - * - * @return array */ - public function headers() + public function headers(): array { return $this->headers; } @@ -228,9 +209,8 @@ public function headers() * * @param string $controller Route controller name. * @throws InvalidArgumentException If the route view controller is not a string. - * @return self */ - public function setController($controller) + public function setController($controller): static { if (!is_string($controller)) { throw new InvalidArgumentException( @@ -250,9 +230,9 @@ public function setController($controller) * * @return string */ - public function controller() + public function controller(): ?string { - if (!isset($this->controller)) { + if ($this->controller === null) { return $this->ident(); } @@ -263,9 +243,8 @@ public function controller() * Set route methods * * @param string[] $methods The route's supported HTTP methods. - * @return self */ - public function setMethods(array $methods) + public function setMethods(array $methods): static { $this->methods = []; @@ -281,15 +260,14 @@ public function setMethods(array $methods) * * @param string $method The route's supported HTTP method. * @throws InvalidArgumentException If the HTTP method is invalid. - * @return self */ - public function addMethod($method) + public function addMethod($method): static { if (!is_string($method)) { throw new InvalidArgumentException( sprintf( 'Unsupported HTTP method; must be a string, received %s', - (is_object($method) ? get_class($method) : gettype($method)) + (get_debug_type($method)) ) ); } @@ -327,7 +305,7 @@ public function addMethod($method) * * @return string[] */ - public function methods() + public function methods(): array { return $this->methods; } diff --git a/packages/app/src/Charcoal/App/Route/RouteInterface.php b/packages/app/src/Charcoal/App/Route/RouteInterface.php index 54bd241a8..51d415832 100644 --- a/packages/app/src/Charcoal/App/Route/RouteInterface.php +++ b/packages/app/src/Charcoal/App/Route/RouteInterface.php @@ -1,5 +1,7 @@ config(); - if (PHP_SAPI == 'cli') { - $scripts = ( isset($routes['scripts']) ? $routes['scripts'] : [] ); + if (PHP_SAPI === 'cli') { + $scripts = ( $routes['scripts'] ?? [] ); foreach ($scripts as $scriptIdent => $scriptConfig) { $this->setupScript($scriptIdent, $scriptConfig); } } else { - $templates = ( isset($routes['templates']) ? $routes['templates'] : [] ); + $templates = ( $routes['templates'] ?? [] ); foreach ($templates as $routeIdent => $templateConfig) { $this->setupTemplate($routeIdent, $templateConfig); } - $actions = ( isset($routes['actions']) ? $routes['actions'] : [] ); + $actions = ( $routes['actions'] ?? [] ); foreach ($actions as $actionIdent => $actionConfig) { $this->setupAction($actionIdent, $actionConfig); } @@ -77,15 +75,11 @@ public function setupRoutes() */ private function setupTemplate($routeIdent, $templateConfig) { - $routePattern = isset($templateConfig['route']) - ? $templateConfig['route'] - : '/' . ltrim($routeIdent, '/'); + $routePattern = $templateConfig['route'] ?? '/' . ltrim($routeIdent, '/'); $templateConfig['route'] = $routePattern; - $methods = isset($templateConfig['methods']) - ? $templateConfig['methods'] - : [ 'GET' ]; + $methods = $templateConfig['methods'] ?? [ 'GET' ]; $routeHandler = $this->app->map( $methods, @@ -119,9 +113,7 @@ function ( } $defaultController = $this['route/controller/template/class']; - $routeController = isset($templateConfig['route_controller']) - ? $templateConfig['route_controller'] - : $defaultController; + $routeController = $templateConfig['route_controller'] ?? $defaultController; $routeFactory = $this['route/factory']; $routeFactory->setDefaultClass($defaultController); @@ -153,15 +145,11 @@ function ( */ private function setupAction($routeIdent, $actionConfig) { - $routePattern = isset($actionConfig['route']) - ? $actionConfig['route'] - : '/' . ltrim($routeIdent, '/'); + $routePattern = $actionConfig['route'] ?? '/' . ltrim($routeIdent, '/'); $actionConfig['route'] = $routePattern; - $methods = isset($actionConfig['methods']) - ? $actionConfig['methods'] - : [ 'POST' ]; + $methods = $actionConfig['methods'] ?? [ 'POST' ]; $routeHandler = $this->app->map( $methods, @@ -195,9 +183,7 @@ function ( } $defaultController = $this['route/controller/action/class']; - $routeController = isset($actionConfig['route_controller']) - ? $actionConfig['route_controller'] - : $defaultController; + $routeController = $actionConfig['route_controller'] ?? $defaultController; $routeFactory = $this['route/factory']; $routeFactory->setDefaultClass($defaultController); @@ -229,15 +215,11 @@ function ( */ private function setupScript($routeIdent, $scriptConfig) { - $routePattern = isset($scriptConfig['route']) - ? $scriptConfig['route'] - : '/' . ltrim($routeIdent, '/'); + $routePattern = $scriptConfig['route'] ?? '/' . ltrim($routeIdent, '/'); $scriptConfig['route'] = $routePattern; - $methods = isset($scriptConfig['methods']) - ? $scriptConfig['methods'] - : [ 'GET' ]; + $methods = $scriptConfig['methods'] ?? [ 'GET' ]; $routeHandler = $this->app->map( $methods, @@ -271,9 +253,7 @@ function ( } $defaultController = $this['route/controller/script/class']; - $routeController = isset($scriptConfig['route_controller']) - ? $scriptConfig['route_controller'] - : $defaultController; + $routeController = $scriptConfig['route_controller'] ?? $defaultController; $routeFactory = $this['route/factory']; $routeFactory->setDefaultClass($defaultController); diff --git a/packages/app/src/Charcoal/App/Route/ScriptRoute.php b/packages/app/src/Charcoal/App/Route/ScriptRoute.php index 5af4e1a3d..0baeb1fc7 100644 --- a/packages/app/src/Charcoal/App/Route/ScriptRoute.php +++ b/packages/app/src/Charcoal/App/Route/ScriptRoute.php @@ -36,10 +36,9 @@ public function __construct(array $data) /** * @param mixed|null $data Optional config data. - * @return ScriptRouteConfig * @see ConfigurableTrait::createConfig() */ - public function createConfig($data = null) + public function createConfig($data = null): \Charcoal\App\Route\ScriptRouteConfig { return new ScriptRouteConfig($data); } diff --git a/packages/app/src/Charcoal/App/Route/ScriptRouteConfig.php b/packages/app/src/Charcoal/App/Route/ScriptRouteConfig.php index f34fa141f..cea95818b 100644 --- a/packages/app/src/Charcoal/App/Route/ScriptRouteConfig.php +++ b/packages/app/src/Charcoal/App/Route/ScriptRouteConfig.php @@ -1,5 +1,7 @@ createConfig() * * @param mixed|null $data Optional config data. - * @return TemplateRouteConfig */ - public function createConfig($data = null) + public function createConfig($data = null): \Charcoal\App\Route\TemplateRouteConfig { return new TemplateRouteConfig($data); } diff --git a/packages/app/src/Charcoal/App/Route/TemplateRouteConfig.php b/packages/app/src/Charcoal/App/Route/TemplateRouteConfig.php index 845ef94b5..2249cf297 100644 --- a/packages/app/src/Charcoal/App/Route/TemplateRouteConfig.php +++ b/packages/app/src/Charcoal/App/Route/TemplateRouteConfig.php @@ -16,22 +16,19 @@ class TemplateRouteConfig extends RouteConfig { /** * The template ident (to load). - * @var string|null $template */ - private $template; + private ?string $template = null; /** * The view engine ident to use. * Ex: "mustache", "" - * @var string|null $engine */ - private $engine; + private ?string $engine = null; /** * Additional template data. - * @var array $templateData */ - private $templateData = []; + private array $templateData = []; /** * Redirect URL. @@ -41,28 +38,25 @@ class TemplateRouteConfig extends RouteConfig /** * Redirect Mode (HTTP status code). - * @var integer $redirectMode */ - private $redirectMode = 301; + private int $redirectMode = 301; /** * Enable route-level caching for this template. - * @var boolean $cache */ - private $cache = false; + private bool $cache = false; /** * If using cache, the time-to-live, in seconds, of the cache. (0 = no limit). - * @var integer $cacheTtl */ - private $cacheTtl = 0; + private int $cacheTtl = 0; /** * @param string|null $template The template identifier. * @throws InvalidArgumentException If the tempalte parameter is not null or not a string. * @return TemplateRouteConfig Chainable */ - public function setTemplate($template) + public function setTemplate($template): static { if ($template === null) { $this->template = null; @@ -80,7 +74,7 @@ public function setTemplate($template) /** * @return string */ - public function template() + public function template(): ?string { if ($this->template === null) { return $this->ident(); @@ -110,7 +104,7 @@ public function defaultController() * @throws InvalidArgumentException If the engine is not null or not a string. * @return TemplateRouteConfig Chainable */ - public function setEngine($engine) + public function setEngine($engine): static { if ($engine === null) { $this->engine = null; @@ -156,9 +150,9 @@ public function defaultEngine() * @param array $templateData The route template data. * @return TemplateRouteConfig Chainable */ - public function setTemplateData(array $templateData) + public function setTemplateData(array $templateData): static { - if (!isset($this->templateData)) { + if ($this->templateData === null) { $this->templateData = []; } @@ -169,10 +163,8 @@ public function setTemplateData(array $templateData) /** * Get the template data for the view. - * - * @return array */ - public function templateData() + public function templateData(): array { return $this->templateData; } @@ -181,7 +173,7 @@ public function templateData() * @param string|string[] $url Points to a route. * @return TemplateRouteConfig Chainable */ - public function setRedirect($url) + public function setRedirect($url): static { $this->redirect = $url; @@ -205,7 +197,7 @@ public function redirect() * @throws InvalidArgumentException If the redirect mode is not 3xx. * @return TemplateRouteConfig Chainable */ - public function setRedirectMode($redirectMode) + public function setRedirectMode($redirectMode): static { $redirectMode = (int)$redirectMode; if ($redirectMode < 300 || $redirectMode >= 400) { @@ -218,10 +210,7 @@ public function setRedirectMode($redirectMode) return $this; } - /** - * @return integer - */ - public function redirectMode() + public function redirectMode(): int { return $this->redirectMode; } @@ -230,16 +219,13 @@ public function redirectMode() * @param boolean $cache The cache enabled flag. * @return TemplateRouteConfig Chainable */ - public function setCache($cache) + public function setCache($cache): static { - $this->cache = !!$cache; + $this->cache = (bool) $cache; return $this; } - /** - * @return boolean - */ - public function cache() + public function cache(): bool { return $this->cache; } @@ -248,16 +234,13 @@ public function cache() * @param integer $ttl The cache Time-To-Live, in seconds. * @return TemplateRouteConfig Chainable */ - public function setCacheTtl($ttl) + public function setCacheTtl($ttl): static { $this->cacheTtl = intval($ttl); return $this; } - /** - * @return integer - */ - public function cacheTtl() + public function cacheTtl(): int { return $this->cacheTtl; } diff --git a/packages/app/src/Charcoal/App/Script/AbstractScript.php b/packages/app/src/Charcoal/App/Script/AbstractScript.php index 614b39dba..7c7e30043 100644 --- a/packages/app/src/Charcoal/App/Script/AbstractScript.php +++ b/packages/app/src/Charcoal/App/Script/AbstractScript.php @@ -30,50 +30,23 @@ abstract class AbstractScript extends AbstractEntity implements { use LoggerAwareTrait; - /** - * @var string $ident - */ - private $ident; + private ?string $ident = null; - /** - * @var string $description - */ - private $description; + private ?string $description = null; - /** - * @var array $arguments - */ - private $arguments; + private ?array $arguments = null; - /** - * @var CLImate $climate - */ - private $climate; + private \League\CLImate\CLImate $climate; - /** - * @var ReaderInterface $cliamteReader - */ - private $climateReader; + private ?\League\CLImate\Util\Reader\ReaderInterface $climateReader = null; - /** - * @var boolean $quiet - */ - private $quiet = false; + private bool $quiet = false; - /** - * @var boolean $verbose - */ - private $verbose = false; + private bool $verbose = false; - /** - * @var boolean $interactive - */ - private $interactive = false; + private bool $interactive = false; - /** - * @var boolean $dryRun - */ - private $dryRun = false; + private bool $dryRun = false; /** * Return a new CLI script. @@ -231,7 +204,7 @@ public function description() */ public function setQuiet($quiet) { - $this->quiet = !!$quiet; + $this->quiet = (bool) $quiet; return $this; } @@ -249,7 +222,7 @@ public function quiet() */ public function setVerbose($verbose) { - $this->verbose = !!$verbose; + $this->verbose = (bool) $verbose; return $this; } @@ -267,7 +240,7 @@ public function verbose() */ public function setInteractive($interactive) { - $this->interactive = !!$interactive; + $this->interactive = (bool) $interactive; return $this; } @@ -285,7 +258,7 @@ public function interactive() */ public function setDryRun($simulate) { - $this->dryRun = !!$simulate; + $this->dryRun = (bool) $simulate; return $this; } @@ -434,7 +407,7 @@ protected function argOrInput($argName) * @throws RuntimeException If a radio or checkbox prompt has no options. * @return mixed Returns the prompt value. */ - protected function input($name) + protected function input(string $name) { $cli = $this->climate(); $arg = $this->argument($name); @@ -468,19 +441,15 @@ protected function input($name) $accept = false; } - if (!in_array($type, [ 'confirm', 'checkboxes', 'radio' ])) { - if (isset($arg['defaultValue'])) { - $default = $arg['defaultValue']; - - if (is_bool($default) || is_null($default)) { - $default = var_export($default, true); - } - - if ($default && is_string($default) || is_numeric($default)) { - $pattern = '/[\(\[\<]' . preg_quote($default, '/') . '[\)\]\>]/'; - if (!preg_match($pattern, $prompt)) { - $prompt .= ' (' . $default . ')'; - } + if (!in_array($type, [ 'confirm', 'checkboxes', 'radio' ]) && isset($arg['defaultValue'])) { + $default = $arg['defaultValue']; + if (is_bool($default) || is_null($default)) { + $default = var_export($default, true); + } + if ($default && is_string($default) || is_numeric($default)) { + $pattern = '/[\(\[\<]' . preg_quote($default, '/') . '[\)\]\>]/'; + if (!preg_match($pattern, (string) $prompt)) { + $prompt .= ' (' . $default . ')'; } } } @@ -523,12 +492,8 @@ protected function input($name) break; } - if ($accept) { - if (isset($arg['acceptValue'])) { - if (is_array($arg['acceptValue']) || is_callable($arg['acceptValue'])) { - $input->accept($arg['acceptValue']); - } - } + if ($accept && isset($arg['acceptValue']) && (is_array($arg['acceptValue']) || is_callable($arg['acceptValue']))) { + $input->accept($arg['acceptValue']); } if (isset($arg['defaultValue'])) { @@ -540,18 +505,16 @@ protected function input($name) /** * @param CLImate $climate A climate instance. - * @return void */ - private function setClimate(CLImate $climate) + private function setClimate(CLImate $climate): void { $this->climate = $climate; } /** * @param ReaderInterface $climateReader A climate reader. - * @return void */ - private function setClimateReader(ReaderInterface $climateReader) + private function setClimateReader(ReaderInterface $climateReader): void { $this->climateReader = $climateReader; } diff --git a/packages/app/src/Charcoal/App/Script/ArgScriptTrait.php b/packages/app/src/Charcoal/App/Script/ArgScriptTrait.php index 8f0dbb00a..c2fe6e98f 100644 --- a/packages/app/src/Charcoal/App/Script/ArgScriptTrait.php +++ b/packages/app/src/Charcoal/App/Script/ArgScriptTrait.php @@ -31,7 +31,7 @@ protected function parseAsArray($var, $delimiter = '[\s,]+') $var = preg_split('#(?useLock = !!$useLock; + $this->useLock = (bool) $useLock; return $this; } @@ -40,9 +40,8 @@ public function useLock() /** * @throws Exception If the lock file can not be opened or the script is already locked. - * @return boolean */ - public function startLock() + public function startLock(): bool { $lockFile = $this->getLockFileName(); $this->lockFilePointer = fopen($lockFile, 'w'); @@ -60,10 +59,7 @@ public function startLock() } } - /** - * @return void - */ - public function stopLock() + public function stopLock(): void { if ($this->lockFilePointer) { flock($this->lockFilePointer, LOCK_UN); @@ -71,10 +67,7 @@ public function stopLock() } } - /** - * @return string - */ - private function getLockFileName() + private function getLockFileName(): string { $lockName = str_replace('\\', '-', static::class); $lockName .= md5(__DIR__); diff --git a/packages/app/src/Charcoal/App/Script/PathScriptTrait.php b/packages/app/src/Charcoal/App/Script/PathScriptTrait.php index 9b43b7b86..85bb1d95f 100644 --- a/packages/app/src/Charcoal/App/Script/PathScriptTrait.php +++ b/packages/app/src/Charcoal/App/Script/PathScriptTrait.php @@ -45,7 +45,7 @@ public function processMultiplePaths($paths) throw new InvalidArgumentException('Received invalid paths.'); } - if (empty($paths)) { + if ($paths === []) { throw new InvalidArgumentException('Received empty paths.'); } @@ -78,13 +78,13 @@ public function pathExists($path) * @throws InvalidArgumentException If the path is invalid. * @return string Returns the filtered path. */ - public function filterPath($path, $trim = null) + public function filterPath($path, $trim = null): string { if (!is_string($path)) { throw new InvalidArgumentException('The path must be a string.'); } - if ($trim === null && defined(get_called_class() . '::DIRECTORY_SEPARATORS')) { + if ($trim === null && defined(static::class . '::DIRECTORY_SEPARATORS')) { $trim = static::DIRECTORY_SEPARATORS; } @@ -111,7 +111,7 @@ public function filterPath($path, $trim = null) */ public function filterWritablePath($path, $name = null) { - if ($name === null && defined(get_called_class() . '::DEFAULT_BASENAME')) { + if ($name === null && defined(static::class . '::DEFAULT_BASENAME')) { $name = static::DEFAULT_BASENAME; } @@ -135,7 +135,7 @@ public function filterWritablePath($path, $name = null) throw new InvalidArgumentException('The target file is not writeable.'); } } else { - $info = pathinfo($path); + $info = pathinfo((string) $path); $path = $this->filterWritablePath($info['dirname'], $info['basename']); } @@ -169,7 +169,7 @@ public function globRecursive($pattern, $flags = 0) } } - static::$globCache[$pattern][$depthKey] = array_filter($files, 'is_file'); + static::$globCache[$pattern][$depthKey] = array_filter($files, is_file(...)); return $files; } diff --git a/packages/app/src/Charcoal/App/Script/ScriptInterface.php b/packages/app/src/Charcoal/App/Script/ScriptInterface.php index 9a9b66ee2..0c6a1873b 100644 --- a/packages/app/src/Charcoal/App/Script/ScriptInterface.php +++ b/packages/app/src/Charcoal/App/Script/ScriptInterface.php @@ -1,5 +1,7 @@ register(new CacheServiceProvider()); $container->register(new DatabaseServiceProvider()); @@ -104,13 +103,13 @@ protected function registerKernelServices(Container $container) * @param Container $container A service container. * @return boolean */ - $container['debug'] = function (Container $container) { + $container['debug'] = function (Container $container): bool { if (isset($container['config']['debug'])) { - return !!$container['config']['debug']; + return (bool) $container['config']['debug']; } if (isset($container['config']['dev_mode'])) { - return !!$container['config']['dev_mode']; + return (bool) $container['config']['dev_mode']; } return false; @@ -273,19 +272,17 @@ protected function registerRouteServices(Container $container) * @param Container $container A service container. * @return \Charcoal\Factory\FactoryInterface */ - $container['route/factory'] = function (Container $container) { - return new Factory([ - 'base_class' => RouteInterface::class, - 'resolver_options' => [ - 'suffix' => 'Route', - ], - 'arguments' => [ - [ - 'logger' => $container['logger'], - ], + $container['route/factory'] = (fn(Container $container): \Charcoal\Factory\GenericFactory => new Factory([ + 'base_class' => RouteInterface::class, + 'resolver_options' => [ + 'suffix' => 'Route', + ], + 'arguments' => [ + [ + 'logger' => $container['logger'], ], - ]); - }; + ], + ])); } /** @@ -298,7 +295,7 @@ protected function registerMiddlewareServices(Container $container) * @param Container $container A service container. * @return IpMiddleware */ - $container['middlewares/charcoal/app/middleware/ip'] = function (container $container) { + $container['middlewares/charcoal/app/middleware/ip'] = function (container $container): \Charcoal\App\Middleware\IpMiddleware { $wareConfig = $container['config']['middlewares']['charcoal/app/middleware/ip']; return new IpMiddleware($wareConfig); }; @@ -319,20 +316,18 @@ protected function registerRequestControllerServices(Container $container) * @param Container $container A service container. * @return \Charcoal\Factory\FactoryInterface */ - $container['action/factory'] = function (Container $container) { - return new Factory([ - 'base_class' => ActionInterface::class, - 'resolver_options' => [ - 'suffix' => 'Action', + $container['action/factory'] = (fn(Container $container): \Charcoal\Factory\GenericFactory => new Factory([ + 'base_class' => ActionInterface::class, + 'resolver_options' => [ + 'suffix' => 'Action', + ], + 'arguments' => [ + [ + 'container' => $container, + 'logger' => $container['logger'], ], - 'arguments' => [ - [ - 'container' => $container, - 'logger' => $container['logger'], - ], - ], - ]); - }; + ], + ])); /** * The Template Factory service is used to instanciate new templates. @@ -343,20 +338,18 @@ protected function registerRequestControllerServices(Container $container) * @param Container $container A service container. * @return \Charcoal\Factory\FactoryInterface */ - $container['template/factory'] = function (Container $container) { - return new Factory([ - 'base_class' => TemplateInterface::class, - 'resolver_options' => [ - 'suffix' => 'Template', - ], - 'arguments' => [ - [ - 'container' => $container, - 'logger' => $container['logger'], - ], + $container['template/factory'] = (fn(Container $container): \Charcoal\Factory\GenericFactory => new Factory([ + 'base_class' => TemplateInterface::class, + 'resolver_options' => [ + 'suffix' => 'Template', + ], + 'arguments' => [ + [ + 'container' => $container, + 'logger' => $container['logger'], ], - ]); - }; + ], + ])); /** * The Widget Factory service is used to instanciate new widgets. @@ -367,28 +360,24 @@ protected function registerRequestControllerServices(Container $container) * @param Container $container A service container. * @return \Charcoal\Factory\FactoryInterface */ - $container['widget/factory'] = function (Container $container) { - return new Factory([ - 'base_class' => WidgetInterface::class, - 'resolver_options' => [ - 'suffix' => 'Widget', + $container['widget/factory'] = (fn(Container $container): \Charcoal\Factory\GenericFactory => new Factory([ + 'base_class' => WidgetInterface::class, + 'resolver_options' => [ + 'suffix' => 'Widget', + ], + 'arguments' => [ + [ + 'container' => $container, + 'logger' => $container['logger'], ], - 'arguments' => [ - [ - 'container' => $container, - 'logger' => $container['logger'], - ], - ], - ]); - }; + ], + ])); /** * @param Container $container A service container. * @return WidgetBuilder */ - $container['widget/builder'] = function (Container $container) { - return new WidgetBuilder($container['widget/factory'], $container); - }; + $container['widget/builder'] = (fn(Container $container): \Charcoal\App\Template\WidgetBuilder => new WidgetBuilder($container['widget/factory'], $container)); } /** @@ -405,19 +394,17 @@ protected function registerModuleServices(Container $container) * @param Container $container A service container. * @return \Charcoal\Factory\FactoryInterface */ - $container['module/factory'] = function (Container $container) { - return new Factory([ - 'base_class' => ModuleInterface::class, - 'resolver_options' => [ - 'suffix' => 'Module', - ], - 'arguments' => [ - [ - 'logger' => $container['logger'], - ], + $container['module/factory'] = (fn(Container $container): \Charcoal\Factory\GenericFactory => new Factory([ + 'base_class' => ModuleInterface::class, + 'resolver_options' => [ + 'suffix' => 'Module', + ], + 'arguments' => [ + [ + 'logger' => $container['logger'], ], - ]); - }; + ], + ])); /** * The modules as PHP classes. @@ -425,7 +412,7 @@ protected function registerModuleServices(Container $container) * @param Container $container A service container. * @return array */ - $container['module/classes'] = function (Container $container) { + $container['module/classes'] = function (Container $container): array { $appConfig = $container['config']; $modules = $appConfig['modules']; @@ -435,13 +422,9 @@ protected function registerModuleServices(Container $container) 'suffix' => 'Module', ]); - $modules = array_map(function ($module) use ($moduleResolver) { - return $moduleResolver->resolve($module); - }, $modules); + $modules = array_map($moduleResolver->resolve(...), $modules); - array_filter($modules, function ($class) { - return class_exists($class); - }); + array_filter($modules, class_exists(...)); return $modules; }; @@ -462,14 +445,11 @@ protected function registerViewServices(Container $container) /** * @param Container $container The DI container. - * @return void */ protected function registerMustacheHelpersServices(Container $container): void { if (!isset($container['view/mustache/helpers'])) { - $container['view/mustache/helpers'] = function () { - return []; - }; + $container['view/mustache/helpers'] = (fn(): array => []); } /** @@ -477,7 +457,7 @@ protected function registerMustacheHelpersServices(Container $container): void * * @return array */ - $container->extend('view/mustache/helpers', function (array $helpers, Container $container) { + $container->extend('view/mustache/helpers', function (array $helpers, Container $container): array { $baseUrl = $container['base-url']; $urls = [ /** @@ -504,8 +484,8 @@ protected function registerMustacheHelpersServices(Container $container): void * @param string $uri A URI path to wrap. * @return UriInterface|null */ - 'withBaseUrl' => function ($uri, LambdaHelper $helper = null) use ($baseUrl) { - if ($helper) { + 'withBaseUrl' => function ($uri, ?LambdaHelper $helper = null) use ($baseUrl) { + if ($helper instanceof \Mustache_LambdaHelper) { $uri = $helper->render($uri); } @@ -514,24 +494,19 @@ protected function registerMustacheHelpersServices(Container $container): void $uri = $baseUrl->withPath(''); } else { $parts = parse_url($uri); - if (!isset($parts['scheme'])) { - if (!in_array($uri[0], [ '/', '#', '?' ])) { - $path = isset($parts['path']) ? $parts['path'] : ''; - $query = isset($parts['query']) ? $parts['query'] : ''; - $hash = isset($parts['fragment']) ? $parts['fragment'] : ''; - - $uri = $baseUrl->withPath($path) - ->withQuery($query) - ->withFragment($hash); - } + if (!isset($parts['scheme']) && !in_array($uri[0], [ '/', '#', '?' ])) { + $path = $parts['path'] ?? ''; + $query = $parts['query'] ?? ''; + $hash = $parts['fragment'] ?? ''; + $uri = $baseUrl->withPath($path) + ->withQuery($query) + ->withFragment($hash); } } return $uri; }, - 'renderContext' => function ($text, LambdaHelper $helper = null) { - return $helper->render('{{>' . $helper->render($text) . '}}'); - }, + 'renderContext' => fn($text, ?LambdaHelper $helper = null) => $helper->render('{{>' . $helper->render($text) . '}}'), ]; return array_merge($helpers, $urls); @@ -540,14 +515,11 @@ protected function registerMustacheHelpersServices(Container $container): void /** * @param Container $container The DI container. - * @return void */ protected function registerTwigHelpersServices(Container $container): void { if (!isset($container['view/twig/helpers'])) { - $container['view/twig/helpers'] = function () { - return []; - }; + $container['view/twig/helpers'] = (fn(): array => []); } /** @@ -555,22 +527,18 @@ protected function registerTwigHelpersServices(Container $container): void * * @return TwigUrlHelpers */ - $container['view/twig/helpers/url'] = function (Container $container): TwigHelpersInterface { - return new TwigUrlHelpers([ - 'baseUrl' => $container['base-url'], - ]); - }; + $container['view/twig/helpers/url'] = (fn(Container $container): TwigHelpersInterface => new TwigUrlHelpers([ + 'baseUrl' => $container['base-url'], + ])); /** * Debug helpers for Twig. * * @return TwigDebugHelpers */ - $container['view/twig/helpers/debug'] = function (Container $container): TwigHelpersInterface { - return new TwigDebugHelpers([ - 'debug' => $container['debug'], - ]); - }; + $container['view/twig/helpers/debug'] = (fn(Container $container): TwigHelpersInterface => new TwigDebugHelpers([ + 'debug' => $container['debug'], + ])); /** * Extend global helpers for the Twig Engine. @@ -579,12 +547,10 @@ protected function registerTwigHelpersServices(Container $container): void * @param Container $container A container instance. * @return array */ - $container->extend('view/twig/helpers', function (array $helpers, Container $container): array { - return array_merge( - $helpers, - $container['view/twig/helpers/url']->toArray(), - $container['view/twig/helpers/debug']->toArray(), - ); - }); + $container->extend('view/twig/helpers', fn(array $helpers, Container $container): array => array_merge( + $helpers, + $container['view/twig/helpers/url']->toArray(), + $container['view/twig/helpers/debug']->toArray(), + )); } } diff --git a/packages/app/src/Charcoal/App/ServiceProvider/DatabaseServiceProvider.php b/packages/app/src/Charcoal/App/ServiceProvider/DatabaseServiceProvider.php index fb1f6c3ab..f541efce4 100644 --- a/packages/app/src/Charcoal/App/ServiceProvider/DatabaseServiceProvider.php +++ b/packages/app/src/Charcoal/App/ServiceProvider/DatabaseServiceProvider.php @@ -32,15 +32,14 @@ class DatabaseServiceProvider implements ServiceProviderInterface * It should not get services. * * @param Container $container A service container. - * @return void */ - public function register(Container $container) + public function register(Container $container): void { /** * @param Container $container A service container. * @return Container A map of database configsets. */ - $container['databases/config'] = function (Container $container) { + $container['databases/config'] = function (Container $container): \Pimple\Container { $databases = ($container['config']['databases'] ?? []); $configs = new Container(); @@ -48,9 +47,7 @@ public function register(Container $container) /** * @return DatabaseConfig */ - $configs[$dbIdent] = function () use ($dbOptions) { - return new DatabaseConfig($dbOptions); - }; + $configs[$dbIdent] = (fn(): \Charcoal\App\Config\DatabaseConfig => new DatabaseConfig($dbOptions)); } return $configs; @@ -60,7 +57,7 @@ public function register(Container $container) * @param Container $container A service container. * @return Container A map of database handlers. */ - $container['databases'] = function (Container $container) { + $container['databases'] = function (Container $container): \Pimple\Container { $databases = ($container['config']['databases'] ?? []); $dbs = new Container(); @@ -68,7 +65,7 @@ public function register(Container $container) /** * @return PDO */ - $dbs[$dbIdent] = function () use ($dbIdent, $container) { + $dbs[$dbIdent] = function () use ($dbIdent, $container): \PDO { $dbConfig = $container['databases/config'][$dbIdent]; $type = $dbConfig['type']; @@ -82,21 +79,17 @@ public function register(Container $container) $extraOptions = null; if (!isset($dbConfig['disable_utf8']) || !$dbConfig['disable_utf8']) { $extraOptions = [ - PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4', + \Pdo\Mysql::ATTR_INIT_COMMAND => 'SET NAMES utf8mb4', ]; } - if ($type === 'sqlite') { - $dsn = $type . ':' . $database; - } else { - $dsn = $type . ':host=' . $host . ';dbname=' . $database; - } + $dsn = $type === 'sqlite' ? $type . ':' . $database : $type . ':host=' . $host . ';dbname=' . $database; $db = new PDO($dsn, $username, $password, $extraOptions); $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); if ($type === 'mysql') { - $db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true); + $db->setAttribute(\Pdo\Mysql::ATTR_USE_BUFFERED_QUERY, true); } return $db; diff --git a/packages/app/src/Charcoal/App/ServiceProvider/FilesystemServiceProvider.php b/packages/app/src/Charcoal/App/ServiceProvider/FilesystemServiceProvider.php index 48b93ae39..2b050c6ba 100644 --- a/packages/app/src/Charcoal/App/ServiceProvider/FilesystemServiceProvider.php +++ b/packages/app/src/Charcoal/App/ServiceProvider/FilesystemServiceProvider.php @@ -33,15 +33,14 @@ class FilesystemServiceProvider implements ServiceProviderInterface { /** * @param Container $container A service container. - * @return void */ - public function register(Container $container) + public function register(Container $container): void { /** * @param Container $container A service container. * @return FilesystemConfig */ - $container['filesystem/config'] = function (Container $container) { + $container['filesystem/config'] = function (Container $container): \Charcoal\App\Config\FilesystemConfig { $fsConfig = ($container['config']['filesystem'] ?? null); return new FilesystemConfig($fsConfig); }; @@ -50,15 +49,13 @@ public function register(Container $container) * @param Container $container A service container. * @return MountManager */ - $container['filesystem/manager'] = function () { - return new MountManager(); - }; + $container['filesystem/manager'] = (fn(): \League\Flysystem\MountManager => new MountManager()); /** * @param Container $container A service container. * @return array */ - $container['filesystems'] = function (Container $container) { + $container['filesystems'] = function (Container $container): \Pimple\Container { $filesystemConfig = $container['filesystem/config']; $filesystems = new Container(); @@ -77,9 +74,8 @@ public function register(Container $container) * @param Container $container A service container. * @throws Exception If the filesystem type is not defined in config. * @throws UnexpectedValueException If the filesystem type is invalid / unsupported. - * @return Filesystem */ - private function createConnection(array $config, Container $container) + private function createConnection(array $config, Container $container): \League\Flysystem\Filesystem { if (!isset($config['type'])) { throw new Exception( @@ -89,36 +85,17 @@ private function createConnection(array $config, Container $container) $type = $config['type']; - switch ($type) { - case 'local': - $adapter = $this->createLocalAdapter($config, $container); - break; - - case 's3': - $adapter = $this->createS3Adapter($config); - break; - - case 'ftp': - $adapter = $this->createFtpAdapter($config); - break; - - case 'sftp': - $adapter = $this->createSftpAdapter($config); - break; - - case 'memory': - $adapter = $this->createMemoryAdapter(); - break; - - case 'noop': - $adapter = $this->createNullAdapter(); - break; - - default: - throw new UnexpectedValueException( - sprintf('Invalid filesystem type "%s"', $type) - ); - } + $adapter = match ($type) { + 'local' => $this->createLocalAdapter($config, $container), + 's3' => $this->createS3Adapter($config), + 'ftp' => $this->createFtpAdapter($config), + 'sftp' => $this->createSftpAdapter($config), + 'memory' => $this->createMemoryAdapter(), + 'noop' => $this->createNullAdapter(), + default => throw new UnexpectedValueException( + sprintf('Invalid filesystem type "%s"', $type) + ), + }; return new Filesystem($adapter); } @@ -127,9 +104,8 @@ private function createConnection(array $config, Container $container) * @param array $config The driver (adapter) configuration. * @param Container $container A service container. * @throws InvalidArgumentException If the path is not defined. - * @return LocalAdapter */ - private function createLocalAdapter(array $config, Container $container) + private function createLocalAdapter(array $config, Container $container): \League\Flysystem\Adapter\Local { if (empty($config['path'])) { throw new InvalidArgumentException( @@ -138,10 +114,8 @@ private function createLocalAdapter(array $config, Container $container) } $path = $config['path']; - if (is_string($path)) { - if (isset($container['config']) && ($container['config'] instanceof AppConfig)) { - $path = $container['config']->resolveValue($path); - } + if (is_string($path) && (isset($container['config']) && $container['config'] instanceof AppConfig)) { + $path = $container['config']->resolveValue($path); } $defaults = [ @@ -157,9 +131,8 @@ private function createLocalAdapter(array $config, Container $container) /** * @param array $config The driver (adapter) configuration. * @throws InvalidArgumentException If the key, secret or bucket is not defined in config. - * @return AwsS3Adapter */ - private function createS3Adapter(array $config) + private function createS3Adapter(array $config): \League\Flysystem\AwsS3v3\AwsS3Adapter { if (!isset($config['key']) || !$config['key']) { throw new InvalidArgumentException( @@ -195,13 +168,9 @@ private function createS3Adapter(array $config) 'version' => $config['version'], ]); - if (isset($config['public']) && !$config['public']) { - $permissions = null; - } else { - $permissions = [ - 'ACL' => 'public-read', - ]; - } + $permissions = isset($config['public']) && !$config['public'] ? null : [ + 'ACL' => 'public-read', + ]; return new AwsS3Adapter($client, $config['bucket'], $config['prefix'], $permissions); } @@ -209,9 +178,8 @@ private function createS3Adapter(array $config) /** * @param array $config The driver (adapter) configuration. * @throws InvalidArgumentException If the host, username or password is not defined in config. - * @return FtpAdapter */ - private function createFtpAdapter(array $config) + private function createFtpAdapter(array $config): \League\Flysystem\Adapter\Ftp { if (!$config['host']) { throw new InvalidArgumentException( @@ -246,9 +214,8 @@ private function createFtpAdapter(array $config) /** * @param array $config The driver (adapter) configuration. * @throws InvalidArgumentException If the host, username or password is not defined in config. - * @return SftpAdapter */ - private function createSftpAdapter(array $config) + private function createSftpAdapter(array $config): \League\Flysystem\Sftp\SftpAdapter { if (!$config['host']) { throw new InvalidArgumentException( @@ -279,18 +246,12 @@ private function createSftpAdapter(array $config) return new SftpAdapter($config); } - /** - * @return MemoryAdapter - */ - private function createMemoryAdapter() + private function createMemoryAdapter(): \League\Flysystem\Memory\MemoryAdapter { return new MemoryAdapter(); } - /** - * @return NullAdapter - */ - private function createNullAdapter() + private function createNullAdapter(): \League\Flysystem\Adapter\NullAdapter { return new NullAdapter(); } diff --git a/packages/app/src/Charcoal/App/ServiceProvider/LoggerServiceProvider.php b/packages/app/src/Charcoal/App/ServiceProvider/LoggerServiceProvider.php index ee36758b3..3f4113899 100644 --- a/packages/app/src/Charcoal/App/ServiceProvider/LoggerServiceProvider.php +++ b/packages/app/src/Charcoal/App/ServiceProvider/LoggerServiceProvider.php @@ -44,15 +44,14 @@ class LoggerServiceProvider implements ServiceProviderInterface * It should not get services. * * @param Container $container A service container. - * @return void */ - public function register(Container $container) + public function register(Container $container): void { /** * @param Container $container A service container. * @return LoggerConfig */ - $container['logger/config'] = function (Container $container) { + $container['logger/config'] = function (Container $container): \Charcoal\App\Config\LoggerConfig { $loggerConfig = ($container['config']['logger'] ?? null); return new LoggerConfig($loggerConfig); }; @@ -62,7 +61,7 @@ public function register(Container $container) * @throws InvalidArgumentException If the path is not defined or invalid. * @return StreamHandler|null */ - $container['logger/handler/stream'] = function (Container $container) { + $container['logger/handler/stream'] = function (Container $container): ?\Monolog\Handler\StreamHandler { $loggerConfig = $container['logger/config']; $handlerConfig = $loggerConfig['handlers.stream']; @@ -77,10 +76,8 @@ public function register(Container $container) } $stream = $handlerConfig['stream']; - if (is_string($stream)) { - if (isset($container['config']) && ($container['config'] instanceof AppConfig)) { - $stream = $container['config']->resolveValue($stream); - } + if (is_string($stream) && (isset($container['config']) && $container['config'] instanceof AppConfig)) { + $stream = $container['config']->resolveValue($stream); } $level = $handlerConfig['level'] ?: $loggerConfig['level']; @@ -91,7 +88,7 @@ public function register(Container $container) * @param Container $container A service container. * @return BrowserConsoleHandler|null */ - $container['logger/handler/browser-console'] = function (Container $container) { + $container['logger/handler/browser-console'] = function (Container $container): ?\Monolog\Handler\BrowserConsoleHandler { $loggerConfig = $container['logger/config']; $handlerConfig = $loggerConfig['handlers.console']; @@ -109,7 +106,7 @@ public function register(Container $container) * @param Container $container A service container. * @return LoggerInterface */ - $container['logger'] = function (Container $container) { + $container['logger'] = function (Container $container): \Psr\Log\NullLogger|\Monolog\Logger { $loggerConfig = $container['logger/config']; if ($loggerConfig['active'] !== true) { diff --git a/packages/app/src/Charcoal/App/ServiceProvider/ScriptServiceProvider.php b/packages/app/src/Charcoal/App/ServiceProvider/ScriptServiceProvider.php index a7c0ec6f2..73dda4365 100644 --- a/packages/app/src/Charcoal/App/ServiceProvider/ScriptServiceProvider.php +++ b/packages/app/src/Charcoal/App/ServiceProvider/ScriptServiceProvider.php @@ -1,5 +1,7 @@ ScriptInterface::class, - 'resolver_options' => [ - 'suffix' => 'Script', - ], - 'arguments' => [ - [ - 'container' => $container, - 'logger' => $container['logger'], - 'climate' => $container['script/climate'], - 'climate_reader' => $container['script/climate/reader'], - ], + $container['script/factory'] = (fn(Container $container): \Charcoal\Factory\GenericFactory => new Factory([ + 'base_class' => ScriptInterface::class, + 'resolver_options' => [ + 'suffix' => 'Script', + ], + 'arguments' => [ + [ + 'container' => $container, + 'logger' => $container['logger'], + 'climate' => $container['script/climate'], + 'climate_reader' => $container['script/climate/reader'], ], - ]); - }; + ], + ])); } /** * @param Container $container A service container. - * @return void */ - private function registerClimate(Container $container) + private function registerClimate(Container $container): void { /** * @param Container $container A service container. * @return \League\CLImate\Util\Reader\ReaderInterface|null */ - $container['script/climate/reader'] = function () { - return null; - }; + $container['script/climate/reader'] = (fn(): null => null); /** * @param Container $container A service container. * @return CLImate */ - $container['script/climate'] = function () { - $climate = new CLImate(); - return $climate; - }; + $container['script/climate'] = (fn(): \League\CLImate\CLImate => new CLImate()); } } diff --git a/packages/app/src/Charcoal/App/Template/AbstractTemplate.php b/packages/app/src/Charcoal/App/Template/AbstractTemplate.php index 1d156e67e..92df1a863 100644 --- a/packages/app/src/Charcoal/App/Template/AbstractTemplate.php +++ b/packages/app/src/Charcoal/App/Template/AbstractTemplate.php @@ -49,14 +49,14 @@ public function __construct($data = null) */ public function templateName() { - $key = substr(strrchr('\\' . get_class($this), '\\'), 1); + $key = substr(strrchr('\\' . static::class, '\\'), 1); if (!isset(static::$templateNameCache[$key])) { $value = $key; if (!ctype_lower($value)) { $value = preg_replace('/\s+/u', '', $value); - $value = mb_strtolower(preg_replace('/(.)(?=[A-Z])/u', '$1-', $value), 'UTF-8'); + $value = mb_strtolower((string) preg_replace('/(.)(?=[A-Z])/u', '$1-', (string) $value), 'UTF-8'); } $value = str_replace( diff --git a/packages/app/src/Charcoal/App/Template/AbstractWidget.php b/packages/app/src/Charcoal/App/Template/AbstractWidget.php index 2721ee557..792aa82d6 100644 --- a/packages/app/src/Charcoal/App/Template/AbstractWidget.php +++ b/packages/app/src/Charcoal/App/Template/AbstractWidget.php @@ -26,10 +26,7 @@ abstract class AbstractWidget extends AbstractEntity implements use LoggerAwareTrait; use ViewableTrait; - /** - * @var boolean $active - */ - private $active = true; + private bool $active = true; /** * @param array|\ArrayAccess $data Optional dependencies. @@ -49,7 +46,7 @@ public function __construct($data = null) */ public function setActive($active) { - $this->active = !!$active; + $this->active = (bool) $active; return $this; } diff --git a/packages/app/src/Charcoal/App/Template/GenericTemplate.php b/packages/app/src/Charcoal/App/Template/GenericTemplate.php index 3a9d77ba5..cf5c7d1bc 100644 --- a/packages/app/src/Charcoal/App/Template/GenericTemplate.php +++ b/packages/app/src/Charcoal/App/Template/GenericTemplate.php @@ -1,5 +1,7 @@ factory = $factory; - $this->container = $container; } /** diff --git a/packages/app/src/Charcoal/App/Template/WidgetInterface.php b/packages/app/src/Charcoal/App/Template/WidgetInterface.php index 23afe7e5b..3ec065347 100644 --- a/packages/app/src/Charcoal/App/Template/WidgetInterface.php +++ b/packages/app/src/Charcoal/App/Template/WidgetInterface.php @@ -1,5 +1,7 @@ obj->setData([ 'mode' => 'redirect', @@ -63,7 +59,7 @@ public function testSetData() $this->assertEquals('fail', $this->obj->failureUrl()); } - public function testSetMode() + public function testSetMode(): void { $this->assertEquals('json', $this->obj->mode()); $ret = $this->obj->setMode('redirect'); @@ -74,7 +70,7 @@ public function testSetMode() $this->obj->setMode(false); } - public function testSetSuccess() + public function testSetSuccess(): void { $ret = $this->obj->setSuccess(false); $this->assertSame($ret, $this->obj); @@ -86,7 +82,7 @@ public function testSetSuccess() $this->assertTrue($this->obj->success()); } - public function testSuccessUrl() + public function testSuccessUrl(): void { $this->assertEquals('', $this->obj->successUrl()); $ret = $this->obj->setSuccessUrl('foo'); @@ -99,7 +95,7 @@ public function testSuccessUrl() $this->obj->setSuccessUrl([]); } - public function testSetFailureUrl() + public function testSetFailureUrl(): void { $this->assertEquals('', $this->obj->failureUrl()); $ret = $this->obj->setFailureUrl('foo'); @@ -112,7 +108,7 @@ public function testSetFailureUrl() $this->obj->setFailureUrl([]); } - public function testRedirectUrlSuccess() + public function testRedirectUrlSuccess(): void { $this->obj->setData([ 'failure_url' => 'fail', @@ -130,9 +126,9 @@ public function testRedirectUrlSuccess() * * For this, the `run` method must be added as public in the mock object. */ - public function testInvokable() + public function testInvokable(): void { - $request = $this->createMock(RequestInterface::class); + $request = $this->createStub(RequestInterface::class); $response = new Response(); $this->obj->expects($this->any()) @@ -145,9 +141,9 @@ public function testInvokable() $this->assertInstanceOf(Response::class, $res); } - public function testDefaultModeisJson() + public function testDefaultModeisJson(): void { - $request = $this->createMock(RequestInterface::class); + $request = $this->createStub(RequestInterface::class); $response = new Response(); $this->obj->expects($this->any()) @@ -161,9 +157,9 @@ public function testDefaultModeisJson() $this->assertEquals('application/json', $headers['Content-Type'][0]); } - public function testInvokeModeJson() + public function testInvokeModeJson(): void { - $request = $this->createMock(RequestInterface::class); + $request = $this->createStub(RequestInterface::class); $response = new Response(); $this->obj->expects($this->any()) @@ -178,9 +174,9 @@ public function testInvokeModeJson() $this->assertEquals('application/json', $headers['Content-Type'][0]); } - public function testInvokeModeXml() + public function testInvokeModeXml(): void { - $request = $this->createMock(RequestInterface::class); + $request = $this->createStub(RequestInterface::class); $response = new Response(); $this->obj->expects($this->any()) @@ -195,9 +191,9 @@ public function testInvokeModeXml() $this->assertEquals('text/xml', $headers['Content-Type'][0]); } - public function testInvokeModeRedirect() + public function testInvokeModeRedirect(): void { - $request = $this->createMock(RequestInterface::class); + $request = $this->createStub(RequestInterface::class); $response = new Response(); $this->obj->expects($this->any()) @@ -215,20 +211,18 @@ public function testInvokeModeRedirect() $this->assertEquals('example.com', $headers['Location'][0]); } - public function testInitIsTrue() + public function testInitIsTrue(): void { - $request = $this->createMock(RequestInterface::class); + $request = $this->createStub(RequestInterface::class); $this->assertTrue($this->obj->init($request)); } /** * Set up the service container. - * - * @return Container */ - private function container() + private function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerLogger($container); diff --git a/packages/app/tests/Charcoal/App/AppConfigTest.php b/packages/app/tests/Charcoal/App/AppConfigTest.php index b699bd4d9..7047f7255 100644 --- a/packages/app/tests/Charcoal/App/AppConfigTest.php +++ b/packages/app/tests/Charcoal/App/AppConfigTest.php @@ -1,5 +1,7 @@ assertInstanceOf(AppConfig::class, $obj); diff --git a/packages/app/tests/Charcoal/App/AppTest.php b/packages/app/tests/Charcoal/App/AppTest.php index 65984146d..6d150b1db 100644 --- a/packages/app/tests/Charcoal/App/AppTest.php +++ b/packages/app/tests/Charcoal/App/AppTest.php @@ -1,5 +1,7 @@ obj = new App($container); } - public function testAppIsConstructed() + public function testAppIsConstructed(): void { $app = new App(); $this->assertInstanceOf(App::class, $app); } - public function testConstructor() + public function testConstructor(): void { $this->assertInstanceOf(App::class, $this->obj); } - public function testRun() + public function testRun(): void { $res = $this->obj->run(true); $this->assertInstanceOf(ResponseInterface::class, $res); diff --git a/packages/app/tests/Charcoal/App/Config/DatabaseConfigTest.php b/packages/app/tests/Charcoal/App/Config/DatabaseConfigTest.php index 980014fe5..9b9475041 100644 --- a/packages/app/tests/Charcoal/App/Config/DatabaseConfigTest.php +++ b/packages/app/tests/Charcoal/App/Config/DatabaseConfigTest.php @@ -17,7 +17,7 @@ public function setUp(): void $this->obj = new DatabaseConfig(); } - public function testDefaults() + public function testDefaults(): void { $this->assertEquals('mysql', $this->obj->type()); $this->assertEquals('localhost', $this->obj->hostname()); @@ -26,7 +26,7 @@ public function testDefaults() $this->assertFalse($this->obj->disableUtf8()); } - public function testSetType() + public function testSetType(): void { $ret = $this->obj->setType('sqlite'); $this->assertSame($ret, $this->obj); @@ -36,7 +36,7 @@ public function testSetType() $this->obj->setType([]); } - public function testSetHostname() + public function testSetHostname(): void { $ret = $this->obj->setHostname('foo'); $this->assertSame($ret, $this->obj); @@ -46,7 +46,7 @@ public function testSetHostname() $this->obj->setHostname([]); } - public function testSetUsername() + public function testSetUsername(): void { $ret = $this->obj->setUsername('foobar'); $this->assertSame($ret, $this->obj); @@ -56,7 +56,7 @@ public function testSetUsername() $this->obj->setUsername([]); } - public function testSetPassword() + public function testSetPassword(): void { $ret = $this->obj->setPassword('baz'); $this->assertSame($ret, $this->obj); @@ -66,7 +66,7 @@ public function testSetPassword() $this->obj->setPassword([]); } - public function testSetDatabase() + public function testSetDatabase(): void { $ret = $this->obj->setDatabase('barbaz'); $this->assertSame($ret, $this->obj); @@ -76,7 +76,7 @@ public function testSetDatabase() $this->obj->setDatabase([]); } - public function testSetDIsableUtf8() + public function testSetDIsableUtf8(): void { $ret = $this->obj->setDIsableUtf8(true); $this->assertSame($ret, $this->obj); diff --git a/packages/app/tests/Charcoal/App/Config/FilesystemConfigTest.php b/packages/app/tests/Charcoal/App/Config/FilesystemConfigTest.php index 1c8357bab..5f0a4cc02 100644 --- a/packages/app/tests/Charcoal/App/Config/FilesystemConfigTest.php +++ b/packages/app/tests/Charcoal/App/Config/FilesystemConfigTest.php @@ -17,7 +17,7 @@ public function setUp(): void $this->obj = new FilesystemConfig(); } - public function testDefaultConnections() + public function testDefaultConnections(): void { $this->assertArrayHasKey('private', $this->obj->defaultConnections()); $this->assertArrayHasKey('public', $this->obj->defaultConnections()); @@ -26,7 +26,7 @@ public function testDefaultConnections() $this->assertArrayHasKey('public', $this->obj->connections()); } - public function testConnectionsAlwaysHaveDefaultConnections() + public function testConnectionsAlwaysHaveDefaultConnections(): void { $this->obj->setData([ 'connections' => [ diff --git a/packages/app/tests/Charcoal/App/Config/LoggerConfigTest.php b/packages/app/tests/Charcoal/App/Config/LoggerConfigTest.php index 3a71a17e6..8d71ac2b3 100644 --- a/packages/app/tests/Charcoal/App/Config/LoggerConfigTest.php +++ b/packages/app/tests/Charcoal/App/Config/LoggerConfigTest.php @@ -25,7 +25,7 @@ public function setUp(): void $this->obj = new LoggerConfig(); } - public function testDefaults() + public function testDefaults(): void { $this->assertEquals('charcoal', LoggerConfig::DEFAULT_CHANNEL); @@ -46,14 +46,14 @@ public function testDefaults() $this->assertEquals(LoggerConfig::DEFAULT_CHANNEL, $this->obj->channel()); } - public function testSetActive() + public function testSetActive(): void { $ret = $this->obj->setActive(false); $this->assertSame($ret, $this->obj); $this->assertFalse($this->obj->active()); } - public function testSetHandlers() + public function testSetHandlers(): void { $ret = $this->obj->setHandlers([ 'errlog' => [ 'type' => 'error-log' ], [ 'type' => 'mail' ] ]); $this->assertSame($ret, $this->obj); @@ -68,7 +68,7 @@ public function testSetHandlers() $this->obj->setHandlers([ [ 'foo' => 'baz' ] ]); } - public function testSetProcessors() + public function testSetProcessors(): void { $ret = $this->obj->setProcessors([ 'web' => [ 'type' => 'web' ], [ 'type' => 'process-id' ] ]); $this->assertSame($ret, $this->obj); @@ -83,7 +83,7 @@ public function testSetProcessors() $this->obj->setProcessors([ [ 'foo' => 'baz' ] ]); } - public function testSetChannel() + public function testSetChannel(): void { $ret = $this->obj->setChannel('foo'); $this->assertSame($ret, $this->obj); diff --git a/packages/app/tests/Charcoal/App/ContainerProvider.php b/packages/app/tests/Charcoal/App/ContainerProvider.php index f4286074c..cb24697d5 100644 --- a/packages/app/tests/Charcoal/App/ContainerProvider.php +++ b/packages/app/tests/Charcoal/App/ContainerProvider.php @@ -59,9 +59,8 @@ class ContainerProvider * Register the unit tests required services. * * @param Container $container A DI container. - * @return void */ - public function registerBaseServices(Container $container) + public function registerBaseServices(Container $container): void { $this->registerConfig($container); $this->registerBaseUrl($container); @@ -74,57 +73,47 @@ public function registerBaseServices(Container $container) * Setup the application's base URI. * * @param Container $container A DI container. - * @return void */ - public function registerBaseUrl(Container $container) + public function registerBaseUrl(Container $container): void { - $container['base-url'] = function (Container $container) { - return Uri::createFromString('https://example.com:8080/foo/bar?abc=123'); - }; + $container['base-url'] = (fn(Container $container) => Uri::createFromString('https://example.com:8080/foo/bar?abc=123')); } /** * Setup the application configset. * * @param Container $container A DI container. - * @return void */ - public function registerConfig(Container $container) + public function registerConfig(Container $container): void { - $container['config'] = function (Container $container) { - return new AppConfig([ - 'base_path' => realpath(__DIR__ . '/../../..'), - ]); - }; + $container['config'] = (fn(Container $container): \Charcoal\App\AppConfig => new AppConfig([ + 'base_path' => realpath(__DIR__ . '/../../..'), + ])); } - public function registerWidgetFactory(Container $container) + public function registerWidgetFactory(Container $container): void { $this->registerLogger($container); - $container['widget/factory'] = function (Container $container) { - return new Factory([ - 'resolver_options' => [ - 'suffix' => 'Widget' - ], - 'arguments' => [[ - 'container' => $container, - 'logger' => $container['logger'] - ]] - ]); - }; + $container['widget/factory'] = (fn(Container $container): \Charcoal\Factory\GenericFactory => new Factory([ + 'resolver_options' => [ + 'suffix' => 'Widget' + ], + 'arguments' => [[ + 'container' => $container, + 'logger' => $container['logger'] + ]] + ])); } - public function registerWidgetBuilder(Container $container) + public function registerWidgetBuilder(Container $container): void { $this->registerWidgetFactory($container); - $container['widget/builder'] = function (Container $container) { - return new WidgetBuilder($container['widget/factory'], $container); - }; + $container['widget/builder'] = (fn(Container $container): \Charcoal\App\Template\WidgetBuilder => new WidgetBuilder($container['widget/factory'], $container)); } - public function registerClimate(Container $container) + public function registerClimate(Container $container): void { $container['climate/system'] = function (Container $container) { $system = Mockery::mock(Linux::class); @@ -151,11 +140,9 @@ public function registerClimate(Container $container) return $reader; }; - $container['climate/util'] = function (Container $container) { - return new UtilFactory($container['climate/system']); - }; + $container['climate/util'] = (fn(Container $container): \League\CLImate\Util\UtilFactory => new UtilFactory($container['climate/system'])); - $container['climate'] = function (Container $container) { + $container['climate'] = function (Container $container): \League\CLImate\CLImate { $climate = new CLImate(); $climate->setOutput($container['climate/output']); @@ -170,162 +157,136 @@ public function registerClimate(Container $container) * Setup the framework's view renderer. * * @param Container $container A DI container. - * @return void */ - public function registerView(Container $container) + public function registerView(Container $container): void { - $container['view/loader'] = function (Container $container) { - return new MustacheLoader([ - 'logger' => $container['logger'], - 'base_path' => $container['config']['base_path'], - 'paths' => [ - 'views' - ] - ]); - }; - - $container['view/engine'] = function (Container $container) { - return new MustacheEngine([ - 'logger' => $container['logger'], - 'cache' => MustacheEngine::DEFAULT_CACHE_PATH, - 'loader' => $container['view/loader'] - ]); - }; - - $container['view'] = function (Container $container) { - return new GenericView([ - 'logger' => $container['logger'], - 'engine' => $container['view/engine'] - ]); - }; + $container['view/loader'] = (fn(Container $container): \Charcoal\View\Mustache\MustacheLoader => new MustacheLoader([ + 'logger' => $container['logger'], + 'base_path' => $container['config']['base_path'], + 'paths' => [ + 'views' + ] + ])); + + $container['view/engine'] = (fn(Container $container): \Charcoal\View\Mustache\MustacheEngine => new MustacheEngine([ + 'logger' => $container['logger'], + 'cache' => MustacheEngine::DEFAULT_CACHE_PATH, + 'loader' => $container['view/loader'] + ])); + + $container['view'] = (fn(Container $container): \Charcoal\View\GenericView => new GenericView([ + 'logger' => $container['logger'], + 'engine' => $container['view/engine'] + ])); } /** * Setup the application's translator service. * * @param Container $container A DI container. - * @return void */ - public function registerTranslator(Container $container) + public function registerTranslator(Container $container): void { - $container['locales/manager'] = function (Container $container) { - return new LocalesManager([ - 'locales' => [ - 'en' => [ 'locale' => 'en-US' ] - ] - ]); - }; - - $container['translator'] = function (Container $container) { - return new Translator([ - 'manager' => $container['locales/manager'] - ]); - }; + $container['locales/manager'] = (fn(Container $container): \Charcoal\Translator\LocalesManager => new LocalesManager([ + 'locales' => [ + 'en' => [ 'locale' => 'en-US' ] + ] + ])); + + $container['translator'] = (fn(Container $container): \Charcoal\Translator\Translator => new Translator([ + 'manager' => $container['locales/manager'] + ])); } /** * Setup the application's logging interface. * * @param Container $container A DI container. - * @return void */ - public function registerLogger(Container $container) + public function registerLogger(Container $container): void { - $container['logger'] = function (Container $container) { - return new NullLogger(); - }; + $container['logger'] = (fn(Container $container): \Psr\Log\NullLogger => new NullLogger()); } /** * Setup the application's caching interface. * * @param Container $container A DI container. - * @return void */ - public function registerCache(Container $container) + public function registerCache(Container $container): void { - $container['cache/config'] = function (Container $container) { - return new CacheConfig(); - }; + $container['cache/config'] = (fn(Container $container): \Charcoal\Cache\CacheConfig => new CacheConfig()); - $container['cache'] = function ($container) { - return new Pool(); - }; + $container['cache'] = (fn($container): \Stash\Pool => new Pool()); } - public function registerDatabase(Container $container) + public function registerDatabase(Container $container): void { - $container['database'] = function (Container $container) { + $container['database'] = function (Container $container): \PDO { $pdo = new PDO('sqlite::memory:'); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); return $pdo; }; } - public function registerMetadataLoader(Container $container) + public function registerMetadataLoader(Container $container): void { $this->registerLogger($container); $this->registerCache($container); - $container['metadata/loader'] = function (Container $container) { - return new MetadataLoader([ - 'logger' => $container['logger'], - 'cache' => $container['cache'], - 'base_path' => $container['config']['base_path'], - 'paths' => [ - 'metadata', - // Standalone - 'vendor/charcoal/object/metadata', - 'vendor/charcoal/user/metadata', - // Monorepo - '/../object/metadata', - '/../user/metadata' - ] - ]); - }; + $container['metadata/loader'] = (fn(Container $container): \Charcoal\Model\Service\MetadataLoader => new MetadataLoader([ + 'logger' => $container['logger'], + 'cache' => $container['cache'], + 'base_path' => $container['config']['base_path'], + 'paths' => [ + 'metadata', + // Standalone + 'vendor/charcoal/object/metadata', + 'vendor/charcoal/user/metadata', + // Monorepo + '/../object/metadata', + '/../user/metadata' + ] + ])); } - public function registerSourceFactory(Container $container) + public function registerSourceFactory(Container $container): void { $this->registerLogger($container); $this->registerDatabase($container); - $container['source/factory'] = function (Container $container) { - return new Factory([ - 'map' => [ - 'database' => DatabaseSource::class - ], - 'arguments' => [[ - 'logger' => $container['logger'], - 'pdo' => $container['database'] - ]] - ]); - }; + $container['source/factory'] = (fn(Container $container): \Charcoal\Factory\GenericFactory => new Factory([ + 'map' => [ + 'database' => DatabaseSource::class + ], + 'arguments' => [[ + 'logger' => $container['logger'], + 'pdo' => $container['database'] + ]] + ])); } - public function registerPropertyFactory(Container $container) + public function registerPropertyFactory(Container $container): void { $this->registerTranslator($container); $this->registerDatabase($container); $this->registerLogger($container); - $container['property/factory'] = function (Container $container) { - return new Factory([ - 'resolver_options' => [ - 'prefix' => '\\Charcoal\\Property\\', - 'suffix' => 'Property' - ], - 'arguments' => [[ - 'container' => $container, - 'database' => $container['database'], - 'translator' => $container['translator'], - 'logger' => $container['logger'] - ]] - ]); - }; + $container['property/factory'] = (fn(Container $container): \Charcoal\Factory\GenericFactory => new Factory([ + 'resolver_options' => [ + 'prefix' => '\\Charcoal\\Property\\', + 'suffix' => 'Property' + ], + 'arguments' => [[ + 'container' => $container, + 'database' => $container['database'], + 'translator' => $container['translator'], + 'logger' => $container['logger'] + ]] + ])); } - public function registerModelFactory(Container $container) + public function registerModelFactory(Container $container): void { $this->registerLogger($container); $this->registerTranslator($container); @@ -333,51 +294,45 @@ public function registerModelFactory(Container $container) $this->registerPropertyFactory($container); $this->registerSourceFactory($container); - $container['model/factory'] = function (Container $container) { - return new Factory([ - 'arguments' => [[ - 'container' => $container, - 'logger' => $container['logger'], - 'metadata_loader' => $container['metadata/loader'], - 'property_factory' => $container['property/factory'], - 'source_factory' => $container['source/factory'] - ]] - ]); - }; + $container['model/factory'] = (fn(Container $container): \Charcoal\Factory\GenericFactory => new Factory([ + 'arguments' => [[ + 'container' => $container, + 'logger' => $container['logger'], + 'metadata_loader' => $container['metadata/loader'], + 'property_factory' => $container['property/factory'], + 'source_factory' => $container['source/factory'] + ]] + ])); } - public function registerCollectionLoader(Container $container) + public function registerCollectionLoader(Container $container): void { $this->registerLogger($container); $this->registerModelFactory($container); - $container['model/collection/loader'] = function (Container $container) { - return new \Charcoal\Loader\CollectionLoader([ - 'logger' => $container['logger'], - 'factory' => $container['model/factory'] - ]); - }; + $container['model/collection/loader'] = (fn(Container $container): \Charcoal\Loader\CollectionLoader => new \Charcoal\Loader\CollectionLoader([ + 'logger' => $container['logger'], + 'factory' => $container['model/factory'] + ])); } - public function registerModuleFactory(Container $container) + public function registerModuleFactory(Container $container): void { $this->registerLogger($container); $this->registerDatabase($container); - $container['module/factory'] = function (Container $container) { - return new Factory([ - 'base_class' => ModuleInterface::class, - 'resolver_options' => [ - 'suffix' => 'Module' - ], - 'arguments' => [[ - 'logger' => $container['logger'] - ]] - ]); - }; + $container['module/factory'] = (fn(Container $container): \Charcoal\Factory\GenericFactory => new Factory([ + 'base_class' => ModuleInterface::class, + 'resolver_options' => [ + 'suffix' => 'Module' + ], + 'arguments' => [[ + 'logger' => $container['logger'] + ]] + ])); } - public function registerAppDependencies(Container $container) + public function registerAppDependencies(Container $container): void { $this->registerConfig($container); $this->registerBaseUrl($container); @@ -387,21 +342,21 @@ public function registerAppDependencies(Container $container) $this->registerModuleFactory($container); } - public function registerActionDependencies(Container $container) + public function registerActionDependencies(Container $container): void { $this->registerLogger($container); $this->registerTranslator($container); $this->registerBaseUrl($container); } - public function registerTemplateDependencies(Container $container) + public function registerTemplateDependencies(Container $container): void { $this->registerLogger($container); $this->registerTranslator($container); $this->registerBaseUrl($container); } - public function registerWidgetDependencies(Container $container) + public function registerWidgetDependencies(Container $container): void { $this->registerLogger($container); $this->registerTranslator($container); diff --git a/packages/app/tests/Charcoal/App/Route/ActionRouteConfigTest.php b/packages/app/tests/Charcoal/App/Route/ActionRouteConfigTest.php index 49ee92040..d4ff47317 100644 --- a/packages/app/tests/Charcoal/App/Route/ActionRouteConfigTest.php +++ b/packages/app/tests/Charcoal/App/Route/ActionRouteConfigTest.php @@ -14,7 +14,7 @@ public function setUp(): void $this->obj = new ActionRouteConfig(); } - public function testSetActionData() + public function testSetActionData(): void { $ret = $this->obj->setActionData([ 'foo' => 'bar' ]); $this->assertSame($ret, $this->obj); diff --git a/packages/app/tests/Charcoal/App/Route/ActionRouteTest.php b/packages/app/tests/Charcoal/App/Route/ActionRouteTest.php index d32b3bd4a..9eba4948e 100644 --- a/packages/app/tests/Charcoal/App/Route/ActionRouteTest.php +++ b/packages/app/tests/Charcoal/App/Route/ActionRouteTest.php @@ -14,17 +14,13 @@ class ActionRouteTest extends AbstractTestCase { /** * Tested Class. - * - * @var ActionRoute */ - private $obj; + private \Charcoal\App\Route\ActionRoute $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. @@ -39,19 +35,17 @@ public function setUp(): void ]); } - public function testConstructor() + public function testConstructor(): void { $this->assertInstanceOf(ActionRoute::class, $this->obj); } /** * Set up the service container. - * - * @return Container */ - private function container() + private function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerLogger($container); diff --git a/packages/app/tests/Charcoal/App/Route/RouteConfigTest.php b/packages/app/tests/Charcoal/App/Route/RouteConfigTest.php index a2890b0b9..5a80df703 100644 --- a/packages/app/tests/Charcoal/App/Route/RouteConfigTest.php +++ b/packages/app/tests/Charcoal/App/Route/RouteConfigTest.php @@ -16,7 +16,7 @@ public function setUp(): void $this->obj = new RouteConfig(); } - public function testSetIdent() + public function testSetIdent(): void { $this->assertNull($this->obj->ident()); $ret = $this->obj->setIdent('foobar'); @@ -28,7 +28,7 @@ public function testSetIdent() $this->obj->setIdent(false); } - public function testSetRoute() + public function testSetRoute(): void { $this->assertNull($this->obj->route()); $ret = $this->obj->setRoute('foobar'); @@ -40,7 +40,7 @@ public function testSetRoute() $this->obj->setRoute(false); } - public function testSetGroups() + public function testSetGroups(): void { $this->assertEquals([], $this->obj->groups()); $ret = $this->obj->setGroups(['foo', 'bar']); @@ -49,7 +49,7 @@ public function testSetGroups() $this->assertEquals(['foo', 'bar'], $this->obj->groups()); } - public function testAddGroup() + public function testAddGroup(): void { $this->obj->addGroup('foo'); $this->obj->addGroup('bar'); @@ -60,7 +60,7 @@ public function testAddGroup() $this->obj->addGroup(false); } - public function testSetController() + public function testSetController(): void { $this->assertNull($this->obj->controller()); $ret = $this->obj->setController('foobar'); @@ -72,7 +72,7 @@ public function testSetController() $this->obj->setController(false); } - public function testSetMethods() + public function testSetMethods(): void { $this->assertEquals(['GET'], $this->obj->methods()); $ret = $this->obj->setMethods(['POST']); @@ -81,7 +81,7 @@ public function testSetMethods() $this->assertEquals(['POST'], $this->obj->methods()); } - public function testAddMethod() + public function testAddMethod(): void { $this->assertEquals(['GET'], $this->obj->methods()); $ret = $this->obj->addMethod('post'); @@ -93,13 +93,13 @@ public function testAddMethod() $this->obj->addMethod([]); } - public function testAddMethodInvalidMethodThrowsException() + public function testAddMethodInvalidMethodThrowsException(): void { $this->expectException(InvalidArgumentException::class); $this->obj->addMethod('invalid'); } - public function testSetHeaders() + public function testSetHeaders(): void { $this->assertEquals([], $this->obj->headers()); $ret = $this->obj->setHeaders(['Foo'=>'Bar']); @@ -110,7 +110,7 @@ public function testSetHeaders() $this->assertArrayNotHasKey('Foo', $this->obj->headers()); } - public function testAddHeader() + public function testAddHeader(): void { $this->assertEquals([], $this->obj->headers()); $ret = $this->obj->addHeader('Foo', 'Bar'); diff --git a/packages/app/tests/Charcoal/App/Route/RouteManagerTest.php b/packages/app/tests/Charcoal/App/Route/RouteManagerTest.php index f28385a3f..fc7ec11dd 100644 --- a/packages/app/tests/Charcoal/App/Route/RouteManagerTest.php +++ b/packages/app/tests/Charcoal/App/Route/RouteManagerTest.php @@ -1,5 +1,7 @@ obj->setupRoutes(); } - public function testConstructor() + public function testConstructor(): void { $this->assertInstanceOf(RouteManager::class, $this->obj); } - public function testSetupTemplate() + public function testSetupTemplate(): void { $config = [ 'templates' => [ @@ -64,14 +66,13 @@ public function testSetupTemplate() $reflector = new \ReflectionObject($obj); $method = $reflector->getMethod('setupTemplate'); - $method->setAccessible(true); foreach($config['templates'] as $routeIdent => $templateConfig) { $ret = $method->invoke($obj, $routeIdent, $templateConfig); $this->assertInstanceOf(RouteInterface::class, $ret); } } - public function testSetupAction() + public function testSetupAction(): void { $config = [ 'actions' => [ @@ -88,14 +89,13 @@ public function testSetupAction() $reflector = new \ReflectionObject($obj); $method = $reflector->getMethod('setupAction'); - $method->setAccessible(true); foreach($config['actions'] as $routeIdent => $actionConfig) { $ret = $method->invoke($obj, $routeIdent, $actionConfig); $this->assertInstanceOf(RouteInterface::class, $ret); } } - public function testSetupScript() + public function testSetupScript(): void { $config = [ 'scripts' => [ @@ -112,7 +112,6 @@ public function testSetupScript() $reflector = new \ReflectionObject($obj); $method = $reflector->getMethod('setupScript'); - $method->setAccessible(true); foreach($config['scripts'] as $routeIdent => $scriptConfig) { $ret = $method->invoke($obj, $routeIdent, $scriptConfig); $this->assertInstanceOf(RouteInterface::class, $ret); diff --git a/packages/app/tests/Charcoal/App/Route/ScriptRouteTest.php b/packages/app/tests/Charcoal/App/Route/ScriptRouteTest.php index 630f4ece7..f9f0d20c0 100644 --- a/packages/app/tests/Charcoal/App/Route/ScriptRouteTest.php +++ b/packages/app/tests/Charcoal/App/Route/ScriptRouteTest.php @@ -25,17 +25,13 @@ class ScriptRouteTest extends AbstractTestCase { /** * Tested Class. - * - * @var ScriptRoute */ - private $obj; + private \Charcoal\App\Route\ScriptRoute $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. @@ -49,30 +45,26 @@ public function setUp(): void ]); } - public function testInvoke() + public function testInvoke(): void { $container = $this->container(); - $container['script/factory'] = function($c) { - return new Factory(); - }; + $container['script/factory'] = (fn($c): \Charcoal\Factory\GenericFactory => new Factory()); - $request = $this->createMock(RequestInterface::class); - $response = $this->createMock(ResponseInterface::class); + $request = $this->createStub(RequestInterface::class); + $response = $this->createStub(ResponseInterface::class); // Invalid because "foo/bar" is not a valid script controller $this->expectException('\Exception'); - $ret = call_user_func([$this->obj, '__invoke'], $container, $request, $response); + call_user_func($this->obj->__invoke(...), $container, $request, $response); } /** * Set up the service container. - * - * @return Container */ - private function container() + private function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerLogger($container); diff --git a/packages/app/tests/Charcoal/App/Route/TemplateRouteConfigTest.php b/packages/app/tests/Charcoal/App/Route/TemplateRouteConfigTest.php index 175db9cd8..b7d020fcc 100644 --- a/packages/app/tests/Charcoal/App/Route/TemplateRouteConfigTest.php +++ b/packages/app/tests/Charcoal/App/Route/TemplateRouteConfigTest.php @@ -14,7 +14,7 @@ public function setUp(): void $this->obj = new TemplateRouteConfig(); } - public function testSetEngine() + public function testSetEngine(): void { //$this->assertEquals('mustache', $this->obj->engine()); $ret = $this->obj->setEngine('twig'); @@ -28,7 +28,7 @@ public function testSetEngine() $this->obj->setEngine(false); } - public function testSetTemplate() + public function testSetTemplate(): void { $this->assertNull($this->obj->template()); $ret = $this->obj->setTemplate('foobar'); @@ -40,7 +40,7 @@ public function testSetTemplate() $this->obj->setTemplate(false); } - public function testRedirect() + public function testRedirect(): void { $this->assertNull($this->obj->redirect()); $ret = $this->obj->setRedirect('foobar'); @@ -49,7 +49,7 @@ public function testRedirect() $this->assertEquals('foobar', $this->obj->redirect()); } - public function testSetRedirectMode() + public function testSetRedirectMode(): void { $this->assertEquals(301, $this->obj->redirectMode()); $ret = $this->obj->setRedirectMode(302); @@ -60,7 +60,7 @@ public function testSetRedirectMode() $this->obj->setRedirectMode(666); } - public function testSetCache() + public function testSetCache(): void { $this->assertFalse($this->obj->cache()); $ret = $this->obj->setCache(true); @@ -68,7 +68,7 @@ public function testSetCache() $this->assertTrue($this->obj->cache()); } - public function testSetCacheTtl() + public function testSetCacheTtl(): void { $this->assertEquals(0, $this->obj->cacheTtl()); $ret = $this->obj->setCacheTtl('42'); diff --git a/packages/app/tests/Charcoal/App/Script/AbstractScriptTest.php b/packages/app/tests/Charcoal/App/Script/AbstractScriptTest.php index 990d0165a..dbebf014e 100644 --- a/packages/app/tests/Charcoal/App/Script/AbstractScriptTest.php +++ b/packages/app/tests/Charcoal/App/Script/AbstractScriptTest.php @@ -21,17 +21,13 @@ class AbstractScriptTest extends AbstractTestCase { /** * Tested Class. - * - * @var AbstractScript */ - private $obj; + private \Charcoal\App\Script\AbstractScript $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. @@ -47,21 +43,21 @@ public function setUp(): void ]]); } - public function testSetIdent() + public function testSetIdent(): void { $ret = $this->obj->setIdent('foobar'); $this->assertSame($ret, $this->obj); $this->assertEquals('foobar', $this->obj->ident()); } - public function testSetDescription() + public function testSetDescription(): void { $ret = $this->obj->setDescription('Foo Description'); $this->assertSame($ret, $this->obj); $this->assertEQuals('Foo Description', $this->obj->description()); } - public function testSetQuiet() + public function testSetQuiet(): void { $this->assertFalse($this->obj->quiet()); $ret = $this->obj->setQuiet(true); @@ -69,7 +65,7 @@ public function testSetQuiet() $this->assertTrue($this->obj->quiet()); } - public function testSetVerbose() + public function testSetVerbose(): void { $this->assertFalse($this->obj->verbose()); $ret = $this->obj->setVerbose(true); @@ -77,9 +73,9 @@ public function testSetVerbose() $this->assertTrue($this->obj->verbose()); } - public function testSetArguments() + public function testSetArguments(): void { - $defaultArgs = $this->obj->arguments(); + $this->obj->arguments(); $ret = $this->obj->setArguments([ 'foo'=>[] ]); @@ -90,12 +86,10 @@ public function testSetArguments() /** * Set up the service container. - * - * @return Container */ - private function container() + private function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerLogger($container); diff --git a/packages/app/tests/Charcoal/App/ServerTestTrait.php b/packages/app/tests/Charcoal/App/ServerTestTrait.php index ea84e6730..b7309e1f1 100644 --- a/packages/app/tests/Charcoal/App/ServerTestTrait.php +++ b/packages/app/tests/Charcoal/App/ServerTestTrait.php @@ -14,7 +14,7 @@ trait ServerTestTrait /** * @var mixed The process identifier of the built-in PHP server. */ - static private $serverProcess = null; + static private $serverProcess; /** * @var string The hostname for the built-in PHP server. @@ -29,7 +29,7 @@ trait ServerTestTrait /** * @var null|string The server root directory, where it should be ran from. */ - static protected $serverRoot = null; + static protected $serverRoot; /** * @var string The APPLICATION_ENV environment variable. @@ -44,15 +44,13 @@ trait ServerTestTrait * @param boolean $checkForObjectIdentity Unused. * @param string $message The error to report. * @throws InvalidArgumentException - * @return void */ abstract public function assertArraySubset($subset, $array, $checkForObjectIdentity = false, $message = ''): void; /** * Retrieve the built-in PHP server URL. - * @return string */ - protected static function serverURL() + protected static function serverURL(): string { return static::$serverHost.':'.static::$serverPort; } @@ -71,18 +69,17 @@ protected static function serverRoot() /** * Retrieve wether the tests are run on windows or not. - * @return boolean */ - protected static function isWindows() + protected static function isWindows(): bool { return (stristr(php_uname('s'), 'win') !== false); } /** * Start a built-in PHP server process. - * @beforeClass */ - public static function bootUpBuiltInServer() + #[\PHPUnit\Framework\Attributes\BeforeClass] + public static function bootUpBuiltInServer(): void { $command = sprintf( 'php -S %s -t %s', @@ -110,18 +107,17 @@ public static function bootUpBuiltInServer() /** * Terminates the built-in PHP server process. - * @afterClass */ - public static function turnDownBuiltInServer() + #[\PHPUnit\Framework\Attributes\AfterClass] + public static function turnDownBuiltInServer(): void { pclose(static::$serverProcess); } /** * @param array $request The request data (method, route, options). - * @return \Psr\Http\Message\ResponseInterface */ - protected function callRequest(array $request) + protected function callRequest(array $request): \Psr\Http\Message\ResponseInterface { $route = str_replace('.', '', $request['route']); $client = new HttpClient(); @@ -132,10 +128,6 @@ protected function callRequest(array $request) ); } - /** - * @param array $expected - * @param ResponseInterface $response - */ protected function assertResponseMatchesExpected(array $expected, ResponseInterface $response) { if (isset($expected['statusCode']) && $expected['statusCode']) { @@ -157,7 +149,6 @@ protected function assertResponseMatchesExpected(array $expected, ResponseInterf /** * @param integer $expectedStatusCode - * @param ResponseInterface $response */ protected function assertResponseHasStatusCode($expectedStatusCode, ResponseInterface $response) { @@ -166,7 +157,6 @@ protected function assertResponseHasStatusCode($expectedStatusCode, ResponseInte /** * @param array|string $json - * @param ResponseInterface $response */ protected function assertResponseBodyMatchesJson($json, ResponseInterface $response) { @@ -187,7 +177,6 @@ protected function assertResponseBodyMatchesJson($json, ResponseInterface $respo /** * @param string $pattern - * @param ResponseInterface $response */ protected function assertResponseBodyRegExp($pattern, ResponseInterface $response) { diff --git a/packages/app/tests/Charcoal/App/ServiceProvider/AppServiceProviderTest.php b/packages/app/tests/Charcoal/App/ServiceProvider/AppServiceProviderTest.php index 790c95518..2061ad9ec 100644 --- a/packages/app/tests/Charcoal/App/ServiceProvider/AppServiceProviderTest.php +++ b/packages/app/tests/Charcoal/App/ServiceProvider/AppServiceProviderTest.php @@ -12,7 +12,7 @@ */ class AppServiceProviderTest extends AbstractTestCase { - public function testProvider() + public function testProvider(): void { $container = new Container(); $provider = new AppServiceProvider(); diff --git a/packages/app/tests/Charcoal/App/ServiceProvider/DatabaseServiceProviderTest.php b/packages/app/tests/Charcoal/App/ServiceProvider/DatabaseServiceProviderTest.php index c3b737005..f2238f152 100644 --- a/packages/app/tests/Charcoal/App/ServiceProvider/DatabaseServiceProviderTest.php +++ b/packages/app/tests/Charcoal/App/ServiceProvider/DatabaseServiceProviderTest.php @@ -12,7 +12,7 @@ */ class DatabaseServiceProviderTest extends AbstractTestCase { - public function testProvider() + public function testProvider(): void { $container = new Container([ 'config' => [] diff --git a/packages/app/tests/Charcoal/App/ServiceProvider/FilesystemServiceProviderTest.php b/packages/app/tests/Charcoal/App/ServiceProvider/FilesystemServiceProviderTest.php index a9a6e9cc4..fa80f2266 100644 --- a/packages/app/tests/Charcoal/App/ServiceProvider/FilesystemServiceProviderTest.php +++ b/packages/app/tests/Charcoal/App/ServiceProvider/FilesystemServiceProviderTest.php @@ -30,14 +30,14 @@ */ class FilesystemServiceProviderTest extends AbstractTestCase { - private $obj; + private \Charcoal\App\ServiceProvider\FilesystemServiceProvider $obj; public function setUp(): void { $this->obj = new FilesystemServiceProvider(); } - public function testProvider() + public function testProvider(): void { $container = $this->getContainer([ 'config' => $this->createAppConfig([ @@ -54,7 +54,7 @@ public function testProvider() $this->assertInstanceOf(Container::class, $container['filesystems']); } - public function testProviderDefaultAdapters() + public function testProviderDefaultAdapters(): void { $container = $this->getContainer([ 'config' => $this->createAppConfig([ @@ -69,7 +69,7 @@ public function testProviderDefaultAdapters() $this->assertInstanceOf(Filesystem::class, $container['filesystems']['public']); } - public function testProviderLocalAdapter() + public function testProviderLocalAdapter(): void { $container = $this->getContainer([ 'config' => $this->createAppConfig([ @@ -88,7 +88,7 @@ public function testProviderLocalAdapter() $this->assertInstanceOf(Filesystem::class, $container['filesystems']['local']); } - public function testProviderS3Adapter() + public function testProviderS3Adapter(): void { $container = $this->getContainer([ 'config' => $this->createAppConfig([ @@ -110,7 +110,7 @@ public function testProviderS3Adapter() $this->assertInstanceOf(Filesystem::class, $container['filesystems']['s3']); } - public function testProviderFtpAdapter() + public function testProviderFtpAdapter(): void { $container = $this->getContainer([ 'config' => $this->createAppConfig([ @@ -131,7 +131,7 @@ public function testProviderFtpAdapter() $this->assertInstanceOf(Filesystem::class, $container['filesystems']['ftp']); } - public function testProviderSftpAdapter() + public function testProviderSftpAdapter(): void { $container = $this->getContainer([ 'config' => $this->createAppConfig([ @@ -152,7 +152,7 @@ public function testProviderSftpAdapter() $this->assertInstanceOf(Filesystem::class, $container['filesystems']['sftp']); } - public function testProviderMemorypAdapter() + public function testProviderMemorypAdapter(): void { $container = $this->getContainer([ 'config' => $this->createAppConfig([ @@ -171,7 +171,7 @@ public function testProviderMemorypAdapter() $this->assertInstanceOf(Filesystem::class, $container['filesystems']['memory']); } - public function testProviderNullAdapter() + public function testProviderNullAdapter(): void { $container = $this->getContainer([ 'config' => $this->createAppConfig([ @@ -189,10 +189,10 @@ public function testProviderNullAdapter() $this->assertInstanceOf(Filesystem::class, $container['filesystems']['test']); } - public function testConfigWithoutTypeThrowsException() + public function testConfigWithoutTypeThrowsException(): void { $this->expectException('\Exception'); - $container = $this->getContainer([ + $this->getContainer([ 'config' => $this->createAppConfig([ 'filesystem' => [ 'connections' => [ @@ -201,16 +201,14 @@ public function testConfigWithoutTypeThrowsException() ] ]) ]); - - $test = $container['filesystem/test']; } - private function createAppConfig($defaults = null) + private function createAppConfig($defaults = null): \Charcoal\App\AppConfig { return new AppConfig(array_replace(['base_path' => sys_get_temp_dir()], $defaults)); } - private function getContainer($defaults = null) + private function getContainer($defaults = null): \Pimple\Container { $container = new Container($defaults); $this->obj->register($container); diff --git a/packages/app/tests/Charcoal/App/ServiceProvider/LoggerServiceProviderTest.php b/packages/app/tests/Charcoal/App/ServiceProvider/LoggerServiceProviderTest.php index 31b04e18a..41b1d8565 100644 --- a/packages/app/tests/Charcoal/App/ServiceProvider/LoggerServiceProviderTest.php +++ b/packages/app/tests/Charcoal/App/ServiceProvider/LoggerServiceProviderTest.php @@ -12,7 +12,7 @@ */ class LoggerServiceProviderTest extends AbstractTestCase { - public function testProvider() + public function testProvider(): void { $container = new Container([ 'config' => [] diff --git a/packages/app/tests/Charcoal/App/ServiceProvider/ScriptServiceProviderTest.php b/packages/app/tests/Charcoal/App/ServiceProvider/ScriptServiceProviderTest.php index 414ffc43a..1bd759abf 100644 --- a/packages/app/tests/Charcoal/App/ServiceProvider/ScriptServiceProviderTest.php +++ b/packages/app/tests/Charcoal/App/ServiceProvider/ScriptServiceProviderTest.php @@ -12,7 +12,7 @@ */ class ScriptServiceProviderTest extends AbstractTestCase { - public function testProvider() + public function testProvider(): void { $container = new Container(); $provider = new ScriptServiceProvider(); diff --git a/packages/app/tests/Charcoal/App/Template/AbstractTemplateTest.php b/packages/app/tests/Charcoal/App/Template/AbstractTemplateTest.php index b4b284e66..200359d8f 100644 --- a/packages/app/tests/Charcoal/App/Template/AbstractTemplateTest.php +++ b/packages/app/tests/Charcoal/App/Template/AbstractTemplateTest.php @@ -23,17 +23,13 @@ class AbstractTemplateTest extends AbstractTestCase { /** * Tested Class. - * - * @var AbstractTemplate */ - private $obj; + private \Charcoal\App\Template\AbstractTemplate $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. @@ -48,20 +44,18 @@ public function setUp(): void ]]); } - public function testInitIsTrue() + public function testInitIsTrue(): void { - $request = $this->createMock(RequestInterface::class); + $request = $this->createStub(RequestInterface::class); $this->assertTrue($this->obj->init($request)); } /** * Set up the service container. - * - * @return Container */ - private function container() + private function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerLogger($container); diff --git a/packages/app/tests/Charcoal/App/Template/AbstractWidgetTest.php b/packages/app/tests/Charcoal/App/Template/AbstractWidgetTest.php index a2f526bba..56b0c1295 100644 --- a/packages/app/tests/Charcoal/App/Template/AbstractWidgetTest.php +++ b/packages/app/tests/Charcoal/App/Template/AbstractWidgetTest.php @@ -23,17 +23,13 @@ class AbstractWidgetTest extends AbstractTestCase { /** * Tested Class. - * - * @var AbstractWidget */ - private $obj; + private \Charcoal\App\Template\AbstractWidget $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. @@ -54,7 +50,7 @@ public function setUp(): void * - `setActive()` method is chainable * - `setActive()` actually sets the active value. */ - public function testSetActive() + public function testSetActive(): void { $obj = $this->obj; $this->assertTrue($obj->active()); @@ -65,12 +61,10 @@ public function testSetActive() /** * Set up the service container. - * - * @return Container */ - private function container() + private function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerConfig($container); diff --git a/packages/app/tests/Charcoal/AssertionsTrait.php b/packages/app/tests/Charcoal/AssertionsTrait.php index 47f7837dc..767f81794 100644 --- a/packages/app/tests/Charcoal/AssertionsTrait.php +++ b/packages/app/tests/Charcoal/AssertionsTrait.php @@ -16,14 +16,13 @@ trait AssertionsTrait * @param array $haystack The actual haystack. * @param boolean $strict Whether to check for object identity. * @param string $message The error to report. - * @return void */ public function assertArraySubsets( array $expected, array $haystack, $strict = false, $message = '' - ) { + ): void { foreach ($expected as $key => $val) { $this->assertArraySubset([ $key => $val ], $haystack, $strict, $message); } @@ -40,18 +39,17 @@ public function assertArraySubsets( * @param boolean $checkForObjectIdentity Unused. * @param string $message The error to report. * @throws InvalidArgumentException - * @return void */ public function assertArraySubset($subset, $array, $checkForObjectIdentity = false, $message = ''): void { - if (!(is_array($subset) || $subset instanceof ArrayAccess)) { + if (!is_array($subset) && !$subset instanceof ArrayAccess) { throw InvalidArgumentException::create( 1, 'array or ArrayAccess' ); } - if (!(is_array($array) || $array instanceof ArrayAccess)) { + if (!is_array($array) && !$array instanceof ArrayAccess) { throw InvalidArgumentException::create( 2, 'array or ArrayAccess' diff --git a/packages/app/tests/bootstrap.php b/packages/app/tests/bootstrap.php index 53295f7d8..ac8e0b55a 100644 --- a/packages/app/tests/bootstrap.php +++ b/packages/app/tests/bootstrap.php @@ -1,5 +1,7 @@ getParams(); @@ -44,7 +43,7 @@ public function run(RequestInterface $request, ResponseInterface $response) $group = $params['group']; // Need more attachments... - if (!count($attachments)) { + if (count($attachments) === 0) { $this->setSuccess(false); return $response; @@ -53,7 +52,7 @@ public function run(RequestInterface $request, ResponseInterface $response) // Try loading the object try { $obj = $this->modelFactory()->create($objType)->load($objId); - } catch (Exception $e) { + } catch (Exception) { $this->setSuccess(false); return $response; diff --git a/packages/attachment/src/Charcoal/Admin/Action/JoinAction.php b/packages/attachment/src/Charcoal/Admin/Action/JoinAction.php index 2f41bc59e..2e9663384 100644 --- a/packages/attachment/src/Charcoal/Admin/Action/JoinAction.php +++ b/packages/attachment/src/Charcoal/Admin/Action/JoinAction.php @@ -21,9 +21,8 @@ class JoinAction extends AdminAction /** * @param RequestInterface $request A PSR-7 compatible Request instance. * @param ResponseInterface $response A PSR-7 compatible Response instance. - * @return ResponseInterface */ - public function run(RequestInterface $request, ResponseInterface $response) + public function run(RequestInterface $request, ResponseInterface $response): ResponseInterface { $params = $request->getParams(); @@ -44,7 +43,7 @@ public function run(RequestInterface $request, ResponseInterface $response) $group = $params['group']; // Need more attachments... - if (!count($attachments)) { + if (count($attachments) === 0) { $this->setSuccess(false); return $response; @@ -53,7 +52,7 @@ public function run(RequestInterface $request, ResponseInterface $response) // Try loading the object try { $obj = $this->modelFactory()->create($objType)->load($objId); - } catch (Exception $e) { + } catch (Exception) { $this->setSuccess(false); return $response; diff --git a/packages/attachment/src/Charcoal/Admin/Action/RemoveJoinAction.php b/packages/attachment/src/Charcoal/Admin/Action/RemoveJoinAction.php index b75fbcbaa..905b75272 100644 --- a/packages/attachment/src/Charcoal/Admin/Action/RemoveJoinAction.php +++ b/packages/attachment/src/Charcoal/Admin/Action/RemoveJoinAction.php @@ -22,9 +22,8 @@ class RemoveJoinAction extends AdminAction /** * @param RequestInterface $request A PSR-7 compatible Request instance. * @param ResponseInterface $response A PSR-7 compatible Response instance. - * @return ResponseInterface */ - public function run(RequestInterface $request, ResponseInterface $response) + public function run(RequestInterface $request, ResponseInterface $response): ResponseInterface { $params = $request->getParams(); @@ -47,7 +46,7 @@ public function run(RequestInterface $request, ResponseInterface $response) // Try loading the object try { $obj = $this->modelFactory()->create($objType)->load($objId); - } catch (Exception $e) { + } catch (Exception) { $this->setSuccess(false); return $response; @@ -84,7 +83,7 @@ public function run(RequestInterface $request, ResponseInterface $response) if ($attachment['id'] !== null) { $attachment->delete(); } - } catch (Exception $error) { + } catch (Exception) { $this->setSuccess(false); return $response; } diff --git a/packages/attachment/src/Charcoal/Admin/Widget/AddAttachmentWidget.php b/packages/attachment/src/Charcoal/Admin/Widget/AddAttachmentWidget.php index 49094eb64..9cf191a40 100644 --- a/packages/attachment/src/Charcoal/Admin/Widget/AddAttachmentWidget.php +++ b/packages/attachment/src/Charcoal/Admin/Widget/AddAttachmentWidget.php @@ -1,5 +1,7 @@ 'paperclip' ]; - /** - * @var array - */ - private $attachmentOptions; + private ?array $attachmentOptions = null; /** * Inject dependencies from a DI Container. @@ -112,6 +113,7 @@ class AttachmentWidget extends AdminWidget implements * @param Container $container A dependencies container instance. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -143,20 +145,16 @@ protected function createOrLoadObj() /** * Determine if the widget has any attachment types. - * - * @return boolean */ - public function hasAttachmentTypes() + public function hasAttachmentTypes(): bool { return !empty($this->attachableObjects()); } /** * Retrieve the attachment types with their collections. - * - * @return array */ - public function attachmentTypes() + public function attachmentTypes(): array { $attachableObjects = $this->attachableObjects(); @@ -172,17 +170,17 @@ public function attachmentTypes() $label = $attMeta['label']; $out[] = [ - 'id' => (isset($attMeta['att_id']) ? $attMeta['att_id'] : null), + 'id' => ($attMeta['att_id'] ?? null), 'ident' => $this->createIdent($attType), 'skipForm' => $attMeta['skipForm'], 'formIdent' => $attMeta['formIdent'], 'quickFormIdent' => $attMeta['quickFormIdent'], - 'hasFaIcon' => !!$attMeta['faIcon'], + 'hasFaIcon' => (bool) $attMeta['faIcon'], 'faIcon' => $attMeta['faIcon'], 'label' => $label, 'val' => $attType, 'locked' => $attMeta['locked'], - 'active' => ($i == 1) + 'active' => ($i === 1) ]; } @@ -220,20 +218,16 @@ public function attachments() /** * Determine the number of attachments. - * - * @return boolean */ - public function hasAttachments() + public function hasAttachments(): int { return count(iterator_to_array($this->attachments())); } /** * The default set of settings for attachment widget. - * - * @return array */ - public function defaultAttachmentOptions() + public function defaultAttachmentOptions(): array { return [ 'header' => null, @@ -251,9 +245,8 @@ public function defaultAttachmentOptions() * @param string $key The setting to add/replace. * @param mixed $val The settings's value to apply. * @throws InvalidArgumentException If the identifier is not a string. - * @return self */ - public function addAttachmentOption($key, $val) + public function addAttachmentOption($key, $val): static { if (!is_string($key)) { throw new InvalidArgumentException( @@ -291,20 +284,15 @@ public function attachmentOption($key) $this->attachmentOptions(); } - if (isset($this->attachmentOptions[$key])) { - return $this->attachmentOptions[$key]; - } - - return null; + return $this->attachmentOptions[$key] ?? null; } /** * Merge (replacing or adding) attachment options. * * @param array $settings The attachment options. - * @return self */ - public function mergeAttachmentOptions(array $settings) + public function mergeAttachmentOptions(array $settings): static { // Make sure default options are loaded. if ($this->attachmentOptions === null) { @@ -323,12 +311,10 @@ public function mergeAttachmentOptions(array $settings) * @param array $settings The Attachment options. * @return array Returns the parsed options. */ - protected function parseAttachmentOptions(array $settings) + protected function parseAttachmentOptions(array $settings): array { - if (isset($settings['show_header']) && isset($settings['show_preview'])) { - if (!$settings['show_header'] && !$settings['show_preview']) { - $settings['show_header'] = true; - } + if (isset($settings['show_header']) && isset($settings['show_preview']) && (!$settings['show_header'] && !$settings['show_preview'])) { + $settings['show_header'] = true; } return $settings; @@ -336,14 +322,13 @@ protected function parseAttachmentOptions(array $settings) // Setters // ========================================================================= - /** * Set the widget's data. * * @param array $data The widget data. - * @return self */ - public function setData(array $data) + #[\Override] + public function setData(array $data): static { $this->isMergingData = true; /** @@ -375,14 +360,12 @@ public function setData(array $data) * @throws InvalidArgumentException If the argument is not a string. * @return UserDataInterface Chainable */ - public function setLang($lang) + public function setLang($lang): static { - if ($lang !== null) { - if (!is_string($lang)) { - throw new InvalidArgumentException( - 'Language must be a string' - ); - } + if ($lang !== null && !is_string($lang)) { + throw new InvalidArgumentException( + 'Language must be a string' + ); } $this->lang = $lang; @@ -394,9 +377,8 @@ public function setLang($lang) * Set the attachment widget settings. * * @param array $settings Attachments options array. - * @return self */ - public function setAttachmentOptions(array $settings) + public function setAttachmentOptions(array $settings): static { $this->attachmentOptions = array_merge( $this->defaultAttachmentOptions(), @@ -413,14 +395,13 @@ public function setAttachmentOptions(array $settings) * * @param string $group The group identifier. * @throws InvalidArgumentException If the group key is invalid. - * @return self */ - public function setGroup($group) + public function setGroup($group): static { if (!is_string($group) && $group !== null) { throw new InvalidArgumentException(sprintf( 'Attachment group must be string, received %s', - is_object($group) ? get_class($group) : gettype($group) + get_debug_type($group) )); } @@ -433,9 +414,8 @@ public function setGroup($group) * Set an widget factory. * * @param FactoryInterface $factory The factory to create widgets. - * @return self */ - protected function setWidgetFactory(FactoryInterface $factory) + protected function setWidgetFactory(FactoryInterface $factory): static { $this->widgetFactory = $factory; @@ -446,9 +426,8 @@ protected function setWidgetFactory(FactoryInterface $factory) * Set the widget's title. * * @param mixed $title The title for the current widget. - * @return self */ - public function setTitle($title) + public function setTitle($title): static { $this->title = $this->translator()->translation($title); @@ -460,9 +439,8 @@ public function setTitle($title) * * @param integer $num The number of results to retrieve, per page. * @throws InvalidArgumentException If the parameter is not numeric or < 0. - * @return self */ - public function setNumPerPage($num) + public function setNumPerPage($num): static { if (!is_numeric($num)) { throw new InvalidArgumentException( @@ -488,9 +466,8 @@ public function setNumPerPage($num) * * @param integer $page The current page. Start at 0. * @throws InvalidArgumentException If the parameter is not numeric or < 0. - * @return self */ - public function setPage($page) + public function setPage($page): static { if (!is_numeric($page)) { throw new InvalidArgumentException( @@ -520,7 +497,7 @@ public function setPage($page) * @param array|AttachableInterface[] $attachableObjects A list of available attachment types. * @return self|boolean */ - public function setAttachableObjects($attachableObjects) + public function setAttachableObjects($attachableObjects): false|self { if (!$this->isMergingData) { $attachableObjects = $this->mergePresetAttachableObjects($attachableObjects); @@ -564,7 +541,7 @@ public function setAttachableObjects($attachableObjects) } // Useful for attaching a pre-existing attachment - $attId = (isset($attMeta['attachment_id']) ? $attMeta['attachment_id'] : null); + $attId = ($attMeta['attachment_id'] ?? null); if (isset($attMeta['label'])) { $label = $this->translator()->translation($attMeta['label']); @@ -609,14 +586,14 @@ public function setAttachableObjects($attachableObjects) $icon = 'fa fa-' . $icon; } } else { - $attParts = explode('/', $attType); + $attParts = explode('/', (string) $attType); if (isset($this->defaultIcons[end($attParts)])) { $faIcon = 'fa fa-' . $this->defaultIcons[end($attParts)]; } } if (isset($attMeta['show_icon'])) { - $showIcon = !!$attMeta['show_icon']; + $showIcon = (bool) $attMeta['show_icon']; } if (isset($attMeta['locked'])) { @@ -633,7 +610,7 @@ public function setAttachableObjects($attachableObjects) 'skipForm' => $skipForm, 'formIdent' => $formIdent, 'quickFormIdent' => $quickFormIdent, - 'hasFaIcon' => !!$faIcon, + 'hasFaIcon' => (bool) $faIcon, 'faIcon' => $faIcon, 'showIcon' => $showIcon, 'filters' => $filters, @@ -652,19 +629,17 @@ public function setAttachableObjects($attachableObjects) // Getters // ========================================================================= - /** * Retrieve the widget factory. * * @throws RuntimeException If the widget factory was not previously set. - * @return FactoryInterface */ - public function widgetFactory() + public function widgetFactory(): \Charcoal\Factory\FactoryInterface { - if (!isset($this->widgetFactory)) { + if (!$this->widgetFactory instanceof \Charcoal\Factory\FactoryInterface) { throw new RuntimeException(sprintf( 'Widget Factory is not defined for "%s"', - get_class($this) + static::class )); } @@ -683,10 +658,8 @@ public function lang() /** * Retrieve the attachment options. - * - * @return array */ - public function attachmentOptions() + public function attachmentOptions(): array { if ($this->attachmentOptions === null) { $this->attachmentOptions = $this->defaultAttachmentOptions(); @@ -729,10 +702,8 @@ public function attachableObjects() return $this->attachableObjects; } - /** - * @return array - */ - public function widgetDataForJs() + #[\Override] + public function widgetDataForJs(): array { return [ 'obj_type' => $this->obj()->objType(), @@ -743,10 +714,8 @@ public function widgetDataForJs() /** * Retrieve the widget's options. - * - * @return array */ - public function widgetOptions() + public function widgetOptions(): array { return [ 'obj_type' => $this->obj()->objType(), @@ -769,7 +738,7 @@ final public function widgetOptionsAsJson() $options = (JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); if ($this->debug()) { - $options = ($options | JSON_PRETTY_PRINT); + $options |= JSON_PRETTY_PRINT; } return json_encode($this->widgetOptions(), $options); @@ -780,7 +749,7 @@ final public function widgetOptionsAsJson() * * @return string Returns a stringified JSON object, protected from Mustache rendering. */ - final public function escapedWidgetOptionsAsJson() + final public function escapedWidgetOptionsAsJson(): string { return '{{=<% %>=}}' . $this->widgetOptionsAsJson() . '<%={{ }}=%>'; } @@ -802,7 +771,7 @@ public function defaultIcons() * @param string $string A dirty string to filter. * @return string */ - public function createIdent($string) + public function createIdent($string): ?string { return preg_replace('~/~', '-', $string); } diff --git a/packages/attachment/src/Charcoal/Admin/Widget/FormGroup/AttachmentFormGroup.php b/packages/attachment/src/Charcoal/Admin/Widget/FormGroup/AttachmentFormGroup.php index 3f32f6cf7..f1bcdd4d5 100644 --- a/packages/attachment/src/Charcoal/Admin/Widget/FormGroup/AttachmentFormGroup.php +++ b/packages/attachment/src/Charcoal/Admin/Widget/FormGroup/AttachmentFormGroup.php @@ -42,25 +42,21 @@ class AttachmentFormGroup extends AbstractFormGroup implements /** * Store the widget factory instance for the current class. - * - * @var FactoryInterface */ - private $widgetFactory; + private ?\Charcoal\Factory\FactoryInterface $widgetFactory = null; /** * Whether notes should be display before or after the form fields. - * - * @var boolean */ - private $showNotesAbove = false; + private bool $showNotesAbove = false; /** * Set the widget's data. * * @param array $data The widget data. - * @return self */ - public function setData(array $data) + #[\Override] + public function setData(array $data): static { /** * @todo Kinda hacky, but works with the concept of form. @@ -80,10 +76,8 @@ public function setData(array $data) /** * Retrieve the default nested widget options. - * - * @return array */ - public function defaultWidgetData() + public function defaultWidgetData(): array { return [ 'type' => 'charcoal/admin/widget/attachment', @@ -114,9 +108,8 @@ public function widgetId() * Set the widget's ID. * * @param string $widgetId The widget identifier. - * @return self */ - public function setWidgetId($widgetId) + public function setWidgetId($widgetId): static { $this->widgetId = $widgetId; @@ -126,7 +119,8 @@ public function setWidgetId($widgetId) /** * @return Translation|string|null */ - public function description() + #[\Override] + public function description(): string { return $this->renderTemplate((string)parent::description()); } @@ -134,7 +128,8 @@ public function description() /** * @return Translation|string|null */ - public function notes() + #[\Override] + public function notes(): string { return $this->renderTemplate((string)parent::notes()); } @@ -145,7 +140,8 @@ public function notes() * @param boolean|string $show Whether to show or hide notes. * @return self Chainable */ - public function setShowNotes($show) + #[\Override] + public function setShowNotes($show): static { $this->showNotesAbove = ($show === 'above'); parent::setShowNotes($show); @@ -153,10 +149,7 @@ public function setShowNotes($show) return $this; } - /** - * @return boolean - */ - public function showNotesAbove() + public function showNotesAbove(): bool { return $this->showNotesAbove && $this->showNotes(); } @@ -165,6 +158,7 @@ public function showNotesAbove() * @param Container $container The DI container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -186,9 +180,8 @@ protected function setDependencies(Container $container) * Set the widget factory. * * @param FactoryInterface $factory The factory to create widgets. - * @return self */ - protected function setWidgetFactory(FactoryInterface $factory) + protected function setWidgetFactory(FactoryInterface $factory): static { $this->widgetFactory = $factory; @@ -198,15 +191,14 @@ protected function setWidgetFactory(FactoryInterface $factory) /** * Retrieve the widget factory. * - * @return FactoryInterface * @throws RuntimeException If the widget factory was not previously set. */ - protected function widgetFactory() + protected function widgetFactory(): \Charcoal\Factory\FactoryInterface { - if ($this->widgetFactory === null) { + if (!$this->widgetFactory instanceof \Charcoal\Factory\FactoryInterface) { throw new RuntimeException(sprintf( 'Widget Factory is not defined for "%s"', - get_class($this) + static::class )); } diff --git a/packages/attachment/src/Charcoal/Attachment/AttachmentsConfig.php b/packages/attachment/src/Charcoal/Attachment/AttachmentsConfig.php index 481b508b4..fc4f3e65f 100644 --- a/packages/attachment/src/Charcoal/Attachment/AttachmentsConfig.php +++ b/packages/attachment/src/Charcoal/Attachment/AttachmentsConfig.php @@ -13,24 +13,18 @@ class AttachmentsConfig extends AbstractConfig { /** * Available attachment widget structures. - * - * @var array */ - private $widgets = []; + private array $widgets = []; /** * Attachment type groupings. - * - * @var array */ - private $groups = []; + private array $groups = []; /** * Available attachment types. - * - * @var array */ - private $attachables = []; + private array $attachables = []; /** * Set attachments settings in a specific order. @@ -38,6 +32,7 @@ class AttachmentsConfig extends AbstractConfig * @param array $data New config values. * @return AttachmentsConfig Chainable */ + #[\Override] public function setData(array $data) { if (isset($data['attachables'])) { @@ -64,7 +59,7 @@ public function setData(array $data) * @throws InvalidArgumentException If the attachment type or structure is invalid. * @return AttachmentsConfig Chainable */ - public function setAttachables(array $attachables) + public function setAttachables(array $attachables): static { foreach ($attachables as $attType => $attStruct) { if (!is_array($attStruct)) { @@ -94,10 +89,8 @@ public function setAttachables(array $attachables) /** * Retrieve the available attachment types. - * - * @return array */ - public function attachables() + public function attachables(): array { return $this->attachables; } @@ -109,7 +102,7 @@ public function attachables() * @throws InvalidArgumentException If the group identifier or structure is invalid. * @return AttachmentsConfig Chainable */ - public function setGroups(array $groups) + public function setGroups(array $groups): static { foreach ($groups as $groupIdent => $groupStruct) { if (!is_array($groupStruct)) { @@ -144,10 +137,8 @@ public function setGroups(array $groups) /** * Retrieve the available attachment type groups. - * - * @return array */ - public function groups() + public function groups(): array { return $this->groups; } @@ -159,7 +150,7 @@ public function groups() * @throws InvalidArgumentException If the widget identifier or structure is invalid. * @return AttachmentsConfig Chainable */ - public function setWidgets(array $widgets) + public function setWidgets(array $widgets): static { foreach ($widgets as $widgetIdent => $widgetStruct) { if (!is_array($widgetStruct)) { @@ -188,10 +179,8 @@ public function setWidgets(array $widgets) /** * Retrieve the available attachment widget structures. - * - * @return array */ - public function widgets() + public function widgets(): array { return $this->widgets; } diff --git a/packages/attachment/src/Charcoal/Attachment/Interfaces/AttachableInterface.php b/packages/attachment/src/Charcoal/Attachment/Interfaces/AttachableInterface.php index d4dc9e187..311f27456 100644 --- a/packages/attachment/src/Charcoal/Attachment/Interfaces/AttachableInterface.php +++ b/packages/attachment/src/Charcoal/Attachment/Interfaces/AttachableInterface.php @@ -1,5 +1,7 @@ defaultData(...))) { $defaultData = $this->metadata()->defaultData(); if ($defaultData) { $this->setData($defaultData); @@ -203,6 +197,7 @@ public function __construct(array $data = null) * @param Container $container A dependencies container instance. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -217,7 +212,7 @@ protected function setDependencies(Container $container) * @param boolean $presenter The presenter flag. * @return boolean Returns TRUE if model is used for presentation; FALSE for editing. */ - public function isPresentable($presenter = null) + public function isPresentable($presenter = null): bool { if (is_bool($presenter)) { $this->presentable = $presenter; @@ -245,10 +240,8 @@ public function containerId() /** * Determine if the attachment belongs to a container. - * - * @return boolean */ - public function hasContainerObj() + public function hasContainerObj(): bool { return boolval($this->containerObj); } @@ -268,9 +261,8 @@ public function containerObj() * * @param AttachmentContainerInterface|null $obj The container object or NULL. * @throws InvalidArgumentException If the given object is invalid. - * @return Attachment */ - public function setContainerObj($obj) + public function setContainerObj($obj): static { if ($obj === null) { $this->containerObj = null; @@ -282,14 +274,14 @@ public function setContainerObj($obj) throw new InvalidArgumentException(sprintf( 'Container object must be an instance of %s; received %s', AttachmentContainerInterface::class, - (is_object($obj) ? get_class($obj) : gettype($obj)) + (get_debug_type($obj)) )); } if (!$obj->id()) { throw new InvalidArgumentException(sprintf( 'Container object must have an ID.', - (is_object($obj) ? get_class($obj) : gettype($obj)) + (get_debug_type($obj)) )); } @@ -319,7 +311,7 @@ public function type() * @throws InvalidArgumentException If provided argument is not of type 'string'. * @return string */ - public function setType($type) + public function setType($type): static { if (!is_string($type)) { throw new InvalidArgumentException('Attachment type must be a string.'); @@ -354,7 +346,7 @@ public function typeLabel() */ public function microType() { - $classname = get_called_class(); + $classname = static::class; if (!isset(static::$resolvedType[$classname])) { $reflect = new ReflectionClass($this); @@ -367,10 +359,8 @@ public function microType() /** * Retrieve the image attachment type. - * - * @return string */ - public function imageType() + public function imageType(): string { return self::IMAGE_TYPE; } @@ -384,7 +374,7 @@ public function heading() { $heading = $this->renderTemplate((string)$this->heading); - if (!$heading) { + if ($heading === '' || $heading === '0') { $heading = $this->translator()->translation('{{ objType }} #{{ id }}', [ '{{ objType }}' => $this->typeLabel(), '{{ id }}' => $this->id() @@ -410,7 +400,7 @@ public function rawHeading() * @param string $template The attachment heading. * @return Attachment Chainable */ - public function setHeading($template) + public function setHeading($template): static { $this->heading = $this->translator()->translation($template); @@ -422,7 +412,7 @@ public function setHeading($template) * * @return Translation|string|null */ - public function preview() + public function preview(): string { if ($this->preview) { return $this->renderTemplate((string)$this->preview); @@ -447,7 +437,7 @@ public function rawPreview() * @param string $template The attachment preview. * @return Attachment Chainable */ - public function setPreview($template) + public function setPreview($template): static { $this->preview = $this->translator()->translation($template); @@ -456,90 +446,72 @@ public function setPreview($template) /** * Determine if the attachment type is an image. - * - * @return boolean */ - public function isImage() + public function isImage(): bool { return ($this->microType() === 'image'); } /** * Determine if the attachment type is an embed object. - * - * @return boolean */ - public function isEmbed() + public function isEmbed(): bool { return ($this->microType() === 'embed'); } /** * Determine if the attachment type is a video. - * - * @return boolean */ - public function isVideo() + public function isVideo(): bool { return ($this->microType() === 'video'); } /** * Determine if the attachment type is a file attachment. - * - * @return boolean */ - public function isFile() + public function isFile(): bool { return ($this->microType() === 'file'); } /** * Determine if the attachment type is a text-area. - * - * @return boolean */ - public function isText() + public function isText(): bool { return ($this->microType() === 'text'); } /** * Determine if the attachment type is an image gallery. - * - * @return boolean */ - public function isGallery() + public function isGallery(): bool { return ($this->microType() === 'gallery'); } /** * Determine if the attachment type is an accordion. - * - * @return boolean */ - public function isAccordion() + public function isAccordion(): bool { return ($this->microType() === 'accordion'); } /** * Determine if the attachment type is a link. - * - * @return boolean */ - public function isLink() + public function isLink(): bool { return ($this->microType() === 'link'); } /** * Determine if this attachment is a container. - * - * @return boolean */ - public function isAttachmentContainer() + public function isAttachmentContainer(): bool { return ($this instanceof AttachmentContainerInterface); } @@ -553,9 +525,9 @@ public function isAttachmentContainer() * @param boolean $show Show (TRUE) or hide (FALSE) the title. * @return UiItemInterface Chainable */ - public function setShowTitle($show) + public function setShowTitle($show): static { - $this->showTitle = !!$show; + $this->showTitle = (bool) $show; return $this; } @@ -564,9 +536,8 @@ public function setShowTitle($show) * Set the attachment's title. * * @param string $title The object title. - * @return self */ - public function setTitle($title) + public function setTitle($title): static { $this->title = $this->translator()->translation($title); @@ -577,9 +548,8 @@ public function setTitle($title) * Set the attachment's sub-title. * * @param string $title The object title. - * @return self */ - public function setSubtitle($title) + public function setSubtitle($title): static { $this->subtitle = $this->translator()->translation($title); @@ -590,9 +560,8 @@ public function setSubtitle($title) * Set the attachment's description. * * @param string $description The description of the object. - * @return self */ - public function setDescription($description) + public function setDescription($description): static { $this->description = $this->translator()->translation($description); @@ -609,9 +578,8 @@ public function setDescription($description) * Set the attachment's keywords. * * @param string|string[] $keywords One or more entries. - * @return self */ - public function setKeywords($keywords) + public function setKeywords($keywords): static { $this->keywords = $keywords; @@ -622,9 +590,8 @@ public function setKeywords($keywords) * Set the path to the thumbnail associated with the object. * * @param string $path A path to an image. - * @return self */ - public function setThumbnail($path) + public function setThumbnail($path): static { $this->thumbnail = $this->translator()->translation($path); @@ -635,9 +602,8 @@ public function setThumbnail($path) * Set the path to the attached file. * * @param string $path A path to a file. - * @return self */ - public function setFile($path) + public function setFile($path): static { $this->file = $this->translator()->translation($path); @@ -648,9 +614,8 @@ public function setFile($path) * Set the URL. * * @param string $link An external url. - * @return self */ - public function setLink($link) + public function setLink($link): static { $this->link = $this->translator()->translation($link); @@ -661,9 +626,8 @@ public function setLink($link) * Set the file label. * * @param string $label A descriptor. - * @return self */ - public function setFileLabel($label) + public function setFileLabel($label): static { $this->fileLabel = $this->translator()->translation($label); @@ -674,9 +638,8 @@ public function setFileLabel($label) * Set the link label. * * @param string $label A descriptor. - * @return self */ - public function setLinkLabel($label) + public function setLinkLabel($label): static { $this->linkLabel = $this->translator()->translation($label); @@ -688,9 +651,8 @@ public function setLinkLabel($label) * * @param integer|float $size A file size in bytes; the one of the attached. * @throws InvalidArgumentException If provided argument is not of type 'integer' or 'float'. - * @return self */ - public function setFileSize($size) + public function setFileSize($size): static { if ($size === null) { $this->fileSize = null; @@ -711,9 +673,8 @@ public function setFileSize($size) * Set file extension. * * @param string $type File extension. - * @return self */ - public function setFileType($type) + public function setFileType($type): static { $this->fileType = $type; @@ -725,9 +686,8 @@ public function setFileType($type) * * @param string $embed A URI or an HTML media element. * @throws InvalidArgumentException If provided argument is not of type 'string'. - * @return self */ - public function setEmbed($embed) + public function setEmbed($embed): static { $this->embed = $this->translator()->translation($embed); @@ -736,9 +696,8 @@ public function setEmbed($embed) /** * @param string|\string[] $categories Category elements. - * @return self */ - public function setCategories($categories) + public function setCategories($categories): static { $this->categories = $categories; @@ -758,7 +717,7 @@ public function showTitle() if (is_bool($this->showTitle)) { return $this->showTitle; } else { - return !!$this->title(); + return (bool) $this->title(); } } @@ -847,7 +806,7 @@ public function fileOrLink() * * @return string[]|null */ - public function fileAndLink() + public function fileAndLink(): array { $prop = $this->property('file'); $files = $prop->parseValAsFileList($this['file']); @@ -855,16 +814,15 @@ public function fileAndLink() $items = array_merge($files, $links); $items = array_unique($items); - $items = array_values($items); - return $items; + return array_values($items); } /** * Basename of the associated file. * @return string Basename of file. */ - public function basename() + public function basename(): string { if (!$this->file()) { return ''; @@ -940,9 +898,8 @@ public function presenter() /** * @param ModelInterface|mixed $presenter Presenter for Attachment. - * @return self */ - public function setPresenter($presenter) + public function setPresenter($presenter): static { $this->presenter = $presenter; @@ -951,15 +908,14 @@ public function setPresenter($presenter) // Events // ============================================================================= - /** * Event called before _deleting_ the attachment. * * @see Charcoal\Source\StorableTrait::preDelete() For the "create" Event. * @see Charcoal\Attachment\Traits\AttachmentAwareTrait::removeJoins - * @return boolean */ - public function preDelete() + #[\Override] + public function preDelete(): bool { $joinCollection = $this->collectionLoader() ->reset() @@ -976,15 +932,13 @@ public function preDelete() // Utilities // ============================================================================= - /** * Set the base URI of the project. * * @see \Charcoal\Admin\Support\setBaseUrl::baseUrl() * @param UriInterface $uri The base URI. - * @return self */ - protected function setBaseUrl(UriInterface $uri) + protected function setBaseUrl(UriInterface $uri): static { $this->baseUrl = $uri; @@ -995,14 +949,13 @@ protected function setBaseUrl(UriInterface $uri) * Retrieve the base URI of the project. * * @throws RuntimeException If the base URI is missing. - * @return UriInterface|null */ - public function baseUrl() + public function baseUrl(): \Psr\Http\Message\UriInterface { - if (!isset($this->baseUrl)) { + if (!$this->baseUrl instanceof \Psr\Http\Message\UriInterface) { throw new RuntimeException(sprintf( 'The base URI is not defined for [%s]', - get_class($this) + static::class )); } @@ -1024,9 +977,9 @@ public function createAbsoluteUrl($uri) $uri = strval($uri); if ($this->isRelativeUri($uri)) { $parts = parse_url($uri); - $path = isset($parts['path']) ? $parts['path'] : ''; - $query = isset($parts['query']) ? $parts['query'] : ''; - $hash = isset($parts['fragment']) ? $parts['fragment'] : ''; + $path = $parts['path'] ?? ''; + $query = $parts['query'] ?? ''; + $hash = $parts['fragment'] ?? ''; return $this->baseUrl()->withPath($path)->withQuery($query)->withFragment($hash); } @@ -1040,7 +993,7 @@ public function createAbsoluteUrl($uri) * @param string $text A string to parse relative URIs. * @return UriInterface|null */ - protected function resolveUrls($text) + protected function resolveUrls($text): ?string { static $search; @@ -1050,20 +1003,16 @@ protected function resolveUrls($text) $search = sprintf( '(?<=%1$s=")(?!%2$s)(\S+)(?=")', - implode('="|', array_map('preg_quote', $attr, [ '~' ])), - implode('|', array_map('preg_quote', $scheme, [ '~' ])) + implode('="|', array_map(preg_quote(...), $attr, [ '~' ])), + implode('|', array_map(preg_quote(...), $scheme, [ '~' ])) ); } - $text = preg_replace_callback( + return preg_replace_callback( '~' . $search . '~i', - function ($matches) { - return $this->createAbsoluteUrl($matches[1]); - }, + fn($matches) => $this->createAbsoluteUrl($matches[1]), $text ); - - return $text; } /** @@ -1082,21 +1031,15 @@ protected function isRelativeUri($uri) if (\parse_url($uri, PHP_URL_SCHEME)) { return false; } - - if (\preg_match('/^([\/\#\?]|[a-z][a-z0-9+.-]*:)/i', $uri)) { - return false; - } - - return true; + return !\preg_match('/^([\/\#\?]|[a-z][a-z0-9+.-]*:)/i', $uri); } /** * Set a model collection loader. * * @param CollectionLoader $loader The collection loader. - * @return self */ - protected function setCollectionLoader(CollectionLoader $loader) + protected function setCollectionLoader(CollectionLoader $loader): static { $this->collectionLoader = $loader; @@ -1107,14 +1050,13 @@ protected function setCollectionLoader(CollectionLoader $loader) * Retrieve the model collection loader. * * @throws Exception If the collection loader was not previously set. - * @return CollectionLoader */ - public function collectionLoader() + public function collectionLoader(): \Charcoal\Loader\CollectionLoader { - if (!isset($this->collectionLoader)) { + if (!$this->collectionLoader instanceof \Charcoal\Loader\CollectionLoader) { throw new Exception(sprintf( 'Collection Loader is not defined for "%s"', - get_class($this) + static::class )); } diff --git a/packages/attachment/src/Charcoal/Attachment/Object/Category/Generic.php b/packages/attachment/src/Charcoal/Attachment/Object/Category/Generic.php index 0c8506862..304d3e3ec 100644 --- a/packages/attachment/src/Charcoal/Attachment/Object/Category/Generic.php +++ b/packages/attachment/src/Charcoal/Attachment/Object/Category/Generic.php @@ -1,5 +1,7 @@ name = $this->translator()->translation($name); @@ -42,10 +44,7 @@ public function name() return $this->name; } - /** - * @return array - */ - public function loadCategoryItems() + public function loadCategoryItems(): array { return []; } diff --git a/packages/attachment/src/Charcoal/Attachment/Object/Container.php b/packages/attachment/src/Charcoal/Attachment/Object/Container.php index ea282fdcb..d5009af98 100644 --- a/packages/attachment/src/Charcoal/Attachment/Object/Container.php +++ b/packages/attachment/src/Charcoal/Attachment/Object/Container.php @@ -33,6 +33,7 @@ class Container extends Attachment implements * @param ServiceContainer $container A dependencies container instance. * @return void */ + #[\Override] protected function setDependencies(ServiceContainer $container) { parent::setDependencies($container); @@ -54,14 +55,10 @@ protected function setDependencies(ServiceContainer $container) public function attachments(...$args) { $attachables = $this->attachableObjects(); - $attachments = call_user_func_array([ $this, 'getAttachments' ], $args); + $attachments = call_user_func_array($this->getAttachments(...), $args); foreach ($attachments as $attachment) { - if (isset($attachables[$attachment->objType()])) { - $attachment->attachmentType = $attachables[$attachment->objType()]; - } else { - $attachment->attachmentType = []; - } + $attachment->attachmentType = $attachables[$attachment->objType()] ?? []; } return $attachments; @@ -70,14 +67,14 @@ public function attachments(...$args) /** * Event called before _deleting_ the attachment. * - * @return boolean * @see Charcoal\Attachment\Traits\AttachmentAwareTrait::removeJoins * @see Charcoal\Source\StorableTrait::preDelete() For the "create" Event. */ - public function preDelete() + #[\Override] + public function preDelete(): bool { // Delete nested attachments - array_map(function ($attachment) { + array_map(function ($attachment): void { $attachment->delete(); }, $this->attachments()->values()); diff --git a/packages/attachment/src/Charcoal/Attachment/Object/Embed.php b/packages/attachment/src/Charcoal/Attachment/Object/Embed.php index 0c7aa2fc0..a3bb35988 100644 --- a/packages/attachment/src/Charcoal/Attachment/Object/Embed.php +++ b/packages/attachment/src/Charcoal/Attachment/Object/Embed.php @@ -1,5 +1,7 @@ generateThumbnail(); @@ -56,9 +54,9 @@ public function preSave() * * @see StorableTrait::preUpdate() For the "update" Event. * @param array $properties Optional. The list of properties to update. - * @return boolean */ - public function preUpdate(array $properties = null) + #[\Override] + public function preUpdate(?array $properties = null): bool { $this->generateThumbnail(); diff --git a/packages/attachment/src/Charcoal/Attachment/Object/Gallery.php b/packages/attachment/src/Charcoal/Attachment/Object/Gallery.php index f64628c1f..ab6bcd72f 100644 --- a/packages/attachment/src/Charcoal/Attachment/Object/Gallery.php +++ b/packages/attachment/src/Charcoal/Attachment/Object/Gallery.php @@ -21,10 +21,8 @@ class Gallery extends Container /** * Retrieve the container's attachments as rows containing columns. - * - * @return array */ - public function attachmentsAsRows() + public function attachmentsAsRows(): array { $rows = []; @@ -32,7 +30,7 @@ public function attachmentsAsRows() $rows = array_chunk($this->attachments()->values(), $this->numColumns); /** Map row content with useful front-end properties. */ - array_walk($rows, function (&$attachment, $index) { + array_walk($rows, function (&$attachment, $index): void { $attachment = [ 'columns' => $attachment, 'isFirst' => ($index === 0), @@ -45,10 +43,8 @@ public function attachmentsAsRows() /** * Retrieve the Bootstrap column width to be used in front-end templating. - * - * @return string */ - public function columnWidth() + public function columnWidth(): string { return (string)ceil(12 / $this->numColumns); } diff --git a/packages/attachment/src/Charcoal/Attachment/Object/Image.php b/packages/attachment/src/Charcoal/Attachment/Object/Image.php index 7af407710..f173e7117 100644 --- a/packages/attachment/src/Charcoal/Attachment/Object/Image.php +++ b/packages/attachment/src/Charcoal/Attachment/Object/Image.php @@ -14,6 +14,7 @@ class Image extends File * * @return string|null */ + #[\Override] public function src() { $src = $this->thumbnail(); @@ -31,9 +32,9 @@ public function src() * @todo Generate thumbnail from the main image (or not.). * @used-by StorableTrait::preSave() For the "create" Event. * @used-by StorableTrait::preUpdate() For the "update" Event. - * @return boolean */ - public function generateThumbnail() + #[\Override] + public function generateThumbnail(): bool { return true; } diff --git a/packages/attachment/src/Charcoal/Attachment/Object/Join.php b/packages/attachment/src/Charcoal/Attachment/Object/Join.php index be69ce330..99f1a2f84 100644 --- a/packages/attachment/src/Charcoal/Attachment/Object/Join.php +++ b/packages/attachment/src/Charcoal/Attachment/Object/Join.php @@ -112,14 +112,12 @@ class Join extends AbstractModel implements * * @var JoinInterface[]|null */ - private $hierarchy; + private ?array $hierarchy = null; /** * Store the factory instance. - * - * @var FactoryInterface */ - private $modelFactory; + private ?\Charcoal\Factory\FactoryInterface $modelFactory = null; /** * Set the model's dependencies. @@ -127,6 +125,7 @@ class Join extends AbstractModel implements * @param ServiceContainer $container Service container. * @return void */ + #[\Override] protected function setDependencies(ServiceContainer $container) { parent::setDependencies($container); @@ -234,9 +233,8 @@ public function getParent() * * @todo Add support for multiple masters. * @throws LogicException If the relationship is broken or incomplete. - * @return JoinInterface|null */ - public function getMaster() + public function getMaster(): ?\Charcoal\Attachment\Interfaces\JoinInterface { $hierarchy = $this->invertedHierarchy(); if (isset($hierarchy[0])) { @@ -250,10 +248,8 @@ public function getMaster() * Reset this relationship's hierarchy. * * The relationship's hierarchy can be rebuilt with {@see self::hierarchy()}. - * - * @return self */ - public function resetHierarchy() + public function resetHierarchy(): static { $this->hierarchy = null; @@ -265,7 +261,7 @@ public function resetHierarchy() * * @return JoinInterface[] */ - public function hierarchy() + public function hierarchy(): array { if ($this->hierarchy === null) { $hierarchy = []; @@ -287,7 +283,7 @@ public function hierarchy() * * @return JoinInterface[] */ - public function invertedHierarchy() + public function invertedHierarchy(): array { return array_reverse($this->hierarchy()); } @@ -295,16 +291,14 @@ public function invertedHierarchy() // Setters -// ============================================================================= - + // ============================================================================= /** * Set the source object type. * * @param string $type The object type identifier. * @throws InvalidArgumentException If provided argument is not of type 'string'. - * @return self */ - public function setObjectType($type) + public function setObjectType($type): static { if (!is_string($type)) { throw new InvalidArgumentException('Object type must be a string.'); @@ -320,9 +314,8 @@ public function setObjectType($type) * * @param mixed $id The object ID to join the attachment to. * @throws InvalidArgumentException If provided argument is not a string or numerical value. - * @return self */ - public function setObjectId($id) + public function setObjectId($id): static { if (!is_scalar($id)) { throw new InvalidArgumentException( @@ -340,9 +333,8 @@ public function setObjectId($id) * * @param mixed $id The object ID to attach. * @throws InvalidArgumentException If provided argument is not a string or numerical value. - * @return self */ - public function setAttachmentId($id) + public function setAttachmentId($id): static { if (!is_scalar($id)) { throw new InvalidArgumentException( @@ -360,9 +352,8 @@ public function setAttachmentId($id) * * @param mixed $id The group ID describing the relationship. * @throws InvalidArgumentException If provided argument is not a string. - * @return self */ - public function setGroup($id) + public function setGroup($id): static { if (!is_string($id)) { throw new InvalidArgumentException( @@ -383,9 +374,8 @@ public function setGroup($id) * * @param integer $position A position. * @throws InvalidArgumentException If the position is not an integer (or numeric integer string). - * @return self */ - public function setPosition($position) + public function setPosition($position): static { if ($position === null) { $this->position = null; @@ -407,11 +397,10 @@ public function setPosition($position) * Enable/Disable the relationship. * * @param boolean $active The active flag. - * @return self */ - public function setActive($active) + public function setActive($active): static { - $this->active = !!$active; + $this->active = (bool) $active; return $this; } @@ -420,9 +409,8 @@ public function setActive($active) * Set an model factory. * * @param FactoryInterface $factory The factory to create models. - * @return self */ - protected function setModelFactory(FactoryInterface $factory) + protected function setModelFactory(FactoryInterface $factory): static { $this->modelFactory = $factory; @@ -498,14 +486,13 @@ public function active() * Retrieve the model factory. * * @throws RuntimeException If the model factory is missing. - * @return FactoryInterface */ - public function modelFactory() + public function modelFactory(): \Charcoal\Factory\FactoryInterface { - if (!isset($this->modelFactory)) { + if (!$this->modelFactory instanceof \Charcoal\Factory\FactoryInterface) { throw new RuntimeException(sprintf( 'Model Factory is not defined for [%s]', - get_class($this) + static::class )); } diff --git a/packages/attachment/src/Charcoal/Attachment/Object/Link.php b/packages/attachment/src/Charcoal/Attachment/Object/Link.php index 13b03ee17..f56ddd69c 100644 --- a/packages/attachment/src/Charcoal/Attachment/Object/Link.php +++ b/packages/attachment/src/Charcoal/Attachment/Object/Link.php @@ -1,5 +1,7 @@ setModelFactory($container['model/factory']); } - /** - * @return boolean - */ - public function interactive() + #[\Override] + public function interactive(): bool { return true; } /** * Retrieve the script's supported arguments. - * - * @return array */ - public function defaultArguments() + #[\Override] + public function defaultArguments(): array { $arguments = [ 'hard' => [ @@ -166,9 +165,8 @@ public function defaultArguments() * * @param RequestInterface $request A PSR-7 compatible Request instance. * @param ResponseInterface $response A PSR-7 compatible Response instance. - * @return ResponseInterface */ - public function run(RequestInterface $request, ResponseInterface $response) + public function run(RequestInterface $request, ResponseInterface $response): ResponseInterface { unset($request); @@ -183,10 +181,8 @@ public function run(RequestInterface $request, ResponseInterface $response) /** * Execute the prime directive. - * - * @return self */ - public function start() + public function start(): static { $cli = $this->climate(); @@ -202,16 +198,13 @@ public function start() /** * Prune relationships of dead objects. - * - * @return self */ - protected function pruneRelationships() + protected function pruneRelationships(): static { - $cli = $this->climate(); - $ask = $this->interactive(); - $dry = $this->dryRun(); - $verb = $this->verbose(); - $mucho = ($dry || $verb); + $this->climate(); + $this->interactive(); + $this->dryRun(); + $this->verbose(); $attach = $this->modelFactory()->get(Attachment::class); $pivot = $this->modelFactory()->get(Join::class); @@ -227,7 +220,7 @@ protected function pruneRelationships() $sql = 'SELECT DISTINCT `%sourceType` FROM `%pivotTable`;'; $rows = $db->query(strtr($sql, $binds), PDO::FETCH_ASSOC); if ($rows->rowCount()) { - error_log(get_called_class() . '::' . __FUNCTION__); + error_log(static::class . '::' . __FUNCTION__); /** @todo Confirm each distinct source type */ @@ -305,10 +298,8 @@ protected function pruneRelationships() /** * Prune orphan attachments. - * - * @return self */ - protected function pruneAttachments() + protected function pruneAttachments(): static { $cli = $this->climate(); $ask = $this->interactive(); @@ -366,7 +357,7 @@ protected function pruneAttachments() $this->indent = str_repeat(' ', (($length * 2) + 4)); $prop = $attach->property($attach->key()); - if ($prop && preg_match('~\b\w+\((?\d+)\)~', $prop->sqlType(), $matches)) { + if ($prop && preg_match('~\b\w+\((?\d+)\)~', (string) $prop->sqlType(), $matches)) { $pad = (intval($matches['length']) + 5); } else { $pad = 20; @@ -396,8 +387,8 @@ protected function pruneAttachments() if ($ask) { $type = sprintf('[%s]', $obj->microType()); - $label = sprintf('#%1$s %2$s', str_pad($objId, $pad), str_pad($type, 20)); - if ($title) { + $label = sprintf('#%1$s %2$s', str_pad((string) $objId, $pad), str_pad($type, 20)); + if ($title !== '' && $title !== '0') { $label = sprintf('%1$s "%2$s"', $label, $title); } @@ -506,10 +497,8 @@ protected function pruneAttachments() /** * Display stored messages or a generic conclusion. - * - * @return self */ - protected function conclude() + protected function conclude(): static { $cli = $this->climate(); @@ -531,15 +520,14 @@ protected function conclude() * @param string $singular The message when the count is 1. * @param string $zero The message when the count is zero. * @throws InvalidArgumentException If the given argument is not an integer. - * @return boolean */ - protected function describeCount($count, $plural, $singular, $zero) + protected function describeCount($count, $plural, $singular, $zero): bool { if (!is_int($count)) { throw new InvalidArgumentException( sprintf( 'Must be an integer', - is_object($count) ? get_class($count) : gettype($count) + get_debug_type($count) ) ); } @@ -573,9 +561,8 @@ protected function describeCount($count, $plural, $singular, $zero) * @param integer|null $pruned Count the number of deleted objects. * @param integer|null $failed Count the number of failed deletions. * @param array|null $feedback Update the feedback. - * @return boolean */ - protected function deleteObject(AttachableInterface $obj, &$pruned = null, &$failed = null, array &$feedback = null) + protected function deleteObject(AttachableInterface $obj, &$pruned = null, &$failed = null, ?array &$feedback = null): bool { $verb = $this->verbose(); diff --git a/packages/attachment/src/Charcoal/Attachment/Script/MigrateScript.php b/packages/attachment/src/Charcoal/Attachment/Script/MigrateScript.php index ab745426b..0757de3c7 100644 --- a/packages/attachment/src/Charcoal/Attachment/Script/MigrateScript.php +++ b/packages/attachment/src/Charcoal/Attachment/Script/MigrateScript.php @@ -31,6 +31,7 @@ class MigrateScript extends AlterPrimaryKeyScript /** * @return void */ + #[\Override] protected function init() { $this->setArguments($this->defaultArguments()); @@ -44,10 +45,9 @@ protected function init() /** * Execute the prime directive. - * - * @return self */ - public function start() + #[\Override] + public function start(): static { $cli = $this->climate(); @@ -166,7 +166,6 @@ public function start() // Alter Table // ========================================================================= - /** * Sync the new primary keys to pivot table. * @@ -175,14 +174,14 @@ public function start() * @param IdProperty $oldProp The previous ID property. * @param PropertyField $oldField The previous ID field. * @throws InvalidArgumentException If the new property does not implement the proper mode. - * @return self */ + #[\Override] protected function syncRelatedFields( IdProperty $newProp, PropertyField $newField, IdProperty $oldProp, PropertyField $oldField - ) { + ): static { unset($newProp, $oldProp, $oldField); $cli = $this->climate(); @@ -243,6 +242,7 @@ protected function syncRelatedFields( * * @return array */ + #[\Override] public function defaultArguments() { static $arguments; @@ -267,9 +267,10 @@ public function defaultArguments() * * @return ModelInterface */ + #[\Override] public function targetModel() { - if (!isset($this->targetModel)) { + if ($this->targetModel === null) { $this->targetModel = $this->modelFactory()->get(Attachment::class); } @@ -283,7 +284,7 @@ public function targetModel() */ public function pivotModel() { - if (!isset($this->pivotModel)) { + if ($this->pivotModel === null) { $this->pivotModel = $this->modelFactory()->get(Join::class); } diff --git a/packages/attachment/src/Charcoal/Attachment/Traits/AttachmentAwareTrait.php b/packages/attachment/src/Charcoal/Attachment/Traits/AttachmentAwareTrait.php index 29c269507..250bd1567 100644 --- a/packages/attachment/src/Charcoal/Attachment/Traits/AttachmentAwareTrait.php +++ b/packages/attachment/src/Charcoal/Attachment/Traits/AttachmentAwareTrait.php @@ -57,8 +57,8 @@ trait AttachmentAwareTrait public function getAttachments( $group = null, $type = null, - callable $before = null, - callable $after = null + ?callable $before = null, + ?callable $after = null ) { if (is_array($group)) { $options = $group; @@ -81,20 +81,18 @@ public function getAttachments( $options = $this->parseAttachmentOptions($options); extract($options); - if ($group !== 0) { - if (!is_string($group)) { - throw new InvalidArgumentException(sprintf( - 'The "group" must be a string, received %s', - is_object($group) ? get_class($group) : gettype($group) - )); - } + if ($group !== 0 && !is_string($group)) { + throw new InvalidArgumentException(sprintf( + 'The "group" must be a string, received %s', + get_debug_type($group) + )); } if ($type !== 0) { if (!is_string($type)) { throw new InvalidArgumentException(sprintf( 'The "type" must be a string, received %s', - is_object($type) ? get_class($type) : gettype($type) + get_debug_type($type) )); } @@ -140,7 +138,7 @@ public function getAttachments( attachment.active = 1'; } - if ($type) { + if ($type !== '' && $type !== '0') { $query .= sprintf(' AND attachment.type = "%s"', $type); @@ -152,7 +150,7 @@ public function getAttachments( AND joined.object_id = "%s"', $objType, $objId); - if ($group) { + if ($group !== '' && $group !== '0') { $query .= sprintf(' AND joined.group = "%s"', $group); @@ -165,7 +163,7 @@ public function getAttachments( $loader->setModel($attProto); $loader->setDynamicTypeField('type'); - $callable = function (&$att) use ($before) { + $callable = function (&$att) use ($before): void { if ($this instanceof AttachableInterface) { $att->setContainerObj($this); } @@ -194,17 +192,15 @@ public function getAttachments( * * @return boolean Whether $this has any nodes (TRUE) or not (FALSE). */ - public function hasAttachments() + public function hasAttachments(): bool { - return !!($this->numAttachments()); + return (bool) $this->numAttachments(); } /** * Count the number of nodes associated to the current object. - * - * @return integer */ - public function numAttachments() + public function numAttachments(): int { return count($this->getAttachments([ 'group' => null @@ -243,9 +239,9 @@ public function addAttachment($attachment, $group = 'contents') /** * Remove all joins linked to a specific attachment. * - * @deprecated in favour of AttachmentAwareTrait::removeAttachmentJoins() * @return boolean */ + #[\Deprecated(message: 'in favour of AttachmentAwareTrait::removeAttachmentJoins()')] public function removeJoins() { $this->logger->warning( @@ -259,10 +255,8 @@ public function removeJoins() /** * Remove all joins linked to a specific attachment. - * - * @return boolean */ - public function removeAttachmentJoins() + public function removeAttachmentJoins(): bool { $joinProto = $this->modelFactory()->get(Join::class); @@ -285,9 +279,8 @@ public function removeAttachmentJoins() * Delete the objects associated to the current object. * * @param array $options Filter the attachments by an option list. - * @return boolean */ - public function deleteAttachments(array $options = []) + public function deleteAttachments(array $options = []): bool { foreach ($this->getAttachments($options) as $attachment) { $attachment->delete(); @@ -310,7 +303,7 @@ public function deleteAttachments(array $options = []) * ] * @return array Attachment obj_types. */ - public function attachmentObjTypes() + public function attachmentObjTypes(): array { $defaultEditDashboard = $this->metadata()->get('admin.default_edit_dashboard'); $dashboards = $this->metadata()->get('admin.dashboards'); @@ -318,7 +311,7 @@ public function attachmentObjTypes() $widgets = $editDashboard['widgets']; $formIdent = ''; - foreach ($widgets as $ident => $val) { + foreach ($widgets as $val) { if ($val['type'] == 'charcoal/admin/widget/object-form') { $formIdent = $val['form_ident']; } @@ -364,17 +357,15 @@ public function attachmentObjTypes() * @param array $options A list of options. * Option keys not present in {@see self::getDefaultAttachmentOptions() default options} * are rejected. - * @return array */ - protected function parseAttachmentOptions(array $options) + protected function parseAttachmentOptions(array $options): array { $defaults = $this->getDefaultAttachmentOptions(); $options = array_intersect_key($options, $defaults); $options = array_filter($options, [ $this, 'filterAttachmentOption' ], ARRAY_FILTER_USE_BOTH); - $options = array_replace($defaults, $options); - return $options; + return array_replace($defaults, $options); } /** @@ -389,25 +380,17 @@ protected function filterAttachmentOption($val, $key) if ($val === null) { return false; } - - switch ($key) { - case 'isActive': - return is_bool($val); - - case 'before': - case 'after': - return is_callable($val); - } - - return true; + return match ($key) { + 'isActive' => is_bool($val), + 'before', 'after' => is_callable($val), + default => true, + }; } /** * Retrieve the default options for loading a collection of attachments. - * - * @return array */ - protected function getDefaultAttachmentOptions() + protected function getDefaultAttachmentOptions(): array { return [ 'group' => 0, diff --git a/packages/attachment/src/Charcoal/Attachment/Traits/AttachmentContainerTrait.php b/packages/attachment/src/Charcoal/Attachment/Traits/AttachmentContainerTrait.php index d3b6250c3..d4e58aec4 100644 --- a/packages/attachment/src/Charcoal/Attachment/Traits/AttachmentContainerTrait.php +++ b/packages/attachment/src/Charcoal/Attachment/Traits/AttachmentContainerTrait.php @@ -126,10 +126,8 @@ public function attachmentGroup() /** * Retrieve the attachment types with their collections. - * - * @return array */ - public function attachmentTypes() + public function attachmentTypes(): array { return array_values($this->attachableObjects()); } @@ -165,7 +163,7 @@ public function attachableObjects() if (isset($attMeta['label'])) { $attMeta['label'] = $this->translator()->translation($attMeta['label']); } else { - $attMeta['label'] = ucfirst(basename($attType)); + $attMeta['label'] = ucfirst(basename((string) $attType)); } $faIcon = ''; @@ -174,20 +172,12 @@ public function attachableObjects() } $attMeta['faIcon'] = $faIcon; - $attMeta['hasFaIcon'] = !!$faIcon; + $attMeta['hasFaIcon'] = (bool) $faIcon; // Custom forms - if (isset($attMeta['form_ident'])) { - $attMeta['formIdent'] = $attMeta['form_ident']; - } else { - $attMeta['formIdent'] = null; - } + $attMeta['formIdent'] = $attMeta['form_ident'] ?? null; - if (isset($attMeta['quick_form_ident'])) { - $attMeta['quickFormIdent'] = $attMeta['quick_form_ident']; - } else { - $attMeta['quickFormIdent'] = null; - } + $attMeta['quickFormIdent'] = $attMeta['quick_form_ident'] ?? null; $this->attachableObjects[$attType] = $attMeta; } @@ -199,10 +189,8 @@ public function attachableObjects() /** * Determine if this attachment is a container. - * - * @return boolean */ - public function isAttachmentContainer() + public function isAttachmentContainer(): bool { return true; } diff --git a/packages/attachment/src/Charcoal/Attachment/Traits/ConfigurableAttachmentsTrait.php b/packages/attachment/src/Charcoal/Attachment/Traits/ConfigurableAttachmentsTrait.php index f5455b41c..b6d5fcfbe 100644 --- a/packages/attachment/src/Charcoal/Attachment/Traits/ConfigurableAttachmentsTrait.php +++ b/packages/attachment/src/Charcoal/Attachment/Traits/ConfigurableAttachmentsTrait.php @@ -76,7 +76,7 @@ public function config($key = null) * @param array|null $data Optional data to pass to the new configset. * @return ConfigInterface */ - protected function createConfig($data = null) + protected function createConfig($data = null): \Charcoal\Attachment\AttachmentsConfig { return new AttachmentsConfig($data); } diff --git a/packages/attachment/tests/Charcoal/ContainerIntegrationTrait.php b/packages/attachment/tests/Charcoal/ContainerIntegrationTrait.php index 4ad02d1c1..0f4c9eaa5 100644 --- a/packages/attachment/tests/Charcoal/ContainerIntegrationTrait.php +++ b/packages/attachment/tests/Charcoal/ContainerIntegrationTrait.php @@ -51,9 +51,8 @@ protected function getContainerProvider() /** * @see ContainerProvider - * @return void */ - private function setupContainer() + private function setupContainer(): void { $provider = new ContainerProvider(); $container = new Container(); diff --git a/packages/attachment/tests/Charcoal/ContainerProvider.php b/packages/attachment/tests/Charcoal/ContainerProvider.php index 1cacbbbd7..b192e8772 100644 --- a/packages/attachment/tests/Charcoal/ContainerProvider.php +++ b/packages/attachment/tests/Charcoal/ContainerProvider.php @@ -55,9 +55,8 @@ class ContainerProvider * Register the unit tests required services. * * @param Container $container A DI container. - * @return void */ - public function registerBaseServices(Container $container) + public function registerBaseServices(Container $container): void { $this->registerConfig($container); $this->registerDatabase($container); @@ -69,9 +68,8 @@ public function registerBaseServices(Container $container) * Register the admin services. * * @param Container $container A DI container. - * @return void */ - public function registerAdminServices(Container $container) + public function registerAdminServices(Container $container): void { $this->registerBaseUrl($container); $this->registerAdminConfig($container); @@ -81,67 +79,57 @@ public function registerAdminServices(Container $container) * Setup the application's base URI. * * @param Container $container A DI container. - * @return void */ - public function registerBaseUrl(Container $container) + public function registerBaseUrl(Container $container): void { - $container['base-url'] = function () { - return Uri::createFromString(''); - }; + $container['base-url'] = (fn() => Uri::createFromString('')); - $container['admin/base-url'] = function () { - return Uri::createFromString('admin'); - }; + $container['admin/base-url'] = (fn() => Uri::createFromString('admin')); } /** * Setup the application configset. * * @param Container $container A DI container. - * @return void */ - public function registerConfig(Container $container) + public function registerConfig(Container $container): void { - $container['config'] = function () { - return new AppConfig([ - 'base_path' => realpath(__DIR__ . '/../../..'), - 'apis' => [ - 'google' => [ - 'recaptcha' => [ - 'public_key' => 'foobar', - 'private_key' => 'bazqux', - ], - ], - ], - 'locales' => [ - 'en' => [ - 'locale' => 'en-US', + $container['config'] = (fn(): \Charcoal\App\AppConfig => new AppConfig([ + 'base_path' => realpath(__DIR__ . '/../../..'), + 'apis' => [ + 'google' => [ + 'recaptcha' => [ + 'public_key' => 'foobar', + 'private_key' => 'bazqux', ], ], - 'translator' => [ - 'paths' => [], + ], + 'locales' => [ + 'en' => [ + 'locale' => 'en-US', ], - 'metadata' => [ - 'paths' => [ - 'metadata', - // Standalone - 'vendor/charcoal/object/metadata', - 'vendor/charcoal/user/metadata', - // Monorepo - '/../object/metadata', - '/../user/metadata', - ], + ], + 'translator' => [ + 'paths' => [], + ], + 'metadata' => [ + 'paths' => [ + 'metadata', + // Standalone + 'vendor/charcoal/object/metadata', + 'vendor/charcoal/user/metadata', + // Monorepo + '/../object/metadata', + '/../user/metadata', ], - ]); - }; + ], + ])); /** * List of Charcoal module classes. * * Explicitly defined in case of a version mismatch with dependencies. This parameter * is normally defined by {@see \Charcoal\App\ServiceProvider\AppServiceProvider}. - * - * @var array */ $container['module/classes'] = []; } @@ -150,22 +138,18 @@ public function registerConfig(Container $container) * Setup the admin module configset. * * @param Container $container A DI container. - * @return void */ - public function registerAdminConfig(Container $container) + public function registerAdminConfig(Container $container): void { $this->registerConfig($container); - $container['admin/config'] = function () { - return new AdminConfig(); - }; + $container['admin/config'] = (fn(): \Charcoal\Admin\Config => new AdminConfig()); } /** * @param Container $container A DI container. - * @return void */ - public function registerClimate(Container $container) + public function registerClimate(Container $container): void { $container['climate/system'] = function () { $system = Mockery::mock(Linux::class); @@ -192,11 +176,9 @@ public function registerClimate(Container $container) return $reader; }; - $container['climate/util'] = function (Container $container) { - return new UtilFactory($container['climate/system']); - }; + $container['climate/util'] = (fn(Container $container): \League\CLImate\Util\UtilFactory => new UtilFactory($container['climate/system'])); - $container['climate'] = function (Container $container) { + $container['climate'] = function (Container $container): \League\CLImate\CLImate { $climate = new CLImate(); $climate->setOutput($container['climate/output']); @@ -209,11 +191,10 @@ public function registerClimate(Container $container) /** * @param Container $container A DI container. - * @return void */ - public function registerDatabase(Container $container) + public function registerDatabase(Container $container): void { - $container['database'] = function () { + $container['database'] = function (): \PDO { $pdo = new PDO('sqlite::memory:'); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); return $pdo; @@ -222,9 +203,8 @@ public function registerDatabase(Container $container) /** * @param Container $container A DI container. - * @return void */ - public function registerModelServiceProvider(Container $container) + public function registerModelServiceProvider(Container $container): void { static $provider = null; @@ -237,9 +217,8 @@ public function registerModelServiceProvider(Container $container) /** * @param Container $container A DI container. - * @return void */ - public function registerAuthServiceProvider(Container $container) + public function registerAuthServiceProvider(Container $container): void { static $provider = null; @@ -252,9 +231,8 @@ public function registerAuthServiceProvider(Container $container) /** * @param Container $container A DI container. - * @return void */ - public function registerTranslatorServiceProvider(Container $container) + public function registerTranslatorServiceProvider(Container $container): void { static $provider = null; @@ -267,9 +245,8 @@ public function registerTranslatorServiceProvider(Container $container) /** * @param Container $container A DI container. - * @return void */ - public function registerViewServiceProvider(Container $container) + public function registerViewServiceProvider(Container $container): void { static $provider = null; @@ -282,9 +259,8 @@ public function registerViewServiceProvider(Container $container) /** * @param Container $container A DI container. - * @return void */ - public function registerAdminServiceProvider(Container $container) + public function registerAdminServiceProvider(Container $container): void { static $provider = null; @@ -297,23 +273,17 @@ public function registerAdminServiceProvider(Container $container) /** * @param Container $container A DI container. - * @return void */ - public function registerLogger(Container $container) + public function registerLogger(Container $container): void { - $container['logger'] = function () { - return new NullLogger(); - }; + $container['logger'] = (fn(): \Psr\Log\NullLogger => new NullLogger()); } /** * @param Container $container A DI container. - * @return void */ - public function registerCache(Container $container) + public function registerCache(Container $container): void { - $container['cache'] = function () { - return new Pool(); - }; + $container['cache'] = (fn(): \Stash\Pool => new Pool()); } } diff --git a/packages/cache/composer.json b/packages/cache/composer.json index cb723fa7e..6849cb5c2 100644 --- a/packages/cache/composer.json +++ b/packages/cache/composer.json @@ -1,8 +1,12 @@ { - "type": "library", "name": "charcoal/cache", "description": "Charcoal service provider for the Stash Cache Library", - "keywords": ["charcoal", "caching", "cache", "stash"], + "keywords": [ + "charcoal", + "caching", + "cache", + "stash" + ], "homepage": "https://locomotivemtl.github.io/charcoal-config/", "license": "MIT", "authors": [ @@ -15,13 +19,9 @@ "homepage": "https://locomotive.ca" } ], - "extra": { - "branch-alias": { - "dev-main": "5.x-dev" - } - }, + "type": "library", "require": { - "php": "^7.4 || ^8.0", + "php": "^8.3", "charcoal/config": "^5.1", "pimple/pimple": "^3.0", "psr/cache": "^1.0", @@ -30,7 +30,7 @@ "require-dev": { "psr/log": "^1.0", "slim/slim": "^3.7", - "phpunit/phpunit": "^9.5", + "phpunit/phpunit": "^12.5", "squizlabs/php_codesniffer": "^3.5", "php-coveralls/php-coveralls": "^2.2" }, @@ -44,8 +44,10 @@ "Charcoal\\Tests\\": "tests/Charcoal/" } }, - "replace": { - "locomotivemtl/charcoal-cache": "*" + "extra": { + "branch-alias": { + "dev-main": "5.x-dev" + } }, "scripts": { "test": [ @@ -61,6 +63,9 @@ "phpcbf": "php vendor/bin/phpcbf -ps --colors src/ tests/", "phpunit": "php vendor/bin/phpunit --coverage-text" }, + "replace": { + "locomotivemtl/charcoal-cache": "*" + }, "minimum-stability": "dev", "prefer-stable": true } diff --git a/packages/cache/src/Charcoal/Cache/CacheBuilder.php b/packages/cache/src/Charcoal/Cache/CacheBuilder.php index a8b2123fa..ef099014d 100644 --- a/packages/cache/src/Charcoal/Cache/CacheBuilder.php +++ b/packages/cache/src/Charcoal/Cache/CacheBuilder.php @@ -27,31 +27,23 @@ final class CacheBuilder /** * Default logger instance. - * - * @var \Psr\Log\LoggerInterface|null */ - private $logger; + private ?\Psr\Log\LoggerInterface $logger = null; /** * Default namespace for new pools. - * - * @var string|null */ - private $namespace; + private ?string $namespace = null; /** * Default "Pool" class to use for making new pools. - * - * @var string */ - private $poolClass = Pool::class; + private string $poolClass = Pool::class; /** * Default "Item" class to use for making new items. - * - * @var string|null */ - private $itemClass; + private ?string $itemClass = null; /** * Create a cache pool builder. @@ -88,7 +80,7 @@ public function __construct(array $data) * @param mixed $poolOptions Optional settings for the new pool. * @return PoolInterface */ - public function __invoke($cacheDriver, $poolOptions = null) + public function __invoke($cacheDriver, $poolOptions = null): object { return $this->build($cacheDriver, $poolOptions); } @@ -105,7 +97,7 @@ public function __invoke($cacheDriver, $poolOptions = null) * Otherwise, the default settings are used. * @return PoolInterface */ - public function build($cacheDriver, $poolOptions = null) + public function build($cacheDriver, $poolOptions = null): object { if (!($cacheDriver instanceof DriverInterface)) { $cacheDriver = $this->resolveDriver($cacheDriver); @@ -123,9 +115,8 @@ public function build($cacheDriver, $poolOptions = null) * Prepare any pool options for the new pool object. * * @param mixed $options Settings for the new pool. - * @return array */ - private function parsePoolOptions($options) + private function parsePoolOptions($options): array { $defaults = [ 'pool_class' => $this->poolClass, @@ -156,9 +147,8 @@ private function parsePoolOptions($options) * * @param PoolInterface $pool The new pool. * @param array $options Settings for the new pool. - * @return void */ - private function applyPoolOptions(PoolInterface $pool, array $options) + private function applyPoolOptions(PoolInterface $pool, array $options): void { if (isset($options['logger'])) { $pool->setLogger($options['logger']); @@ -188,7 +178,7 @@ private function resolveDriver($driver) foreach ($driver as $drv) { try { return $this->resolveOneDriver($drv); - } catch (InvalidArgumentException $e) { + } catch (InvalidArgumentException) { continue; } } @@ -223,7 +213,7 @@ private function resolveOneDriver($driver) } else { throw new InvalidArgumentException(sprintf( 'Driver class %s must implement %s', - get_class($driver), + $driver::class, DriverInterface::class )); } @@ -246,7 +236,7 @@ private function resolveOneDriver($driver) throw new InvalidArgumentException(sprintf( 'Driver "%s": Class %s must implement %s', $name, - get_class($driver), + $driver::class, DriverInterface::class )); } @@ -267,9 +257,8 @@ private function resolveOneDriver($driver) * * @param ArrayAccess|array $drivers The driver list used to create cache drivers. * @throws InvalidArgumentException If the drivers list is invalid. - * @return void */ - private function setDrivers($drivers) + private function setDrivers($drivers): void { if ($this->isAccessible($drivers)) { $this->drivers = $drivers; @@ -285,12 +274,11 @@ private function setDrivers($drivers) * * @param \Psr\Log\LoggerInterface $logger A PSR-3 logger. * @throws InvalidArgumentException If the logger is invalid PSR-3 client. - * @return void */ - private function setLogger($logger) + private function setLogger($logger): void { - $psr = 'Psr\\Log\\LoggerInterface'; - if (!is_a($logger, $psr)) { + $psr = \Psr\Log\LoggerInterface::class; + if (!$logger instanceof $psr) { throw new InvalidArgumentException( sprintf('Expected an instance of %s', $psr) ); @@ -306,9 +294,8 @@ private function setLogger($logger) * * @param string $namespace The pool namespace. * @throws InvalidArgumentException If the namespaces is invalid. - * @return void */ - private function setNamespace($namespace) + private function setNamespace($namespace): void { if (!ctype_alnum($namespace)) { throw new InvalidArgumentException( @@ -326,9 +313,8 @@ private function setNamespace($namespace) * * @param string $class The pool class name. * @throws InvalidArgumentException When passed an invalid or nonexistant class. - * @return void */ - private function setPoolClass($class) + private function setPoolClass($class): void { if (!class_exists($class)) { throw new InvalidArgumentException( @@ -356,9 +342,8 @@ private function setPoolClass($class) * * @param string $class The item class name. * @throws InvalidArgumentException When passed an invalid or nonexistant class. - * @return void */ - private function setItemClass($class) + private function setItemClass($class): void { if (!class_exists($class)) { throw new InvalidArgumentException( @@ -385,9 +370,9 @@ private function setItemClass($class) * @param mixed $var The value to check * @return boolean TRUE if $var is iterable, FALSE otherwise. */ - private function isIterable($var) + private function isIterable($var): bool { - return is_array($var) || ($var instanceof Traversable); + return is_iterable($var); } /** @@ -396,7 +381,7 @@ private function isIterable($var) * @param mixed $var The value to check * @return boolean TRUE if $var is an array or accessible like an array, FALSE otherwise. */ - private function isAccessible($var) + private function isAccessible($var): bool { return is_array($var) || ($var instanceof ArrayAccess); } diff --git a/packages/cache/src/Charcoal/Cache/CacheConfig.php b/packages/cache/src/Charcoal/Cache/CacheConfig.php index 041df2bc9..2dfa57880 100644 --- a/packages/cache/src/Charcoal/Cache/CacheConfig.php +++ b/packages/cache/src/Charcoal/Cache/CacheConfig.php @@ -33,40 +33,31 @@ class CacheConfig extends AbstractConfig * Note: * - When TRUE, the {@see self::$types} are used. * - When FALSE, the "memory" type is used. - * - * @var boolean */ - private $active = true; + private bool $active = true; /** * Cache type(s) to use. * * Represents a cache driver. - * - * @var array */ - private $types; + private ?array $types = null; /** * Default maximum time an item will be cached. - * - * @var integer */ - private $defaultTtl = self::WEEK_IN_SECONDS; + private int $defaultTtl = self::WEEK_IN_SECONDS; /** * Cache namespace. - * - * @var string */ - private $prefix = self::DEFAULT_NAMESPACE; + private string $prefix = self::DEFAULT_NAMESPACE; /** * Retrieve the default values. - * - * @return array */ - public function defaults() + #[\Override] + public function defaults(): array { return [ 'active' => true, @@ -83,9 +74,9 @@ public function defaults() * TRUE to enable, FALSE to disable. * @return CacheConfig Chainable */ - public function setActive($active) + public function setActive($active): static { - $this->active = !!$active; + $this->active = (bool) $active; return $this; } @@ -94,7 +85,7 @@ public function setActive($active) * * @return boolean TRUE if enabled, FALSE if disabled. */ - public function active() + public function active(): bool { return $this->active; } @@ -107,7 +98,7 @@ public function active() * @param string[] $types One or more types to try as cache driver until success. * @return CacheConfig Chainable */ - public function setTypes(array $types) + public function setTypes(array $types): static { $this->types = []; $this->addTypes($types); @@ -120,7 +111,7 @@ public function setTypes(array $types) * @param string[] $types One or more types to try as cache driver until success. * @return CacheConfig Chainable */ - public function addTypes(array $types) + public function addTypes(array $types): static { foreach ($types as $type) { $this->addType($type); @@ -135,7 +126,7 @@ public function addTypes(array $types) * @throws InvalidArgumentException If the type is not a string or unsupported. * @return CacheConfig Chainable */ - public function addType($type) + public function addType($type): static { if (!in_array($type, $this->validTypes())) { throw new InvalidArgumentException( @@ -153,10 +144,8 @@ public function addType($type) * Note: * 1. The default cache type is always appended. * 2. Duplicate types are removed. - * - * @return array */ - public function types() + public function types(): array { $types = ($this->types + self::DEFAULT_TYPES); return array_keys($types); @@ -167,7 +156,7 @@ public function types() * * @return string[] */ - public function defaultTypes() + public function defaultTypes(): array { return array_keys(self::DEFAULT_TYPES); } @@ -177,7 +166,7 @@ public function defaultTypes() * * @return string[] */ - public function validTypes() + public function validTypes(): array { return [ 'apc', @@ -197,7 +186,7 @@ public function validTypes() * @throws InvalidArgumentException If the TTL is not numeric. * @return CacheConfig Chainable */ - public function setDefaultTtl($ttl) + public function setDefaultTtl($ttl): static { if (!is_numeric($ttl)) { throw new InvalidArgumentException( @@ -211,10 +200,8 @@ public function setDefaultTtl($ttl) /** * Retrieve the default time-to-live for cached items. - * - * @return integer */ - public function defaultTtl() + public function defaultTtl(): int { return $this->defaultTtl; } @@ -226,7 +213,7 @@ public function defaultTtl() * @throws InvalidArgumentException If the prefix is not a string. * @return CacheConfig Chainable */ - public function setPrefix($prefix) + public function setPrefix($prefix): static { if (!is_string($prefix)) { throw new InvalidArgumentException( @@ -247,10 +234,8 @@ public function setPrefix($prefix) /** * Retrieve the cache namespace. - * - * @return string */ - public function prefix() + public function prefix(): string { return $this->prefix; } diff --git a/packages/cache/src/Charcoal/Cache/CachePoolAwareTrait.php b/packages/cache/src/Charcoal/Cache/CachePoolAwareTrait.php index 644d47dd7..e2039c0af 100644 --- a/packages/cache/src/Charcoal/Cache/CachePoolAwareTrait.php +++ b/packages/cache/src/Charcoal/Cache/CachePoolAwareTrait.php @@ -41,7 +41,7 @@ protected function cachePool() if ($this->cachePool === null) { throw new RuntimeException(sprintf( 'Cache Pool is not defined for "%s"', - get_class($this) + $this::class )); } diff --git a/packages/cache/src/Charcoal/Cache/Facade/CachePoolFacade.php b/packages/cache/src/Charcoal/Cache/Facade/CachePoolFacade.php index b053e8368..9040aefe2 100644 --- a/packages/cache/src/Charcoal/Cache/Facade/CachePoolFacade.php +++ b/packages/cache/src/Charcoal/Cache/Facade/CachePoolFacade.php @@ -61,7 +61,7 @@ public function __construct(array $data) * @param mixed $ttl An integer, interval, date, or NULL to use the facade's default value. * @return mixed The value corresponding to this cache item's $key, or NULL if not found. */ - public function get($key, callable $resolve = null, $ttl = null) + public function get($key, ?callable $resolve = null, $ttl = null) { $pool = $this->cachePool(); $item = $pool->getItem($key); @@ -163,9 +163,8 @@ public function defaultTtl() * Set the facade's default time-to-live for cached items. * * @param mixed $ttl An integer, date interval, or date. - * @return void */ - public function setDefaultTtl($ttl) + public function setDefaultTtl($ttl): void { $this->defaultTtl = $ttl; } diff --git a/packages/cache/src/Charcoal/Cache/Middleware/CacheMiddleware.php b/packages/cache/src/Charcoal/Cache/Middleware/CacheMiddleware.php index 1c94a670c..1a89bb03f 100644 --- a/packages/cache/src/Charcoal/Cache/Middleware/CacheMiddleware.php +++ b/packages/cache/src/Charcoal/Cache/Middleware/CacheMiddleware.php @@ -44,14 +44,14 @@ class CacheMiddleware * * @var string[] */ - private $methods; + private readonly array $methods; /** * Cache response if the request matches one of the HTTP status codes. * * @var integer[] */ - private $statusCodes; + private readonly array $statusCodes; /** * Time-to-live in seconds. @@ -142,10 +142,8 @@ public function __construct(array $data) /** * Default middleware options. - * - * @return array */ - public function defaults() + public function defaults(): array { return [ 'ttl' => CacheConfig::DAY_IN_SECONDS, @@ -191,7 +189,7 @@ public function __invoke(RequestInterface $request, ResponseInterface $response, return $next($request, $response); } - if ($this->isSkipCache($request)) { + if ($this->isSkipCache()) { return $next($request, $response); } @@ -230,7 +228,7 @@ public function __invoke(RequestInterface $request, ResponseInterface $response, if (!$this->isQueryIncluded($query)) { $queryArr = $this->parseIgnoredParams($query); - if (!empty($queryArr)) { + if ($queryArr !== []) { return $this->disableCacheHeadersOnResponse($response); } } @@ -286,20 +284,16 @@ private function cacheKeyFromRequest(RequestInterface $request) * Determine if the HTTP request method matches the accepted choices. * * @param RequestInterface $request The PSR-7 HTTP request. - * @return boolean */ - private function isRequestMethodValid(RequestInterface $request) + private function isRequestMethodValid(RequestInterface $request): bool { return in_array($request->getMethod(), $this->methods); } /** * Determine if the HTTP request method matches the accepted choices. - * - * @param RequestInterface $request The PSR-7 HTTP request. - * @return boolean */ - private function isSkipCache(RequestInterface $request) + private function isSkipCache(): bool { if (isset($this->skipCache['session_vars'])) { $skip = $this->skipCache['session_vars']; @@ -315,7 +309,6 @@ private function isSkipCache(RequestInterface $request) } } } - return false; } @@ -323,9 +316,8 @@ private function isSkipCache(RequestInterface $request) * Determine if the HTTP response status matches the accepted choices. * * @param ResponseInterface $response The PSR-7 HTTP response. - * @return boolean */ - private function isResponseStatusValid(ResponseInterface $response) + private function isResponseStatusValid(ResponseInterface $response): bool { return in_array($response->getStatusCode(), $this->statusCodes); } @@ -334,9 +326,8 @@ private function isResponseStatusValid(ResponseInterface $response) * Determine if the request should be cached based on the URI path. * * @param string $path The request path (route) to verify. - * @return boolean */ - private function isPathIncluded($path) + private function isPathIncluded($path): bool { if ($this->includedPath === '*') { return true; @@ -345,23 +336,15 @@ private function isPathIncluded($path) if (empty($this->includedPath) && !is_numeric($this->includedPath)) { return false; } - - foreach ((array)$this->includedPath as $included) { - if (preg_match('@' . $included . '@', $path)) { - return true; - } - } - - return false; + return array_any((array)$this->includedPath, fn($included): int|false => preg_match('@' . $included . '@', $path)); } /** * Determine if the request should NOT be cached based on the URI path. * * @param string $path The request path (route) to verify. - * @return boolean */ - private function isPathExcluded($path) + private function isPathExcluded($path): bool { if ($this->excludedPath === '*') { return true; @@ -370,14 +353,7 @@ private function isPathExcluded($path) if (empty($this->excludedPath) && !is_numeric($this->excludedPath)) { return false; } - - foreach ((array)$this->excludedPath as $excluded) { - if (preg_match('@' . $excluded . '@', $path)) { - return true; - } - } - - return false; + return array_any((array)$this->excludedPath, fn($excluded): int|false => preg_match('@' . $excluded . '@', $path)); } /** @@ -388,7 +364,7 @@ private function isPathExcluded($path) */ private function isQueryIncluded(array $queryParams) { - if (empty($queryParams)) { + if ($queryParams === []) { return true; } @@ -412,7 +388,7 @@ private function isQueryIncluded(array $queryParams) */ private function isQueryExcluded(array $queryParams) { - if (empty($queryParams)) { + if ($queryParams === []) { return false; } @@ -432,11 +408,10 @@ private function isQueryExcluded(array $queryParams) * Returns the query parameters that are NOT ignored. * * @param array $queryParams The query parameters to filter. - * @return array */ - private function parseIgnoredParams(array $queryParams) + private function parseIgnoredParams(array $queryParams): array { - if (empty($queryParams)) { + if ($queryParams === []) { return $queryParams; } @@ -479,9 +454,8 @@ private function disableCacheHeadersOnResponse(ResponseInterface $response) /** * @param Closure|null $processCacheKeyCallback ProcessCacheKeyCallback for CacheMiddleware. - * @return self */ - public function setProcessCacheKeyCallback($processCacheKeyCallback) + public function setProcessCacheKeyCallback($processCacheKeyCallback): static { $this->processCacheKeyCallback = $processCacheKeyCallback; diff --git a/packages/cache/src/Charcoal/Cache/ServiceProvider/CacheServiceProvider.php b/packages/cache/src/Charcoal/Cache/ServiceProvider/CacheServiceProvider.php index 9070ee892..3296ad352 100644 --- a/packages/cache/src/Charcoal/Cache/ServiceProvider/CacheServiceProvider.php +++ b/packages/cache/src/Charcoal/Cache/ServiceProvider/CacheServiceProvider.php @@ -44,9 +44,8 @@ class CacheServiceProvider implements ServiceProviderInterface { /** * @param Container $container A container instance. - * @return void */ - public function register(Container $container) + public function register(Container $container): void { $this->registerDrivers($container); $this->registerService($container); @@ -55,14 +54,11 @@ public function register(Container $container) /** * @param Container $container A container instance. - * @return void */ - public function registerDrivers(Container $container) + public function registerDrivers(Container $container): void { /** * The collection of cache drivers that are supported by this system. - * - * @var array An associative array structured as `"Driver Name" => "Class Name"`. */ $container['cache/available-drivers'] = DriverList::getAvailableDrivers(); @@ -72,14 +68,14 @@ public function registerDrivers(Container $container) * @param Container $container The service container. * @return Container Service container of cache drivers from Stash. */ - $container['cache/drivers'] = function (Container $container) { + $container['cache/drivers'] = function (Container $container): \Pimple\Container { $drivers = new Container(); /** * @param Container $container The service container. * @return \Stash\Driver\Apc|null */ - $drivers['apc'] = function () use ($container) { + $drivers['apc'] = function () use ($container): ?object { $drivers = $container['cache/available-drivers']; if (!isset($drivers['Apc'])) { // Apc is not available on system @@ -99,7 +95,7 @@ public function registerDrivers(Container $container) * @param Container $container A container instance. * @return \Stash\Driver\Sqlite|null */ - $drivers['db'] = function () use ($container) { + $drivers['db'] = function () use ($container): ?object { $drivers = $container['cache/available-drivers']; if (!isset($drivers['SQLite'])) { // SQLite is not available on system @@ -113,7 +109,7 @@ public function registerDrivers(Container $container) * @param Container $container A container instance. * @return \Stash\Driver\FileSystem */ - $drivers['file'] = function () use ($container) { + $drivers['file'] = function () use ($container): object { $drivers = $container['cache/available-drivers']; return new $drivers['FileSystem'](); }; @@ -122,7 +118,7 @@ public function registerDrivers(Container $container) * @param Container $container A container instance. * @return \Stash\Driver\Memcache|null */ - $drivers['memcache'] = function () use ($container) { + $drivers['memcache'] = function () use ($container): ?object { $drivers = $container['cache/available-drivers']; if (!isset($drivers['Memcache'])) { // Memcache is not available on system @@ -152,7 +148,7 @@ public function registerDrivers(Container $container) * @param Container $container A container instance. * @return \Stash\Driver\Ephemeral */ - $drivers['memory'] = function () use ($container) { + $drivers['memory'] = function () use ($container): object { $drivers = $container['cache/available-drivers']; return new $drivers['Ephemeral'](); }; @@ -161,7 +157,7 @@ public function registerDrivers(Container $container) * @param Container $container A container instance. * @return \Stash\Driver\BlackHole */ - $drivers['noop'] = function () use ($container) { + $drivers['noop'] = function () use ($container): object { $drivers = $container['cache/available-drivers']; return new $drivers['BlackHole'](); }; @@ -170,7 +166,7 @@ public function registerDrivers(Container $container) * @param Container $container A container instance. * @return \Stash\Driver\Redis|null */ - $drivers['redis'] = function () use ($container) { + $drivers['redis'] = function () use ($container): ?object { $drivers = $container['cache/available-drivers']; if (!isset($drivers['Redis'])) { // Redis is not available on system @@ -186,9 +182,8 @@ public function registerDrivers(Container $container) /** * @param Container $container A container instance. - * @return void */ - public function registerService(Container $container) + public function registerService(Container $container): void { /** * The cache configset. @@ -196,9 +191,9 @@ public function registerService(Container $container) * @param Container $container The service container. * @return CacheConfig */ - $container['cache/config'] = function (Container $container) { - $appConfig = isset($container['config']) ? $container['config'] : []; - $cacheConfig = isset($appConfig['cache']) ? $appConfig['cache'] : null; + $container['cache/config'] = function (Container $container): \Charcoal\Cache\CacheConfig { + $appConfig = $container['config'] ?? []; + $cacheConfig = $appConfig['cache'] ?? null; return new CacheConfig($cacheConfig); }; @@ -208,7 +203,7 @@ public function registerService(Container $container) * @param Container $container A Pimple DI container. * @return CacheBuilder */ - $container['cache/builder'] = function (Container $container) { + $container['cache/builder'] = function (Container $container): \Charcoal\Cache\CacheBuilder { $cacheConfig = $container['cache/config']; return new CacheBuilder([ @@ -224,9 +219,7 @@ public function registerService(Container $container) * @param Container $container The service container. * @return DriverInterface Primary cache driver from Stash. */ - $container['cache/driver'] = $container->factory(function (Container $container) { - return $container['cache']->getDriver(); - }); + $container['cache/driver'] = $container->factory(fn(Container $container) => $container['cache']->getDriver()); /** * The main cache item pool. @@ -238,11 +231,7 @@ public function registerService(Container $container) $cacheBuilder = $container['cache/builder']; $cacheConfig = $container['cache/config']; - if ($cacheConfig['active'] === true) { - $cacheDrivers = $cacheConfig['types']; - } else { - $cacheDrivers = $cacheConfig['default_types']; - } + $cacheDrivers = $cacheConfig['active'] === true ? $cacheConfig['types'] : $cacheConfig['default_types']; return $cacheBuilder($cacheDrivers); }; @@ -253,7 +242,7 @@ public function registerService(Container $container) * @param Container $container The service container. * @return CachePoolFacade The facade for the main cache pool. */ - $container['cache/facade'] = function (Container $container) { + $container['cache/facade'] = function (Container $container): \Charcoal\Cache\Facade\CachePoolFacade { $args = [ 'cache' => $container['cache'], ]; @@ -269,9 +258,8 @@ public function registerService(Container $container) /** * @param Container $container A container instance. - * @return void */ - private function registerMiddleware(Container $container) + private function registerMiddleware(Container $container): void { /** * The cache middleware configset. @@ -280,7 +268,7 @@ private function registerMiddleware(Container $container) * @return array */ $container['cache/middleware/config'] = function (Container $container) { - $appConfig = isset($container['config']) ? $container['config'] : []; + $appConfig = $container['config'] ?? []; if (isset($appConfig['middlewares']['charcoal/cache/middleware/cache'])) { $wareConfig = $appConfig['middlewares']['charcoal/cache/middleware/cache']; @@ -299,8 +287,6 @@ private function registerMiddleware(Container $container) * @param Container $container A container instance. * @return CacheMiddleware */ - $container['middlewares/charcoal/cache/middleware/cache'] = function (Container $container) { - return new CacheMiddleware($container['cache/middleware/config']); - }; + $container['middlewares/charcoal/cache/middleware/cache'] = (fn(Container $container): \Charcoal\Cache\Middleware\CacheMiddleware => new CacheMiddleware($container['cache/middleware/config'])); } } diff --git a/packages/cache/tests/Charcoal/AbstractTestCase.php b/packages/cache/tests/Charcoal/AbstractTestCase.php index 59ba12ea0..80f1772c4 100644 --- a/packages/cache/tests/Charcoal/AbstractTestCase.php +++ b/packages/cache/tests/Charcoal/AbstractTestCase.php @@ -1,5 +1,7 @@ assertEquals('charcoal', CacheConfig::DEFAULT_NAMESPACE); $this->assertEquals((60 * 60), CacheConfig::HOUR_IN_SECONDS); @@ -70,11 +73,7 @@ public function testDefaults() $this->assertEquals($defaults['prefix'], $this->cfg->prefix()); } - /** - * @covers ::setActive - * @covers ::active - */ - public function testActive() + public function testActive(): void { // Chainable $that = $this->cfg->setActive(false); @@ -84,11 +83,7 @@ public function testActive() $this->assertFalse($this->cfg->active()); } - /** - * @covers ::setTypes - * @covers ::types - */ - public function testReplaceDrivers() + public function testReplaceDrivers(): void { // Chainable $that = $this->cfg->setTypes([ 'memcache', 'noop' ]); @@ -99,10 +94,7 @@ public function testReplaceDrivers() $this->assertEquals([ 'memcache', 'noop', 'memory' ], $types); } - /** - * @covers ::types - */ - public function testUniqueDrivers() + public function testUniqueDrivers(): void { $this->cfg->setTypes([ 'memcache', 'memory', 'file', 'memcache' ]); @@ -110,12 +102,7 @@ public function testUniqueDrivers() $this->assertEquals([ 'memcache', 'memory', 'file' ], $types); } - /** - * @covers ::addTypes - * @covers ::addType - * @covers ::types - */ - public function testAddDrivers() + public function testAddDrivers(): void { // Chainable $that = $this->cfg->addTypes([ 'memcache', 'noop' ]); @@ -128,22 +115,14 @@ public function testAddDrivers() $this->assertContains('noop', $types); } - /** - * @covers ::validTypes - * @covers ::addType - */ - public function testAddDriverOnInvalidType() + public function testAddDriverOnInvalidType(): void { $this->expectExceptionMessage('Invalid cache type: "foobar"'); $this->expectException(InvalidArgumentException::class); $this->cfg->addType('foobar'); } - /** - * @covers ::setDefaultTtl - * @covers ::defaultTtl - */ - public function testDefaultTtl() + public function testDefaultTtl(): void { // Chainable $that = $this->cfg->setDefaultTtl(42); @@ -153,21 +132,14 @@ public function testDefaultTtl() $this->assertEquals(42, $this->cfg->defaultTtl()); } - /** - * @covers ::setDefaultTtl - */ - public function testSetDefaultTtlOnInvalidType() + public function testSetDefaultTtlOnInvalidType(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('TTL must be an integer (seconds)'); $this->cfg->setDefaultTtl('foo'); } - /** - * @covers ::setPrefix - * @covers ::prefix - */ - public function testPrefix() + public function testPrefix(): void { // Chainable $that = $this->cfg->setPrefix('foo'); @@ -177,20 +149,14 @@ public function testPrefix() $this->assertEquals('foo', $this->cfg->prefix()); } - /** - * @covers ::setPrefix - */ - public function testSetPrefixOnInvalidType() + public function testSetPrefixOnInvalidType(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Prefix must be a string'); $this->cfg->setPrefix(false); } - /** - * @covers ::setPrefix - */ - public function testSetPrefixOnInvalidValue() + public function testSetPrefixOnInvalidValue(): void { $this->expectExceptionMessage('Prefix must be alphanumeric'); $this->expectException(InvalidArgumentException::class); diff --git a/packages/cache/tests/Charcoal/Cache/CachePoolAwareTest.php b/packages/cache/tests/Charcoal/Cache/CachePoolAwareTest.php index 2b9762e5b..37f708309 100644 --- a/packages/cache/tests/Charcoal/Cache/CachePoolAwareTest.php +++ b/packages/cache/tests/Charcoal/Cache/CachePoolAwareTest.php @@ -11,16 +11,13 @@ /** * Test CachePoolAwareTrait - * - * @coversDefaultClass \Charcoal\Cache\CachePoolAwareTrait */ +#[\PHPUnit\Framework\Attributes\CoversTrait(\Charcoal\Cache\CachePoolAwareTrait::class)] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\CachePoolAwareTrait::class, 'setCachePool')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\CachePoolAwareTrait::class, 'cachePool')] class CachePoolAwareTest extends AbstractTestCase { - /** - * @covers ::setCachePool - * @covers ::cachePool - */ - public function testCachePool() + public function testCachePool(): void { $obj = new CachePoolAware(); $pool = new Pool(); @@ -31,9 +28,8 @@ public function testCachePool() /** * testSetPrefixOnInvalidValue - * @covers ::cachePool */ - public function testMissingPool() + public function testMissingPool(): void { $this->expectExceptionMessage('Cache Pool is not defined for "Charcoal\Tests\Mocks\CachePoolAware"'); $this->expectException(\RuntimeException::class); diff --git a/packages/cache/tests/Charcoal/Cache/CachePoolTrait.php b/packages/cache/tests/Charcoal/Cache/CachePoolTrait.php index 7e5adb1eb..bcbbeb46c 100644 --- a/packages/cache/tests/Charcoal/Cache/CachePoolTrait.php +++ b/packages/cache/tests/Charcoal/Cache/CachePoolTrait.php @@ -1,5 +1,7 @@ setNamespace('tests'); diff --git a/packages/cache/tests/Charcoal/Cache/Facade/CachePoolFacadeTest.php b/packages/cache/tests/Charcoal/Cache/Facade/CachePoolFacadeTest.php index 561c8839b..62b721246 100644 --- a/packages/cache/tests/Charcoal/Cache/Facade/CachePoolFacadeTest.php +++ b/packages/cache/tests/Charcoal/Cache/Facade/CachePoolFacadeTest.php @@ -23,9 +23,16 @@ * Test CachePoolFacade * * This class is based on {@see \Stash\Test\AbstractPoolTest}. - * - * @coversDefaultClass \Charcoal\Cache\Facade\CachePoolFacade */ +#[\PHPUnit\Framework\Attributes\CoversClass(\Charcoal\Cache\Facade\CachePoolFacade::class)] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\Facade\CachePoolFacade::class, '__construct')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\Facade\CachePoolFacade::class, 'get')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\Facade\CachePoolFacade::class, 'save')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\Facade\CachePoolFacade::class, 'has')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\Facade\CachePoolFacade::class, 'set')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\Facade\CachePoolFacade::class, 'delete')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\Facade\CachePoolFacade::class, 'defaultTtl')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\Facade\CachePoolFacade::class, 'setDefaultTtl')] class CachePoolFacadeTest extends AbstractTestCase { use CachePoolTrait; @@ -43,8 +50,6 @@ class CachePoolFacadeTest extends AbstractTestCase /** * Prepare the cache pool. - * - * @return void */ public function setUp(): void { @@ -53,8 +58,6 @@ public function setUp(): void /** * Empty the cache pool. - * - * @return void */ public function tearDown(): void { @@ -65,9 +68,8 @@ public function tearDown(): void * Create a new CachePoolFacade instance. * * @param array $args Parameters for the initialization of a CachePoolFacade. - * @return CachePoolFacade */ - protected function facadeFactory(array $args = []) + protected function facadeFactory(array $args = []): \Charcoal\Cache\Facade\CachePoolFacade { if (!isset($args['cache'])) { $args['cache'] = static::getCachePool(); @@ -76,10 +78,7 @@ protected function facadeFactory(array $args = []) return new CachePoolFacade($args); } - /** - * @covers ::__construct - */ - public function testConstruct() + public function testConstruct(): void { $facade = $this->facadeFactory([ 'default_ttl' => 120, @@ -88,13 +87,7 @@ public function testConstruct() $this->assertInstanceOf(CachePoolFacade::class, $facade); } - /** - * @covers ::get - * @covers ::save - * - * @return void - */ - public function testGet() + public function testGet(): void { $facade = $this->facadeFactory(); @@ -105,19 +98,12 @@ public function testGet() $data = $facade->get('base/one'); $this->assertEquals($this->data, $data); - $func = function () { - return $this->data; - }; + $func = (fn() => $this->data); $data = $facade->get('base/two', $func); $this->assertEquals($this->data, $data); } - /** - * @covers ::has - * - * @return void - */ - public function testHas() + public function testHas(): void { $facade = $this->facadeFactory(); @@ -128,8 +114,6 @@ public function testHas() } /** - * @covers ::set - * @covers ::save * * @return CachePoolFacade To use the same cache pool facade for the next test. */ @@ -150,13 +134,10 @@ public function testSet() } /** - * @depends testSet - * @covers ::delete - * * @param CachePoolFacade $facade The cache pool facade from the previous test. - * @return void */ - public function testDelete(CachePoolFacade $facade) + #[\PHPUnit\Framework\Attributes\Depends('testSet')] + public function testDelete(CachePoolFacade $facade): void { $keys = array_keys($this->multiData); @@ -170,17 +151,15 @@ public function testDelete(CachePoolFacade $facade) /** * Test a numeric expiration time for this cache item. * - * @covers ::save * - * @dataProvider provideTtlOnSave * * @param DateTimeInterface $expected The expected expiration time * from {@see \Stash\Interfaces\ItemInterface::getExpiration()}. * @param mixed $itemTtl The cache item's expiration time. * @param DateTimeInterface $defaultTtl The facade default expiration time. - * @return void */ - public function testTtlOnSave(DateTimeInterface $expected, $itemTtl, DateTimeInterface $defaultTtl) + #[\PHPUnit\Framework\Attributes\DataProvider('provideTtlOnSave')] + public function testTtlOnSave(DateTimeInterface $expected, $itemTtl, DateTimeInterface $defaultTtl): void { $stash = static::getCachePool(); $facade = $this->facadeFactory([ @@ -199,9 +178,8 @@ public function testTtlOnSave(DateTimeInterface $expected, $itemTtl, DateTimeInt * Provide data for testing the expiration time per cache item. * * @used-by self::testTtlOnSave() - * @return array */ - public function provideTtlOnSave() + public static function provideTtlOnSave(): array { $data = []; $date = new DateTimeImmutable('now'); @@ -231,13 +209,7 @@ public function provideTtlOnSave() return $data; } - /** - * @covers ::defaultTtl - * @covers ::setDefaultTtl - * - * @return void - */ - public function testSetDefaultTtl() + public function testSetDefaultTtl(): void { $time = new \DateInterval('P1D'); $facade = $this->facadeFactory([ diff --git a/packages/cache/tests/Charcoal/Cache/Factory/AbstractCacheBuilderTest.php b/packages/cache/tests/Charcoal/Cache/Factory/AbstractCacheBuilderTest.php index 6494b8a2b..47c3ef256 100644 --- a/packages/cache/tests/Charcoal/Cache/Factory/AbstractCacheBuilderTest.php +++ b/packages/cache/tests/Charcoal/Cache/Factory/AbstractCacheBuilderTest.php @@ -105,7 +105,7 @@ public function getDefaultBuilderAttributes() public function getDefaultPoolAttributes() { return [ - 'item_class' => '\Stash\Item', + 'item_class' => \Stash\Item::class, 'namespace' => null, 'logger' => null, ]; @@ -115,9 +115,8 @@ public function getDefaultPoolAttributes() * Reports an error if $builder does not use the default options. * * @param CacheBuilder $builder The cache builder to test. - * @return void */ - public function assertCacheBuilderHasDefaultAttributes(CacheBuilder $builder) + public function assertCacheBuilderHasDefaultAttributes(CacheBuilder $builder): void { $builderDefaults = $this->getDefaultBuilderAttributes(); @@ -131,9 +130,8 @@ public function assertCacheBuilderHasDefaultAttributes(CacheBuilder $builder) * Reports an error if $pool does not use the default options. * * @param PoolInterface $pool The cache pool to test. - * @return void */ - public function assertCachePoolHasDefaultAttributes(PoolInterface $pool) + public function assertCachePoolHasDefaultAttributes(PoolInterface $pool): void { $builderDefaults = $this->getDefaultBuilderAttributes(); $poolDefaults = $this->getDefaultPoolAttributes(); diff --git a/packages/cache/tests/Charcoal/Cache/Factory/CacheBuilderClassTest.php b/packages/cache/tests/Charcoal/Cache/Factory/CacheBuilderClassTest.php index ca178955b..7f02abd06 100644 --- a/packages/cache/tests/Charcoal/Cache/Factory/CacheBuilderClassTest.php +++ b/packages/cache/tests/Charcoal/Cache/Factory/CacheBuilderClassTest.php @@ -14,42 +14,36 @@ /** * Test constructor and class attributes from the CacheBuilder. - * - * @coversDefaultClass \Charcoal\Cache\CacheBuilder */ +#[\PHPUnit\Framework\Attributes\CoversClass(\Charcoal\Cache\CacheBuilder::class)] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\CacheBuilder::class, '__construct')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\CacheBuilder::class, 'setDrivers')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\CacheBuilder::class, 'isAccessible')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\CacheBuilder::class, 'setLogger')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\CacheBuilder::class, 'setNamespace')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\CacheBuilder::class, 'setItemClass')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\CacheBuilder::class, 'setPoolClass')] class CacheBuilderClassTest extends AbstractCacheBuilderTest { - /** - * @covers ::__construct - * @covers ::setDrivers - * @covers ::isAccessible - */ - public function testSetDriversWithInvalidType() + public function testSetDriversWithInvalidType(): void { $this->expectExceptionMessage('Driver list must be an accessible array'); $this->expectException(InvalidArgumentException::class); - $builder = $this->createBuilder([ + $this->createBuilder([ 'drivers' => false, ]); } - /** - * @covers ::setLogger - */ - public function testSetLoggerWithInvalidType() + public function testSetLoggerWithInvalidType(): void { $this->expectExceptionMessage('Expected an instance of Psr\Log\LoggerInterface'); $this->expectException(InvalidArgumentException::class); - $builder = $this->createBuilder([ + $this->createBuilder([ 'logger' => new \stdClass(), ]); } - /** - * @covers ::__construct - * @covers ::setNamespace - */ - public function testSetNamespace() + public function testSetNamespace(): void { $driver = $this->createDriver('BlackHole'); $builder = $this->createBuilder([ @@ -65,26 +59,19 @@ public function testSetNamespace() $this->assertEquals('foo', $pool->getNamespace()); } - /** - * @covers ::setNamespace - */ - public function testSetInvalidNamespace() + public function testSetInvalidNamespace(): void { $this->expectExceptionMessage('Namespace must be alphanumeric'); $this->expectException(InvalidArgumentException::class); - $builder = $this->createBuilder([ + $this->createBuilder([ 'namespace' => '!@#$%^&*(', ]); } - /** - * @covers ::__construct - * @covers ::setItemClass - */ - public function testSetItemClass() + public function testSetItemClass(): void { - $mockItem = $this->createMock(ItemInterface::class); - $mockClassName = get_class($mockItem); + $mockItem = $this->createStub(ItemInterface::class); + $mockClassName = $mockItem::class; $driver = $this->createDriver('BlackHole'); $builder = $this->createBuilder([ @@ -97,40 +84,28 @@ public function testSetItemClass() $this->assertInstanceOf($mockClassName, $item); } - /** - * - * @covers ::setItemClass - */ - public function testSetFakeItemClass() + public function testSetFakeItemClass(): void { $this->expectExceptionMessage('Item class FakeClassName does not exist'); $this->expectException(InvalidArgumentException::class); - $builder = $this->createBuilder([ + $this->createBuilder([ 'item_class' => 'FakeClassName', ]); } - /** - * - * @covers ::setItemClass - */ - public function testSetInvalidItemClass() + public function testSetInvalidItemClass(): void { $this->expectExceptionMessage('Item class stdClass must inherit from Stash\Interfaces\ItemInterface'); $this->expectException(InvalidArgumentException::class); - $builder = $this->createBuilder([ + $this->createBuilder([ 'item_class' => 'stdClass', ]); } - /** - * @covers ::__construct - * @covers ::setPoolClass - */ - public function testSetPoolClass() + public function testSetPoolClass(): void { - $mockPool = $this->createMock(PoolInterface::class); - $mockClassName = get_class($mockPool); + $mockPool = $this->createStub(PoolInterface::class); + $mockClassName = $mockPool::class; $driver = $this->createDriver('BlackHole'); $builder = $this->createBuilder([ @@ -142,26 +117,20 @@ public function testSetPoolClass() $this->assertInstanceOf($mockClassName, $pool); } - /** - * @covers ::setPoolClass - */ - public function testSetFakePoolClass() + public function testSetFakePoolClass(): void { $this->expectExceptionMessage('Pool class FakeClassName does not exist'); $this->expectException(InvalidArgumentException::class); - $builder = $this->createBuilder([ + $this->createBuilder([ 'pool_class' => 'FakeClassName', ]); } - /** - * @covers ::setPoolClass - */ - public function testSetInvalidPoolClass() + public function testSetInvalidPoolClass(): void { $this->expectExceptionMessage('Pool class stdClass must inherit from Stash\Interfaces\PoolInterface'); $this->expectException(InvalidArgumentException::class); - $builder = $this->createBuilder([ + $this->createBuilder([ 'pool_class' => 'stdClass', ]); } diff --git a/packages/cache/tests/Charcoal/Cache/Factory/CacheBuilderDriverTest.php b/packages/cache/tests/Charcoal/Cache/Factory/CacheBuilderDriverTest.php index dd6c80ffa..57956de00 100644 --- a/packages/cache/tests/Charcoal/Cache/Factory/CacheBuilderDriverTest.php +++ b/packages/cache/tests/Charcoal/Cache/Factory/CacheBuilderDriverTest.php @@ -13,17 +13,18 @@ /** * Test the cache driver resolution from the CacheBuilder. - * - * @coversDefaultClass \Charcoal\Cache\CacheBuilder */ +#[\PHPUnit\Framework\Attributes\CoversClass(\Charcoal\Cache\CacheBuilder::class)] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\CacheBuilder::class, 'build')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\CacheBuilder::class, 'isIterable')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\CacheBuilder::class, 'resolveDriver')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\CacheBuilder::class, 'resolveOneDriver')] class CacheBuilderDriverTest extends AbstractCacheBuilderTest { /** * Test builder with a {@see DriverInterface driver object}. - * - * @covers ::build */ - public function testBuildOnDriverInstance() + public function testBuildOnDriverInstance(): void { $builder = $this->createBuilder(); $driver = $this->createDriver('BlackHole'); @@ -36,16 +37,10 @@ public function testBuildOnDriverInstance() // Resolve One Driver // ========================================================================= - /** * Test builder with a driver class. - * - * @covers ::build - * @covers ::isIterable - * @covers ::resolveDriver - * @covers ::resolveOneDriver */ - public function testBuildOnDriverClass() + public function testBuildOnDriverClass(): void { $builder = $this->createBuilder(); $driver = $this->getDriverClass('BlackHole'); @@ -56,13 +51,8 @@ public function testBuildOnDriverClass() /** * Test builder with a named driver associated to a {@see DriverInterface driver object}. - * - * @covers ::build - * @covers ::isIterable - * @covers ::resolveDriver - * @covers ::resolveOneDriver */ - public function testBuildOnNamedDriverWithInstance() + public function testBuildOnNamedDriverWithInstance(): void { $driver = $this->createDriver('BlackHole'); $builder = $this->createBuilder([ @@ -77,13 +67,8 @@ public function testBuildOnNamedDriverWithInstance() /** * Test builder with a named driver associated to a driver class. - * - * @covers ::build - * @covers ::isIterable - * @covers ::resolveDriver - * @covers ::resolveOneDriver */ - public function testBuildOnNamedDriverWithClass() + public function testBuildOnNamedDriverWithClass(): void { $driver = $this->getDriverClass('BlackHole'); $builder = $this->createBuilder([ @@ -97,13 +82,10 @@ public function testBuildOnNamedDriverWithClass() } // ================================= - /** * Test builder with an empty driver name. - * - * @covers ::resolveOneDriver */ - public function testBuildOnEmptyDriver() + public function testBuildOnEmptyDriver(): void { $this->expectExceptionMessage('Driver is empty'); $this->expectException(InvalidArgumentException::class); @@ -114,10 +96,8 @@ public function testBuildOnEmptyDriver() /** * Test builder with an invalid driver instance. - * - * @covers ::resolveOneDriver */ - public function testBuildOnInvalidDriverInstance() + public function testBuildOnInvalidDriverInstance(): void { $this->expectExceptionMessage('Driver class stdClass must implement Stash\Interfaces\DriverInterface'); $this->expectException(InvalidArgumentException::class); @@ -125,15 +105,13 @@ public function testBuildOnInvalidDriverInstance() $builder = $this->createBuilder(); $driver = new StdClass(); - $pool = $builder->build($driver); + $builder->build($driver); } /** * Test builder with a named driver associated to an empty value. - * - * @covers ::resolveOneDriver */ - public function testBuildOnNamedDriverWithEmptyEntry() + public function testBuildOnNamedDriverWithEmptyEntry(): void { $this->expectExceptionMessage('Driver "foobar" does not exist'); $this->expectException(InvalidArgumentException::class); @@ -144,15 +122,13 @@ public function testBuildOnNamedDriverWithEmptyEntry() ] ]); - $pool = $builder->build('foobar'); + $builder->build('foobar'); } /** * Test builder with a named driver associated to an invalid instance. - * - * @covers ::resolveOneDriver */ - public function testBuildOnNamedDriverWithBadEntry() + public function testBuildOnNamedDriverWithBadEntry(): void { $this->expectExceptionMessage('Driver "foobar": Class stdClass must implement Stash\Interfaces\DriverInterface'); $this->expectException(InvalidArgumentException::class); @@ -164,15 +140,13 @@ public function testBuildOnNamedDriverWithBadEntry() ] ]); - $pool = $builder->build('foobar'); + $builder->build('foobar'); } /** * Test builder with an invalid driver class. - * - * @covers ::resolveOneDriver */ - public function testBuildOnInvalidDriverClass() + public function testBuildOnInvalidDriverClass(): void { $this->expectExceptionMessage('Driver "FakeClassName" cannot be resolved'); $this->expectException(InvalidArgumentException::class); @@ -180,23 +154,17 @@ public function testBuildOnInvalidDriverClass() $builder = $this->createBuilder(); $driver = 'FakeClassName'; - $pool = $builder->build($driver); + $builder->build($driver); } // Resolve Many Drivers // ========================================================================= - /** * Test builder with an array of {@see DriverInterface driver objects}. - * - * @covers ::build - * @covers ::isIterable - * @covers ::resolveDriver - * @covers ::resolveOneDriver */ - public function testBuildOnArrayOfDriverInstances() + public function testBuildOnArrayOfDriverInstances(): void { $builder = $this->createBuilder(); $driver = $this->createDriver('BlackHole'); @@ -206,13 +174,10 @@ public function testBuildOnArrayOfDriverInstances() } // ================================= - /** * Test builder with an invalid array of drivers. - * - * @covers ::resolveDriver */ - public function testBuildOnArrayOfInvalidDrivers() + public function testBuildOnArrayOfInvalidDrivers(): void { $this->expectExceptionMessage('Drivers cannot be resolved'); $this->expectException(InvalidArgumentException::class); diff --git a/packages/cache/tests/Charcoal/Cache/Factory/CacheBuilderPoolTest.php b/packages/cache/tests/Charcoal/Cache/Factory/CacheBuilderPoolTest.php index 10aac436d..8de1ab3a1 100644 --- a/packages/cache/tests/Charcoal/Cache/Factory/CacheBuilderPoolTest.php +++ b/packages/cache/tests/Charcoal/Cache/Factory/CacheBuilderPoolTest.php @@ -14,17 +14,17 @@ /** * Test the cache pool creation and pool attributes from the CacheBuilder. - * - * @coversDefaultClass \Charcoal\Cache\CacheBuilder */ +#[\PHPUnit\Framework\Attributes\CoversClass(\Charcoal\Cache\CacheBuilder::class)] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\CacheBuilder::class, '__invoke')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\CacheBuilder::class, 'parsePoolOptions')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\CacheBuilder::class, 'applyPoolOptions')] class CacheBuilderPoolTest extends AbstractCacheBuilderTest { /** * Asserts that the CacheBuilder is invokable. - * - * @covers ::__invoke */ - public function testBuildIsInvokable() + public function testBuildIsInvokable(): void { $builder = $this->createBuilder(); $driver = $this->createDriver('BlackHole'); @@ -35,11 +35,8 @@ public function testBuildIsInvokable() /** * Asserts that the Pool logger can be assigned from build options. - * - * @covers ::parsePoolOptions - * @covers ::applyPoolOptions */ - public function testBuildWithLoggerOnOptions() + public function testBuildWithLoggerOnOptions(): void { $builder = $this->createBuilder(); $driver = $this->createDriver('BlackHole'); @@ -54,11 +51,8 @@ public function testBuildWithLoggerOnOptions() /** * Asserts that the Pool namespace can be customized from build options. - * - * @covers ::parsePoolOptions - * @covers ::applyPoolOptions */ - public function testBuildWithNamespaceOnOptions() + public function testBuildWithNamespaceOnOptions(): void { $builder = $this->createBuilder(); $driver = $this->createDriver('BlackHole'); @@ -76,17 +70,14 @@ public function testBuildWithNamespaceOnOptions() /** * Asserts that the Item class can be customized from build options. - * - * @covers ::parsePoolOptions - * @covers ::applyPoolOptions */ - public function testBuildWithItemClassOnOptions() + public function testBuildWithItemClassOnOptions(): void { $builder = $this->createBuilder(); $driver = $this->createDriver('BlackHole'); - $mockItem = $this->createMock(ItemInterface::class); - $mockClassName = get_class($mockItem); + $mockItem = $this->createStub(ItemInterface::class); + $mockClassName = $mockItem::class; $pool = $builder($driver, [ 'item_class' => $mockClassName, @@ -98,17 +89,14 @@ public function testBuildWithItemClassOnOptions() /** * Asserts that the Pool class can be customized from build options. - * - * @covers ::parsePoolOptions - * @covers ::applyPoolOptions */ - public function testBuildWithPoolClassOnOptions() + public function testBuildWithPoolClassOnOptions(): void { $builder = $this->createBuilder(); $driver = $this->createDriver('BlackHole'); - $mockPool = $this->createMock(PoolInterface::class); - $mockClassName = get_class($mockPool); + $mockPool = $this->createStub(PoolInterface::class); + $mockClassName = $mockPool::class; // Custom Pool Class $pool = $builder($driver, [ @@ -119,10 +107,8 @@ public function testBuildWithPoolClassOnOptions() /** * Asserts that the CacheBuilder uses default options when given NULL. - * - * @covers ::parsePoolOptions */ - public function testBuildWithNullOnOptions() + public function testBuildWithNullOnOptions(): void { $builder = $this->createBuilder(); $driver = $this->createDriver('BlackHole'); @@ -135,10 +121,8 @@ public function testBuildWithNullOnOptions() /** * Asserts that the CacheBuilder uses default options when given NULL. - * - * @covers ::parsePoolOptions */ - public function testBuildWithInvalidTypeOnOptions() + public function testBuildWithInvalidTypeOnOptions(): void { $builder = $this->createBuilder(); $driver = $this->createDriver('BlackHole'); diff --git a/packages/cache/tests/Charcoal/Cache/Middleware/AbstractCacheMiddlewareTest.php b/packages/cache/tests/Charcoal/Cache/Middleware/AbstractCacheMiddlewareTest.php index 16b9bb5fa..02372cb7e 100644 --- a/packages/cache/tests/Charcoal/Cache/Middleware/AbstractCacheMiddlewareTest.php +++ b/packages/cache/tests/Charcoal/Cache/Middleware/AbstractCacheMiddlewareTest.php @@ -24,9 +24,9 @@ /** * Test CacheMiddleware - * - * @coversDefaultClass \Charcoal\Cache\Middleware\CacheMiddleware */ +#[\PHPUnit\Framework\Attributes\CoversClass(\Charcoal\Cache\Middleware\CacheMiddleware::class)] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\Middleware\CacheMiddleware::class, 'disableCacheHeadersOnResponse')] abstract class AbstractCacheMiddlewareTest extends AbstractTestCase { use CachePoolTrait; @@ -41,9 +41,7 @@ protected function middlewareFactory(array $args = []) { if (!isset($args['cache'])) { $args['cache'] = static::getCachePool(); - $args['processCacheKeyCallback'] = function ($key) { - return $key; - }; + $args['processCacheKeyCallback'] = (fn($key) => $key); } return new CacheMiddleware($args); @@ -56,9 +54,7 @@ protected function middlewareFactory(array $args = []) */ protected function mockNextMiddleware() { - return function ($request, $response) { - return $response; - }; + return fn($request, $response) => $response; } /** @@ -120,9 +116,7 @@ protected function createRequest($method = 'GET', $uri = '/', $query = null) if ($query !== null) { $env['QUERY_STRING'] = is_array($query) ? http_build_query($query) : $query; } - - $request = Request::createFromEnvironment($env); - return $request; + return Request::createFromEnvironment($env); } /** @@ -156,19 +150,16 @@ protected function createResponse($status = 200, $body = null) } $headers = new Headers([ 'Content-Type' => 'text/html; charset=UTF-8' ]); - $response = new Response($status, $headers, $body); - return $response; + return new Response($status, $headers, $body); } /** * Reports an error if the HTTP response headers does not have disabled cache headers. * - * @covers ::disableCacheHeadersOnResponse * * @param array $headers The HTTP response headers to test. - * @return void */ - public function assertResponseHasDisabledCacheHeaders(array $headers) + public function assertResponseHasDisabledCacheHeaders(array $headers): void { $this->assertArrayHasKey('Cache-Control', $headers); $this->assertContains('no-cache, no-store, must-revalidate', $headers['Cache-Control']); diff --git a/packages/cache/tests/Charcoal/Cache/Middleware/CacheMiddlewareRequestTest.php b/packages/cache/tests/Charcoal/Cache/Middleware/CacheMiddlewareRequestTest.php index 6c8ac02df..85f4d2e8c 100644 --- a/packages/cache/tests/Charcoal/Cache/Middleware/CacheMiddlewareRequestTest.php +++ b/packages/cache/tests/Charcoal/Cache/Middleware/CacheMiddlewareRequestTest.php @@ -11,15 +11,21 @@ /** * Test HTTP Requests with CacheMiddleware. - * - * @coversDefaultClass \Charcoal\Cache\Middleware\CacheMiddleware */ +#[\PHPUnit\Framework\Attributes\CoversClass(\Charcoal\Cache\Middleware\CacheMiddleware::class)] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\Middleware\CacheMiddleware::class, '__invoke')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\Middleware\CacheMiddleware::class, 'isRequestMethodValid')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\Middleware\CacheMiddleware::class, 'isResponseStatusValid')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\Middleware\CacheMiddleware::class, 'isPathIncluded')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\Middleware\CacheMiddleware::class, 'isPathExcluded')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\Middleware\CacheMiddleware::class, 'isQueryIncluded')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\Middleware\CacheMiddleware::class, 'isQueryExcluded')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\Middleware\CacheMiddleware::class, 'parseIgnoredParams')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\Middleware\CacheMiddleware::class, 'disableCacheHeadersOnResponse')] class CacheMiddlewareRequestTest extends AbstractCacheMiddlewareTest { /** * Prepare the cache pool. - * - * @return void */ public function setUp(): void { @@ -28,8 +34,6 @@ public function setUp(): void /** * Empty the cache pool. - * - * @return void */ public function tearDown(): void { @@ -39,25 +43,15 @@ public function tearDown(): void /** * Test middleware with an invalid HTTP request method. * - * @covers ::__invoke - * @covers ::isRequestMethodValid - * @covers ::isResponseStatusValid - * @covers ::isPathIncluded - * @covers ::isPathExcluded - * @covers ::isQueryIncluded - * @covers ::isQueryExcluded - * @covers ::parseIgnoredParams - * @covers ::disableCacheHeadersOnResponse * - * @dataProvider provideInvokableSituations * * @param boolean $expected The expected result from {@see \Psr\Cache\CacheItemInterface::isHit()}. * @param boolean $checkHttpHeaders Whether to test the HTTP response's headers. * @param stromg $requestUri The request URI for {@see self::createRequest()}. * @param array $cacheConfig The CacheMiddleware settings. - * @return void */ - public function testInvoke($expected, $checkHttpHeaders, $requestUri, array $cacheConfig) + #[\PHPUnit\Framework\Attributes\DataProvider('provideInvokableSituations')] + public function testInvoke(bool $expected, bool $checkHttpHeaders, string $requestUri, array $cacheConfig): void { $middleware = $this->middlewareFactory($cacheConfig); $request = $this->createRequest('GET', $requestUri); @@ -85,9 +79,8 @@ public function testInvoke($expected, $checkHttpHeaders, $requestUri, array $cac * Provide data for testing the middleware. * * @used-by self::testInvoke() - * @return array */ - public function provideInvokableSituations() + public static function provideInvokableSituations(): array { $target1 = '/foo/bar'; $target2 = '/foo/bar?abc=123'; diff --git a/packages/cache/tests/Charcoal/Cache/Middleware/CacheMiddlewareResponseTest.php b/packages/cache/tests/Charcoal/Cache/Middleware/CacheMiddlewareResponseTest.php index 4127bc4b3..61471fa20 100644 --- a/packages/cache/tests/Charcoal/Cache/Middleware/CacheMiddlewareResponseTest.php +++ b/packages/cache/tests/Charcoal/Cache/Middleware/CacheMiddlewareResponseTest.php @@ -14,15 +14,14 @@ /** * Test HTTP Responses from CacheMiddleware. - * - * @coversDefaultClass \Charcoal\Cache\Middleware\CacheMiddleware */ +#[\PHPUnit\Framework\Attributes\CoversClass(\Charcoal\Cache\Middleware\CacheMiddleware::class)] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\Middleware\CacheMiddleware::class, '__invoke')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\Middleware\CacheMiddleware::class, 'cacheKeyFromRequest')] class CacheMiddlewareResponseTest extends AbstractCacheMiddlewareTest { /** * Prepare the cache pool. - * - * @return void */ public static function setUpBeforeClass(): void { @@ -31,8 +30,6 @@ public static function setUpBeforeClass(): void /** * Empty the cache pool. - * - * @return void */ public static function tearDownAfterClass(): void { @@ -42,8 +39,6 @@ public static function tearDownAfterClass(): void /** * Test the initial state. * - * @covers ::__invoke - * @covers ::cacheKeyFromRequest * * @return CacheMiddleware To use the same cache middleware for the next test. */ @@ -83,14 +78,11 @@ public function testInitialState() /** * Test the cached state. * - * @covers ::__invoke - * @covers ::cacheKeyFromRequest - * @depends testInitialState * * @param CacheMiddleware $middleware The cache middleware from the previous test. - * @return void */ - public function testCachedState(CacheMiddleware $middleware) + #[\PHPUnit\Framework\Attributes\Depends('testInitialState')] + public function testCachedState(CacheMiddleware $middleware): void { $txt = 'Lorem ipsum dolor sit amet.'; diff --git a/packages/cache/tests/Charcoal/Cache/ServiceProvider/CacheServiceProviderTest.php b/packages/cache/tests/Charcoal/Cache/ServiceProvider/CacheServiceProviderTest.php index 198f9be5e..66884f185 100644 --- a/packages/cache/tests/Charcoal/Cache/ServiceProvider/CacheServiceProviderTest.php +++ b/packages/cache/tests/Charcoal/Cache/ServiceProvider/CacheServiceProviderTest.php @@ -26,18 +26,15 @@ /** * Test CacheServiceProvider - * - * @coversDefaultClass \Charcoal\Cache\ServiceProvider\CacheServiceProvider */ +#[\PHPUnit\Framework\Attributes\CoversClass(\Charcoal\Cache\ServiceProvider\CacheServiceProvider::class)] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\ServiceProvider\CacheServiceProvider::class, 'register')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\ServiceProvider\CacheServiceProvider::class, 'registerDrivers')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\ServiceProvider\CacheServiceProvider::class, 'registerService')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Cache\ServiceProvider\CacheServiceProvider::class, 'registerMiddleware')] class CacheServiceProviderTest extends AbstractTestCase { - /** - * @covers ::register - * @covers ::registerDrivers - * @covers ::registerService - * @covers ::registerMiddleware - */ - public function testProvider() + public function testProvider(): void { $container = $this->providerFactory(); @@ -68,10 +65,8 @@ public function testProvider() /** * Test "middlewares/charcoal/cache/middleware/cache" with a user-preferences. - * - * @covers ::registerMiddleware */ - public function testCustomizedMiddleware() + public function testCustomizedMiddleware(): void { $container = $this->providerFactory([ 'config' => [ @@ -87,17 +82,14 @@ public function testCustomizedMiddleware() $middleware = $container['middlewares/charcoal/cache/middleware/cache']; $reflection = new ReflectionClass($middleware); $reflectionProperty = $reflection->getProperty('cacheTtl'); - $reflectionProperty->setAccessible(true); $this->assertEquals(1, $reflectionProperty->getValue($middleware)); } /** * Test "cache/drivers"; basic drivers are instances of {@see DriverInterface}. - * - * @covers ::registerDrivers */ - public function testBasicDriverInstances() + public function testBasicDriverInstances(): void { $container = $this->providerFactory(); @@ -121,10 +113,8 @@ public function testBasicDriverInstances() /** * Test "cache/drivers"; vendor drivers are instances of {@see DriverInterface}. - * - * @covers ::registerDrivers */ - public function testAvailableVendorDriverInstances() + public function testAvailableVendorDriverInstances(): void { $container = $this->providerFactory(); @@ -146,7 +136,7 @@ public function testAvailableVendorDriverInstances() try { $driver = $driverCollection[$driverKey]; $this->assertInstanceOf($className, $driver); - } catch (Throwable $t) { + } catch (Throwable) { // Do nothing; Some cache drivers, such as Redis, // are not correctly implemented. } @@ -160,10 +150,8 @@ public function testAvailableVendorDriverInstances() /** * Test "cache/drivers"; unavailable vendor drivers return NULL. - * - * @covers ::registerDrivers */ - public function testUnavailableVendorDriverInstances() + public function testUnavailableVendorDriverInstances(): void { $container = $this->providerFactory(); @@ -191,16 +179,13 @@ public function testUnavailableVendorDriverInstances() /** * Assert "cache/driver" resolves as expected. * - * @covers ::registerDrivers - * @covers ::registerService * - * @dataProvider provideConfigsForMainDriver * * @param string $className The expected driver class name. * @param array $cacheConfig The cache configset to resolve the main driver. - * @return void */ - public function testMainDriverInstance($className, array $cacheConfig) + #[\PHPUnit\Framework\Attributes\DataProvider('provideConfigsForMainDriver')] + public function testMainDriverInstance(string $className, array $cacheConfig): void { $container = $this->providerFactory([ 'config' => [ @@ -215,9 +200,8 @@ public function testMainDriverInstance($className, array $cacheConfig) * Provide data for testing the "cache/driver" service. * * @used-by self::testMainDriverInstance() - * @return array */ - public function provideConfigsForMainDriver() + public static function provideConfigsForMainDriver(): array { $driverClassNames = DriverList::getAvailableDrivers(); @@ -260,9 +244,8 @@ public function getKeys($value) * Determine whether the given value is array accessible. * * @param mixed $value The variable being evaluated. - * @return boolean */ - public function isAccessible($value) + public function isAccessible($value): bool { return is_array($value) || $value instanceof \ArrayAccess; } @@ -271,9 +254,8 @@ public function isAccessible($value) * Create a new Container instance. * * @param array $args Parameters for the initialization of a Container. - * @return Container */ - public function providerFactory(array $args = []) + public function providerFactory(array $args = []): \Pimple\Container { $container = new Container($args); diff --git a/packages/cache/tests/Charcoal/Mocks/CachePoolAware.php b/packages/cache/tests/Charcoal/Mocks/CachePoolAware.php index b38f09e22..d4bd63c4d 100644 --- a/packages/cache/tests/Charcoal/Mocks/CachePoolAware.php +++ b/packages/cache/tests/Charcoal/Mocks/CachePoolAware.php @@ -1,5 +1,7 @@ itemClass; @@ -21,6 +18,7 @@ public function getItemClass():string /** * @return false|string */ + #[\Override] public function getNamespace(): ?string { return $this->namespace; diff --git a/packages/cache/tests/Charcoal/Mocks/DefaultsAwareCacheMiddlewares.php b/packages/cache/tests/Charcoal/Mocks/DefaultsAwareCacheMiddlewares.php index 75ef83aa9..085f37406 100644 --- a/packages/cache/tests/Charcoal/Mocks/DefaultsAwareCacheMiddlewares.php +++ b/packages/cache/tests/Charcoal/Mocks/DefaultsAwareCacheMiddlewares.php @@ -1,5 +1,7 @@ logger; diff --git a/packages/cms/composer.json b/packages/cms/composer.json index a0ffef05b..cee804e14 100644 --- a/packages/cms/composer.json +++ b/packages/cms/composer.json @@ -1,8 +1,10 @@ { - "type": "library", "name": "charcoal/cms", "description": "Charcoal CMS (Content Management System) Module", - "keywords": ["charcoal", "cms"], + "keywords": [ + "charcoal", + "cms" + ], "homepage": "https://charcoal.locomotive.ca", "license": "MIT", "authors": [ @@ -15,13 +17,9 @@ "homepage": "https://locomotive.ca" } ], - "extra": { - "branch-alias": { - "dev-main": "5.x-dev" - } - }, + "type": "library", "require": { - "php": "^7.4 || ^8.0", + "php": "^8.3", "psr/http-message": "^1.0", "charcoal/app": "^5.1", "charcoal/attachment": "^5.1", @@ -35,13 +33,10 @@ "charcoal/admin": "^5.1", "mustache/mustache": "^2.11", "php-coveralls/php-coveralls": "^2.2", - "phpunit/phpunit": "^9.5", + "phpunit/phpunit": "^12.5", "squizlabs/php_codesniffer": "^3.5", "tedivm/stash": "~0.16" }, - "conflict": { - "charcoal/admin": "<0.28.0" - }, "autoload": { "psr-4": { "Charcoal\\": "src/Charcoal" @@ -52,8 +47,10 @@ "Charcoal\\Tests\\": "tests/Charcoal" } }, - "replace": { - "locomotivemtl/charcoal-cms": "*" + "extra": { + "branch-alias": { + "dev-main": "5.x-dev" + } }, "scripts": { "test": [ @@ -69,6 +66,12 @@ "phpcbf": "php vendor/bin/phpcbf -ps --colors src/ tests/", "phpunit": "php vendor/bin/phpunit --coverage-text" }, + "replace": { + "locomotivemtl/charcoal-cms": "*" + }, + "conflict": { + "charcoal/admin": "<0.28.0" + }, "minimum-stability": "dev", "prefer-stable": true } diff --git a/packages/cms/src/Charcoal/Admin/Widget/Cms/HierarchicalSectionTableWidget.php b/packages/cms/src/Charcoal/Admin/Widget/Cms/HierarchicalSectionTableWidget.php index 6dcba5266..36a534ebc 100644 --- a/packages/cms/src/Charcoal/Admin/Widget/Cms/HierarchicalSectionTableWidget.php +++ b/packages/cms/src/Charcoal/Admin/Widget/Cms/HierarchicalSectionTableWidget.php @@ -24,10 +24,9 @@ class HierarchicalSectionTableWidget extends TableWidget /** * Provide a template to fullfill UIItem interface. - * - * @return string */ - public function template() + #[\Override] + public function template(): string { return 'charcoal/admin/widget/table'; } @@ -38,6 +37,7 @@ public function template() * @see \Charcoal\Admin\Ui\CollectionContainerTrait::sortObjects() * @return array */ + #[\Override] public function sortObjects() { $collection = new HierarchicalCollection($this->objects(), false); @@ -55,6 +55,7 @@ public function sortObjects() * @param Container $container A dependencies container instance. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -71,6 +72,7 @@ protected function setDependencies(Container $container) * @param PropertyInterface $property The current property. * @return void */ + #[\Override] protected function setupDisplayPropertyValue( ModelInterface $object, PropertyInterface $property diff --git a/packages/cms/src/Charcoal/Admin/Widget/Cms/SectionTableTrait.php b/packages/cms/src/Charcoal/Admin/Widget/Cms/SectionTableTrait.php index 4ea99fb2b..d8501e2a7 100644 --- a/packages/cms/src/Charcoal/Admin/Widget/Cms/SectionTableTrait.php +++ b/packages/cms/src/Charcoal/Admin/Widget/Cms/SectionTableTrait.php @@ -48,7 +48,7 @@ public function sectionFactory() { if (!isset($this->sectionFactory)) { throw new RuntimeException( - sprintf('Section Model Factory is not defined for "%s"', get_class($this)) + sprintf('Section Model Factory is not defined for "%s"', $this::class) ); } @@ -89,30 +89,26 @@ protected function parsePropertyCell( case 'menu_label': $sectionType = $object->sectionType(); - switch ($sectionType) { - case AbstractSection::TYPE_EXTERNAL: - $externalUrl = (string)$object->externalUrl(); - $linkExcerpt = ''; - $tagTemplate = ''; - - if ($externalUrl) { - $tagTemplate = '' . - ' ' . - 'URL: %3$s' . - ''; - - $linkExcerpt = $this->abridgeUri($externalUrl); - } - - $p = $object->p('section_type'); - $propertyValue .= sprintf( - '   ' . $tagTemplate, - $p->displayVal($p->val()), - $externalUrl, - $linkExcerpt - ); - break; + if ($sectionType === AbstractSection::TYPE_EXTERNAL) { + $externalUrl = (string)$object->externalUrl(); + $linkExcerpt = ''; + $tagTemplate = ''; + if ($externalUrl !== '' && $externalUrl !== '0') { + $tagTemplate = '' . + ' ' . + 'URL: %3$s' . + ''; + + $linkExcerpt = $this->abridgeUri($externalUrl); + } + $p = $object->p('section_type'); + $propertyValue .= sprintf( + '   ' . $tagTemplate, + $p->displayVal($p->val()), + $externalUrl, + $linkExcerpt + ); } break; } diff --git a/packages/cms/src/Charcoal/Admin/Widget/Cms/SectionTableWidget.php b/packages/cms/src/Charcoal/Admin/Widget/Cms/SectionTableWidget.php index 8586e3621..0733d7e75 100644 --- a/packages/cms/src/Charcoal/Admin/Widget/Cms/SectionTableWidget.php +++ b/packages/cms/src/Charcoal/Admin/Widget/Cms/SectionTableWidget.php @@ -19,6 +19,7 @@ class SectionTableWidget extends TableWidget * @param Container $container A dependencies container instance. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); diff --git a/packages/cms/src/Charcoal/Admin/Widget/FormGroup/GroupAttachmentFormGroup.php b/packages/cms/src/Charcoal/Admin/Widget/FormGroup/GroupAttachmentFormGroup.php index ab2f420a5..44c98700a 100644 --- a/packages/cms/src/Charcoal/Admin/Widget/FormGroup/GroupAttachmentFormGroup.php +++ b/packages/cms/src/Charcoal/Admin/Widget/FormGroup/GroupAttachmentFormGroup.php @@ -1,5 +1,7 @@ hasGroups()) { diff --git a/packages/cms/src/Charcoal/Admin/Widget/FormGroup/MultiGroupFormGroup.php b/packages/cms/src/Charcoal/Admin/Widget/FormGroup/MultiGroupFormGroup.php index 0e660bc4b..cfa828d7e 100644 --- a/packages/cms/src/Charcoal/Admin/Widget/FormGroup/MultiGroupFormGroup.php +++ b/packages/cms/src/Charcoal/Admin/Widget/FormGroup/MultiGroupFormGroup.php @@ -22,24 +22,19 @@ class MultiGroupFormGroup extends MultiGroupWidget implements use FormGroupTrait; use UiItemTrait; - /** - * @return string - */ - public function template() + #[\Override] + public function template(): string { return 'charcoal/admin/widget/multi-group'; } - /** - * @return array - */ - public function dataFromObject() + public function dataFromObject(): array { $obj = $this->obj(); $objMetadata = $obj->metadata(); - $adminMetadata = (isset($objMetadata['admin']) ? $objMetadata['admin'] : null); - $adminFormGroups = (isset($adminMetadata['form_groups']) ? $adminMetadata['form_groups'] : null); + $adminMetadata = ($objMetadata['admin'] ?? null); + $adminFormGroups = ($adminMetadata['form_groups'] ?? null); $groups = $this->groups(); @@ -70,8 +65,8 @@ public function dataFromMetadata() return []; } - $adminMetadata = (isset($data['admin']) ? $data['admin'] : null); - $adminFormGroups = (isset($adminMetadata['form_groups']) ? $adminMetadata['form_groups'] : null); + $adminMetadata = ($data['admin'] ?? null); + $adminFormGroups = ($adminMetadata['form_groups'] ?? null); if (isset($data['groups']) && isset($adminFormGroups)) { $extraFormGroups = array_intersect( @@ -100,10 +95,10 @@ public function dataFromMetadata() * @throws \UnexpectedValueException If a property data is invalid. * @return FormPropertyWidget[] */ - public function formProperties(array $group = null) + public function formProperties(?array $group = null): array { if ( - !key_exists(self::DATA_SOURCE_METADATA, array_flip($this->dataSources())) || + !array_key_exists(self::DATA_SOURCE_METADATA, array_flip($this->dataSources())) || !$this->widgetMetadata() ) { return iterator_to_array($this->form()->formProperties()); @@ -114,7 +109,7 @@ public function formProperties(array $group = null) try { $store = $this->storageProperty(); - } catch (\Exception $e) { + } catch (\Exception) { $store = null; } @@ -128,7 +123,7 @@ public function formProperties(array $group = null) $props = $this->widgetMetadata()['properties']; // We need to sort form properties by form group property order if a group exists - if (!empty($group)) { + if ($group !== null && $group !== []) { $props = array_merge(array_flip($group), $props); } @@ -139,7 +134,7 @@ public function formProperties(array $group = null) throw new \UnexpectedValueException(sprintf( 'Invalid property data for "%1$s", received %2$s', $propertyIdent, - (is_object($propertyMetadata) ? get_class($propertyMetadata) : gettype($propertyMetadata)) + (get_debug_type($propertyMetadata)) )); } @@ -172,9 +167,10 @@ public function formProperties(array $group = null) * * @return FormInterface|self */ + #[\Override] protected function formWidget() { - if (!key_exists(self::DATA_SOURCE_METADATA, array_flip($this->dataSources()))) { + if (!array_key_exists(self::DATA_SOURCE_METADATA, array_flip($this->dataSources()))) { return $this->form(); } diff --git a/packages/cms/src/Charcoal/Admin/Widget/GroupAttachmentWidget.php b/packages/cms/src/Charcoal/Admin/Widget/GroupAttachmentWidget.php index 39a5f6570..36889ae4d 100644 --- a/packages/cms/src/Charcoal/Admin/Widget/GroupAttachmentWidget.php +++ b/packages/cms/src/Charcoal/Admin/Widget/GroupAttachmentWidget.php @@ -32,10 +32,8 @@ class GroupAttachmentWidget extends AttachmentWidget implements /** * Store the metadata loader instance. - * - * @var MetadataLoader */ - private $metadataLoader; + private ?\Charcoal\Model\Service\MetadataLoader $metadataLoader = null; /** * @var boolean @@ -57,21 +55,17 @@ class GroupAttachmentWidget extends AttachmentWidget implements protected function sortItemsByPriority( PrioritizableInterface $a, PrioritizableInterface $b - ) { + ): int { $priorityA = $a->priority(); $priorityB = $b->priority(); - - if ($priorityA === $priorityB) { - return 0; - } - - return ($priorityA < $priorityB) ? (-1) : 1; + return $priorityA <=> $priorityB; } /** * @param Container $container The DI container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -87,9 +81,8 @@ protected function setDependencies(Container $container) * Set a metadata loader. * * @param MetadataLoader $loader The loader instance, used to load metadata. - * @return self */ - protected function setMetadataLoader(MetadataLoader $loader) + protected function setMetadataLoader(MetadataLoader $loader): static { $this->metadataLoader = $loader; @@ -100,14 +93,13 @@ protected function setMetadataLoader(MetadataLoader $loader) * Retrieve the metadata loader. * * @throws RuntimeException If the metadata loader was not previously set. - * @return MetadataLoader */ - protected function metadataLoader() + protected function metadataLoader(): \Charcoal\Model\Service\MetadataLoader { - if ($this->metadataLoader === null) { + if (!$this->metadataLoader instanceof \Charcoal\Model\Service\MetadataLoader) { throw new RuntimeException(sprintf( 'Metadata Loader is not defined for "%s"', - \get_class($this) + static::class )); } @@ -123,16 +115,15 @@ protected function metadataLoader() protected function loadMetadata($metadataIdent) { $metadataLoader = $this->metadataLoader(); - $metadata = $metadataLoader->load($metadataIdent, $this->createMetadata()); - return $metadata; + return $metadataLoader->load($metadataIdent, $this->createMetadata()); } /** * @throws InvalidArgumentException If structureMetadata $data is note defined. * @return MetadataInterface */ - protected function createMetadata() + protected function createMetadata(): \Charcoal\Property\Structure\StructureMetadata { return new StructureMetadata(); } @@ -141,9 +132,9 @@ protected function createMetadata() * Sets data on this widget. * * @param array $data Key-value array of data to append. - * @return self */ - public function setData(array $data) + #[\Override] + public function setData(array $data): static { parent::setData($data); @@ -173,7 +164,7 @@ protected function addAttachmentGroupFromMetadata($reload = false) $interfaces = [$this->objType()]; if ($controllerInterface) { - array_push($interfaces, $controllerInterface); + $interfaces[] = $controllerInterface; } $structureMetadata = $this->createMetadata(); @@ -204,9 +195,8 @@ protected function addAttachmentGroupFromMetadata($reload = false) * Set the form object's template controller identifier. * * @param mixed $ident The template controller identifier. - * @return self */ - public function setControllerIdent($ident) + public function setControllerIdent(string $ident): static { if (class_exists($ident)) { $this->controllerIdent = $ident; @@ -214,7 +204,7 @@ public function setControllerIdent($ident) return $this; } - if (substr($ident, -9) !== '-template') { + if (!str_ends_with($ident, '-template')) { $ident .= '-template'; } @@ -235,10 +225,8 @@ public function controllerIdent() /** * Disable the pill nav if there is only one group. - * - * @return boolean */ - public function displayPills() + public function displayPills(): bool { return $this->numGroups() > 1; } diff --git a/packages/cms/src/Charcoal/Admin/Widget/MultiGroupWidget.php b/packages/cms/src/Charcoal/Admin/Widget/MultiGroupWidget.php index 51417f576..8ea4e30dd 100644 --- a/packages/cms/src/Charcoal/Admin/Widget/MultiGroupWidget.php +++ b/packages/cms/src/Charcoal/Admin/Widget/MultiGroupWidget.php @@ -81,22 +81,17 @@ class MultiGroupWidget extends AdminWidget implements protected function sortItemsByPriority( PrioritizableInterface $a, PrioritizableInterface $b - ) { + ): int { $priorityA = $a->priority(); $priorityB = $b->priority(); - - if ($priorityA === $priorityB) { - return 0; - } - - return ($priorityA < $priorityB) ? (-1) : 1; + return $priorityA <=> $priorityB; } /** * @param array $data The widget data. - * @return self */ - public function setData(array $data) + #[\Override] + public function setData(array $data): static { parent::setData($data); @@ -109,6 +104,7 @@ public function setData(array $data) * @param Container $container The DI container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -122,10 +118,7 @@ protected function setDependencies(Container $container) $this->setLayoutBuilder($container['layout/builder']); } - /** - * @return string - */ - public function defaultGroupType() + public function defaultGroupType(): string { return 'charcoal/admin/widget/form-group/generic'; } @@ -135,7 +128,8 @@ public function defaultGroupType() * * @return string[] */ - protected function defaultDataSources() + #[\Override] + protected function defaultDataSources(): array { return [self::DATA_SOURCE_OBJECT]; } @@ -144,9 +138,8 @@ protected function defaultDataSources() * Set an widget factory. * * @param FactoryInterface $factory The factory to create widgets. - * @return self */ - protected function setWidgetFactory(FactoryInterface $factory) + protected function setWidgetFactory(FactoryInterface $factory): static { $this->widgetFactory = $factory; @@ -161,10 +154,10 @@ protected function setWidgetFactory(FactoryInterface $factory) */ public function widgetFactory() { - if (!isset($this->widgetFactory)) { + if ($this->widgetFactory === null) { throw new RuntimeException(sprintf( 'Widget Factory is not defined for "%s"', - get_class($this) + static::class )); } @@ -181,9 +174,8 @@ public function formGroups() /** * @param mixed $formGroups FormGroups for MultiGroupWidget. - * @return self */ - public function setFormGroups($formGroups) + public function setFormGroups($formGroups): static { $this->formGroups = $formGroups; @@ -200,18 +192,13 @@ public function widgetMetadata() /** * @param array|mixed $widgetMetadata WidgetMetadata for MultiGroupWidget. - * @return self */ - public function setWidgetMetadata($widgetMetadata) + public function setWidgetMetadata($widgetMetadata): static { if (is_string($widgetMetadata) && $this->view()) { $widgetMetadata = $this->view()->renderTemplate($widgetMetadata, $this->obj()); - if ($widgetMetadata !== '') { - $widgetMetadata = json_decode($widgetMetadata, true); - } else { - $widgetMetadata = null; - } + $widgetMetadata = $widgetMetadata !== '' ? json_decode($widgetMetadata, true) : null; } $this->widgetMetadata = $widgetMetadata; @@ -228,9 +215,8 @@ public function setWidgetMetadata($widgetMetadata) * @param string|ModelStructureProperty $propertyIdent The property identifier—or instance—of a storage property. * @throws \InvalidArgumentException If the property identifier is not a string. * @throws \UnexpectedValueException If a property is invalid. - * @return self */ - public function setStorageProperty($propertyIdent) + public function setStorageProperty($propertyIdent): static { $property = null; if ($propertyIdent instanceof PropertyInterface) { @@ -247,11 +233,11 @@ public function setStorageProperty($propertyIdent) throw new \UnexpectedValueException(sprintf( 'The "%1$s" property is not defined on [%2$s]', $propertyIdent, - get_class($obj) + $obj::class )); } - if ($property === null) { + if (!$property instanceof \Charcoal\Property\PropertyInterface) { $property = $obj->property($propertyIdent); } @@ -261,8 +247,8 @@ public function setStorageProperty($propertyIdent) throw new \UnexpectedValueException(sprintf( '"%s" [%s] is not a model structure property on [%s].', $propertyIdent, - (is_object($property) ? get_class($property) : gettype($property)), - (is_object($obj) ? get_class($obj) : gettype($obj)) + (get_debug_type($property)), + (get_debug_type($obj)) )); } @@ -280,7 +266,7 @@ public function storageProperty() if ($this->storageProperty === null) { throw new RuntimeException(sprintf( 'Storage property owner is not defined for "%s"', - get_class($this) + static::class )); } diff --git a/packages/cms/src/Charcoal/Cms/AbstractEvent.php b/packages/cms/src/Charcoal/Cms/AbstractEvent.php index 99350d4bf..a214aeac3 100644 --- a/packages/cms/src/Charcoal/Cms/AbstractEvent.php +++ b/packages/cms/src/Charcoal/Cms/AbstractEvent.php @@ -50,15 +50,9 @@ abstract class AbstractEvent extends Content implements EventInterface */ private $image; - /** - * @var DateTimeInterface|null - */ - private $startDate; + private ?\DateTimeInterface $startDate = null; - /** - * @var DateTimeInterface|null - */ - private $endDate; + private ?\DateTimeInterface $endDate = null; /** * @var Translation|string|null @@ -71,15 +65,9 @@ abstract class AbstractEvent extends Content implements EventInterface private $infoPhone; - /** - * @var float|null - */ - private $ticketPriceMin; + private ?float $ticketPriceMin = null; - /** - * @var float|null - */ - private $ticketPriceMax; + private ?float $ticketPriceMax = null; /** * @var Translation|string|null @@ -105,11 +93,11 @@ abstract class AbstractEvent extends Content implements EventInterface * Section constructor. * @param array $data The data. */ - public function __construct(array $data = null) + public function __construct(?array $data = null) { parent::__construct($data); - if (is_callable([ $this, 'defaultData' ])) { + if (is_callable($this->defaultData(...))) { $this->setData($this->defaultData()); } } @@ -125,9 +113,8 @@ public function canonicalUrl() /** * Some dates cannot be null - * @return void */ - public function verifyDates() + public function verifyDates(): void { if (!$this['startDate']) { $this->setStartDate('now'); @@ -150,13 +137,7 @@ public function adminDateFilter() $start = $this['startDate']->format('Y-m-d'); $end = $this['endDate']->format('Y-m-d'); - if ($start === $end) { - $date = $start; - } else { - $date = $start . ' - ' . $end; - } - - return $date; + return $start === $end ? $start : $start . ' - ' . $end; } /** @@ -506,10 +487,9 @@ public function isActiveRoute() /** * {@inheritdoc} - * - * @return boolean */ - protected function preSave() + #[\Override] + protected function preSave(): bool { $this->verifyDates(); $this->setSlug($this->generateSlug()); @@ -521,9 +501,9 @@ protected function preSave() * {@inheritdoc} * * @param array $properties Optional properties to update. - * @return boolean */ - protected function preUpdate(array $properties = null) + #[\Override] + protected function preUpdate(?array $properties = null): bool { $this->verifyDates(); $this->setSlug($this->generateSlug()); @@ -534,7 +514,8 @@ protected function preUpdate(array $properties = null) /** * @return boolean Parent postSave(). */ - protected function postSave() + #[\Override] + protected function postSave(): bool { // RoutableTrait $this->generateObjectRoute($this['slug']); @@ -544,9 +525,9 @@ protected function postSave() /** * @param array|null $properties Properties. - * @return boolean */ - protected function postUpdate(array $properties = null) + #[\Override] + protected function postUpdate(?array $properties = null): bool { // RoutableTrait $this->generateObjectRoute($this['slug']); diff --git a/packages/cms/src/Charcoal/Cms/AbstractFaq.php b/packages/cms/src/Charcoal/Cms/AbstractFaq.php index 5db4eba6e..46a5aef10 100644 --- a/packages/cms/src/Charcoal/Cms/AbstractFaq.php +++ b/packages/cms/src/Charcoal/Cms/AbstractFaq.php @@ -1,5 +1,7 @@ defaultData(...))) { $this->setData($this->defaultData()); } } @@ -92,9 +89,8 @@ public function dateTimeDate() /** * Some dates cannot be null - * @return void */ - public function verifyDates() + public function verifyDates(): void { if (!$this['newsDate']) { $this->setNewsDate('now'); @@ -350,9 +346,9 @@ public function isActiveRoute() * {@inheritdoc} * * @see \Charcoal\Source\StorableTrait::preSave() - * @return boolean */ - protected function preSave() + #[\Override] + protected function preSave(): bool { $this->verifyDates(); $this->setSlug($this->generateSlug()); @@ -365,9 +361,9 @@ protected function preSave() * * @see \Charcoal\Source\StorableTrait::preUpdate() * @param array $properties Optional properties to update. - * @return boolean */ - protected function preUpdate(array $properties = null) + #[\Override] + protected function preUpdate(?array $properties = null): bool { $this->verifyDates(); $this->setSlug($this->generateSlug()); @@ -379,7 +375,8 @@ protected function preUpdate(array $properties = null) * @see \Charcoal\Source\StorableTrait::postSave() * @return boolean Parent postSave(). */ - protected function postSave() + #[\Override] + protected function postSave(): bool { // RoutableTrait $this->generateObjectRoute($this['slug']); @@ -390,9 +387,9 @@ protected function postSave() /** * @see \Charcoal\Source\StorableTrait::postUpdate() * @param array|null $properties Properties. - * @return boolean */ - protected function postUpdate(array $properties = null) + #[\Override] + protected function postUpdate(?array $properties = null): bool { // RoutableTrait $this->generateObjectRoute($this['slug']); diff --git a/packages/cms/src/Charcoal/Cms/AbstractSection.php b/packages/cms/src/Charcoal/Cms/AbstractSection.php index 2076af6f2..975ef8888 100644 --- a/packages/cms/src/Charcoal/Cms/AbstractSection.php +++ b/packages/cms/src/Charcoal/Cms/AbstractSection.php @@ -47,10 +47,7 @@ abstract class AbstractSection extends Content implements SectionInterface public const TYPE_EXTERNAL = 'charcoal/cms/section/external-section'; public const DEFAULT_TYPE = self::TYPE_CONTENT; - /** - * @var string - */ - private $sectionType = self::DEFAULT_TYPE; + private string $sectionType = self::DEFAULT_TYPE; /** * @var Translation|string|null @@ -103,11 +100,11 @@ abstract class AbstractSection extends Content implements SectionInterface * Section constructor. * @param array $data Init data. */ - public function __construct(array $data = null) + public function __construct(?array $data = null) { parent::__construct($data); - if (is_callable([ $this, 'defaultData' ])) { + if (is_callable($this->defaultData(...))) { $this->setData($this->defaultData()); } } @@ -119,7 +116,7 @@ public function __construct(array $data = null) */ public function isDeletable() { - return !!$this->id() && !$this->locked(); + return (bool) $this->id() && !$this->locked(); } /** @@ -423,10 +420,9 @@ public function defaultMetaImage() * Route generated on postSave in case * it contains the ID of the section, which * you only get once you have save - * - * @return boolean */ - protected function postSave() + #[\Override] + protected function postSave(): bool { // RoutableTrait if (!$this->locked()) { @@ -440,9 +436,9 @@ protected function postSave() * Check whatever before the update. * * @param array|null $properties Properties. - * @return boolean */ - protected function postUpdate(array $properties = null) + #[\Override] + protected function postUpdate(?array $properties = null): bool { if (!$this->locked()) { $this->generateObjectRoute($this['slug']); @@ -453,10 +449,9 @@ protected function postUpdate(array $properties = null) /** * {@inheritdoc} - * - * @return boolean */ - protected function preSave() + #[\Override] + protected function preSave(): bool { if (!$this->locked()) { $this->setSlug($this->generateSlug()); @@ -469,9 +464,9 @@ protected function preSave() * {@inheritdoc} * * @param array $properties Optional properties to update. - * @return boolean */ - protected function preUpdate(array $properties = null) + #[\Override] + protected function preUpdate(?array $properties = null): bool { if (!$this->locked()) { $this->setSlug($this->generateSlug()); @@ -484,9 +479,9 @@ protected function preUpdate(array $properties = null) * Event called before _deleting_ the object. * * @see \Charcoal\Model\AbstractModel::preDelete() For the "delete" Event. - * @return boolean */ - protected function preDelete() + #[\Override] + protected function preDelete(): bool { if ($this->locked()) { return false; diff --git a/packages/cms/src/Charcoal/Cms/AbstractTag.php b/packages/cms/src/Charcoal/Cms/AbstractTag.php index 29f281069..bca5e1559 100644 --- a/packages/cms/src/Charcoal/Cms/AbstractTag.php +++ b/packages/cms/src/Charcoal/Cms/AbstractTag.php @@ -47,7 +47,7 @@ abstract class AbstractTag extends Content implements TagInterface /** * @param array $data The object's data options. */ - public function __construct(array $data = null) + public function __construct(?array $data = null) { parent::__construct($data); diff --git a/packages/cms/src/Charcoal/Cms/AbstractWebTemplate.php b/packages/cms/src/Charcoal/Cms/AbstractWebTemplate.php index f07337ca2..b72d84150 100644 --- a/packages/cms/src/Charcoal/Cms/AbstractWebTemplate.php +++ b/packages/cms/src/Charcoal/Cms/AbstractWebTemplate.php @@ -61,10 +61,8 @@ abstract class AbstractWebTemplate extends AbstractTemplate /** * Additional SEO metadata. - * - * @var array */ - private $seoMetadata = []; + private \ArrayIterator|array $seoMetadata = []; /** * Inject dependencies from a DI Container. @@ -72,6 +70,7 @@ abstract class AbstractWebTemplate extends AbstractTemplate * @param Container $container A dependencies container instance. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -343,7 +342,7 @@ public function opengraphImage() $img = (string)$this->fallbackOpengraphImage(); } - if ($img) { + if ($img !== '' && $img !== '0') { $uri = $this->baseUrl(); return $uri->withPath(strval($img)); } @@ -384,7 +383,7 @@ public function hasSeoMetadata() return (count($this->seoMetadata) > 0); } - return !empty($this->seoMetadata); + return $this->seoMetadata !== []; } /** @@ -443,12 +442,10 @@ public function appConfig($key = null, $default = null) if ($key) { if (isset($this->appConfig[$key])) { return $this->appConfig[$key]; + } elseif (!is_string($default) && is_callable($default)) { + return $default(); } else { - if (!is_string($default) && is_callable($default)) { - return $default(); - } else { - return $default; - } + return $default; } } @@ -477,10 +474,10 @@ protected function setBaseUrl(UriInterface $uri) */ public function baseUrl() { - if (!isset($this->baseUrl)) { + if ($this->baseUrl === null) { throw new RuntimeException(sprintf( 'The base URI is not defined for [%s]', - get_class($this) + static::class )); } @@ -500,13 +497,11 @@ public function createAbsoluteUrl($uri) $uri = $this->baseUrl()->withPath(''); } else { $parts = parse_url($uri); - if (!isset($parts['scheme'])) { - if (!in_array($uri[0], [ '/', '#', '?' ])) { - $path = isset($parts['path']) ? $parts['path'] : ''; - $query = isset($parts['query']) ? $parts['query'] : ''; - $hash = isset($parts['fragment']) ? $parts['fragment'] : ''; - $uri = $this->baseUrl()->withPath($path)->withQuery($query)->withFragment($hash); - } + if (!isset($parts['scheme']) && !in_array($uri[0], [ '/', '#', '?' ])) { + $path = $parts['path'] ?? ''; + $query = $parts['query'] ?? ''; + $hash = $parts['fragment'] ?? ''; + $uri = $this->baseUrl()->withPath($path)->withQuery($query)->withFragment($hash); } } diff --git a/packages/cms/src/Charcoal/Cms/Config.php b/packages/cms/src/Charcoal/Cms/Config.php index 1a663185d..4c0bfdc09 100644 --- a/packages/cms/src/Charcoal/Cms/Config.php +++ b/packages/cms/src/Charcoal/Cms/Config.php @@ -53,11 +53,11 @@ class Config extends Content implements * Section constructor. * @param array $data The data. */ - public function __construct(array $data = null) + public function __construct(?array $data = null) { parent::__construct($data); - if (is_callable([$this, 'defaultData'])) { + if (is_callable($this->defaultData(...))) { $this->setData($this->defaultData()); } } @@ -65,12 +65,10 @@ public function __construct(array $data = null) // ========================================================================== // SETTERS // ========================================================================== - /** * @param mixed $defaultMetaTitle The default meta title. - * @return self */ - public function setDefaultMetaTitle($defaultMetaTitle) + public function setDefaultMetaTitle($defaultMetaTitle): static { $this->defaultMetaTitle = $this->translator()->translation($defaultMetaTitle); @@ -79,9 +77,8 @@ public function setDefaultMetaTitle($defaultMetaTitle) /** * @param mixed $defaultMetaDescription The default meta description. - * @return self */ - public function setDefaultMetaDescription($defaultMetaDescription) + public function setDefaultMetaDescription($defaultMetaDescription): static { $this->defaultMetaDescription = $this->translator()->translation($defaultMetaDescription); @@ -90,9 +87,8 @@ public function setDefaultMetaDescription($defaultMetaDescription) /** * @param mixed $defaultMetaImage The default meta image. - * @return self */ - public function setDefaultMetaImage($defaultMetaImage) + public function setDefaultMetaImage($defaultMetaImage): static { $this->defaultMetaImage = $defaultMetaImage; @@ -101,9 +97,8 @@ public function setDefaultMetaImage($defaultMetaImage) /** * @param mixed $defaultMetaUrl The default meta url. - * @return self */ - public function setDefaultMetaUrl($defaultMetaUrl) + public function setDefaultMetaUrl($defaultMetaUrl): static { $this->defaultMetaUrl = $this->translator()->translation($defaultMetaUrl); @@ -112,9 +107,8 @@ public function setDefaultMetaUrl($defaultMetaUrl) /** * @param array|StructureMetadata|mixed $socialMedias The social media array. - * @return self */ - public function setSocialMedias($socialMedias) + public function setSocialMedias($socialMedias): static { $this->socialMedias = $socialMedias; @@ -123,9 +117,8 @@ public function setSocialMedias($socialMedias) /** * @param string $defaultFromEmail The default email to send from. - * @return self */ - public function setDefaultFromEmail($defaultFromEmail) + public function setDefaultFromEmail($defaultFromEmail): static { $this->defaultFromEmail = $defaultFromEmail; diff --git a/packages/cms/src/Charcoal/Cms/Config/CmsConfig.php b/packages/cms/src/Charcoal/Cms/Config/CmsConfig.php index 9e6862af0..201f87343 100644 --- a/packages/cms/src/Charcoal/Cms/Config/CmsConfig.php +++ b/packages/cms/src/Charcoal/Cms/Config/CmsConfig.php @@ -74,12 +74,10 @@ class CmsConfig extends AbstractConfig // ========================================================================== // INIT // ========================================================================== - /** * @param ModelInterface $model The object model. - * @return void */ - public function addModel(ModelInterface $model) + public function addModel(ModelInterface $model): void { $this->setData($model->data()); } @@ -87,12 +85,10 @@ public function addModel(ModelInterface $model) // ========================================================================== // SETTERS // ========================================================================== - /** * @param mixed $defaultFromEmail The default email for contact forms. - * @return self */ - public function setDefaultFromEmail($defaultFromEmail) + public function setDefaultFromEmail($defaultFromEmail): static { $this->defaultFromEmail = $defaultFromEmail; @@ -101,9 +97,8 @@ public function setDefaultFromEmail($defaultFromEmail) /** * @param mixed $homeNews The news to display on home page. - * @return self */ - public function setHomeNews($homeNews) + public function setHomeNews($homeNews): static { $this->homeNews = $homeNews; @@ -112,9 +107,8 @@ public function setHomeNews($homeNews) /** * @param mixed $homeEvents The events to display on home page. - * @return self */ - public function setHomeEvents($homeEvents) + public function setHomeEvents($homeEvents): static { $this->homeEvents = $homeEvents; @@ -125,7 +119,7 @@ public function setHomeEvents($homeEvents) * @param array $newsConfig The news configuration object. * @return $this */ - public function setNews(array $newsConfig) + public function setNews(array $newsConfig): static { if (!$this->newsConfig) { $this->newsConfig = new NewsConfig(); @@ -140,7 +134,7 @@ public function setNews(array $newsConfig) * @param array $eventConfig The event configuration object. * @return $this */ - public function setEvent(array $eventConfig) + public function setEvent(array $eventConfig): static { if (!$this->eventConfig) { $this->eventConfig = new EventConfig(); @@ -155,7 +149,7 @@ public function setEvent(array $eventConfig) * @param array $sectionConfig The section configuration object. * @return $this */ - public function setSection(array $sectionConfig) + public function setSection(array $sectionConfig): static { if (!$this->sectionConfig) { $this->sectionConfig = new SectionConfig(); @@ -168,9 +162,8 @@ public function setSection(array $sectionConfig) /** * @param string $contactCategory Must conform City\\Support\\Interface\\ContactCategoryInterface. - * @return self */ - public function setContactCategoryObj($contactCategory) + public function setContactCategoryObj($contactCategory): static { $this->contactCategoryObj = $contactCategory; @@ -179,9 +172,8 @@ public function setContactCategoryObj($contactCategory) /** * @param string $contactObj Must conform City\\Support\\Interface\\ContactInterface. - * @return self */ - public function setContactObj($contactObj) + public function setContactObj($contactObj): static { $this->contactObj = $contactObj; @@ -190,9 +182,8 @@ public function setContactObj($contactObj) /** * @param string $defaultContactCategory The default contact category fallback. - * @return self */ - public function setDefaultContactCategory($defaultContactCategory) + public function setDefaultContactCategory($defaultContactCategory): static { $this->defaultContactCategory = $defaultContactCategory; @@ -201,9 +192,8 @@ public function setDefaultContactCategory($defaultContactCategory) /** * @param array $dateFormats Formats for full dates. - * @return self */ - public function setDateFormats(array $dateFormats) + public function setDateFormats(array $dateFormats): static { $this->dateFormats = array_replace_recursive( $this->dateFormats, @@ -215,9 +205,8 @@ public function setDateFormats(array $dateFormats) /** * @param array $timeFormats Formats for time. - * @return self */ - public function setTimeFormats(array $timeFormats) + public function setTimeFormats(array $timeFormats): static { $this->timeFormats = array_replace_recursive( $this->timeFormats, diff --git a/packages/cms/src/Charcoal/Cms/Config/EventConfig.php b/packages/cms/src/Charcoal/Cms/Config/EventConfig.php index 4d70a901a..e10ccaffc 100644 --- a/packages/cms/src/Charcoal/Cms/Config/EventConfig.php +++ b/packages/cms/src/Charcoal/Cms/Config/EventConfig.php @@ -1,5 +1,7 @@ thumbnail; } @@ -120,7 +119,7 @@ public function parentSectionSlug() * @param integer $numPerPage Number of event per page. * @return EventConfig */ - public function setNumPerPage($numPerPage) + public function setNumPerPage($numPerPage): static { $this->numPerPage = $numPerPage; @@ -131,7 +130,7 @@ public function setNumPerPage($numPerPage) * @param boolean $entryCycle Cycle event or not. * @return EventConfig */ - public function setEntryCycle($entryCycle) + public function setEntryCycle($entryCycle): static { $this->entryCycle = $entryCycle; @@ -143,7 +142,7 @@ public function setEntryCycle($entryCycle) * @param string $lifespan Event expiry. * @return EventConfig */ - public function setLifespan($lifespan) + public function setLifespan($lifespan): static { $this->lifespan = $lifespan; @@ -154,7 +153,7 @@ public function setLifespan($lifespan) * @param string $objType Event object type. * @return EventConfig */ - public function setObjType($objType) + public function setObjType($objType): static { $this->objType = $objType; @@ -165,7 +164,7 @@ public function setObjType($objType) * @param string $category Event category object. * @return EventConfig */ - public function setCategory($category) + public function setCategory($category): static { $this->category = $category; @@ -177,7 +176,7 @@ public function setCategory($category) * @param string $configFeatIdent Config property containing featured event. * @return EventConfig */ - public function setConfigFeatIdent($configFeatIdent) + public function setConfigFeatIdent($configFeatIdent): static { $this->configFeatIdent = $configFeatIdent; @@ -189,7 +188,7 @@ public function setConfigFeatIdent($configFeatIdent) * @param array $thumbnail Event thumbnail size. * @return EventConfig */ - public function setThumbnail(array $thumbnail) + public function setThumbnail(array $thumbnail): static { $this->thumbnail = $thumbnail; @@ -200,7 +199,7 @@ public function setThumbnail(array $thumbnail) * @param string $parentSectionSlug Event parent section (slug). * @return EventConfig */ - public function setParentSectionSlug($parentSectionSlug) + public function setParentSectionSlug($parentSectionSlug): static { $this->parentSectionSlug = $parentSectionSlug; diff --git a/packages/cms/src/Charcoal/Cms/Config/NewsConfig.php b/packages/cms/src/Charcoal/Cms/Config/NewsConfig.php index 68f6f100f..debefc86e 100644 --- a/packages/cms/src/Charcoal/Cms/Config/NewsConfig.php +++ b/packages/cms/src/Charcoal/Cms/Config/NewsConfig.php @@ -1,5 +1,7 @@ thumbnail; } @@ -131,9 +130,8 @@ public function parentSectionSlug() /** * @param integer $numPerPage Number of news per page. - * @return NewsConfig */ - public function setNumPerPage($numPerPage) + public function setNumPerPage($numPerPage): static { $this->numPerPage = $numPerPage; @@ -142,9 +140,8 @@ public function setNumPerPage($numPerPage) /** * @param boolean $entryCycle Cycle news or not. - * @return NewsConfig */ - public function setEntryCycle($entryCycle) + public function setEntryCycle($entryCycle): static { $this->entryCycle = $entryCycle; @@ -154,9 +151,8 @@ public function setEntryCycle($entryCycle) /** * Accept all DateTime string. * @param string $defaultExpiry Expiry. - * @return NewsConfig */ - public function setDefaultExpiry($defaultExpiry) + public function setDefaultExpiry($defaultExpiry): static { $this->defaultExpiry = $defaultExpiry; @@ -165,9 +161,8 @@ public function setDefaultExpiry($defaultExpiry) /** * @param string $median DateTime string. - * @return NewsConfig */ - public function setMedian($median) + public function setMedian($median): static { $this->median = $median; @@ -176,9 +171,8 @@ public function setMedian($median) /** * @param string $objType News object type. - * @return NewsConfig */ - public function setObjType($objType) + public function setObjType($objType): static { $this->objType = $objType; @@ -187,9 +181,8 @@ public function setObjType($objType) /** * @param string $category News category object. - * @return NewsConfig */ - public function setCategory($category) + public function setCategory($category): static { $this->category = $category; @@ -199,9 +192,8 @@ public function setCategory($category) /** * Might be overkill. * @param string $configFeatIdent Config property containing featured news. - * @return NewsConfig */ - public function setConfigFeatIdent($configFeatIdent) + public function setConfigFeatIdent($configFeatIdent): static { $this->configFeatIdent = $configFeatIdent; @@ -211,9 +203,8 @@ public function setConfigFeatIdent($configFeatIdent) /** * resize -> width. * @param array $thumbnail News thumbnail size. - * @return NewsConfig */ - public function setThumbnail(array $thumbnail) + public function setThumbnail(array $thumbnail): static { $this->thumbnail = $thumbnail; @@ -222,9 +213,8 @@ public function setThumbnail(array $thumbnail) /** * @param string $parentSectionSlug News parent section (slug). - * @return NewsConfig */ - public function setParentSectionSlug($parentSectionSlug) + public function setParentSectionSlug($parentSectionSlug): static { $this->parentSectionSlug = $parentSectionSlug; diff --git a/packages/cms/src/Charcoal/Cms/Config/SectionConfig.php b/packages/cms/src/Charcoal/Cms/Config/SectionConfig.php index ce6f8eb95..e324e26cc 100644 --- a/packages/cms/src/Charcoal/Cms/Config/SectionConfig.php +++ b/packages/cms/src/Charcoal/Cms/Config/SectionConfig.php @@ -1,5 +1,7 @@ baseSection = $baseSection; @@ -45,9 +46,8 @@ public function setBaseSection($baseSection) * Set the available section types * * @param mixed $sectionTypes Section types. - * @return SectionConfig */ - public function setSectionTypes($sectionTypes) + public function setSectionTypes($sectionTypes): static { $this->sectionTypes = $sectionTypes; return $this; @@ -55,9 +55,8 @@ public function setSectionTypes($sectionTypes) /** * @param string $objType Section object type. - * @return SectionConfig */ - public function setObjType($objType) + public function setObjType($objType): static { $this->objType = $objType; diff --git a/packages/cms/src/Charcoal/Cms/ConfigInterface.php b/packages/cms/src/Charcoal/Cms/ConfigInterface.php index 9c64641bb..047272138 100644 --- a/packages/cms/src/Charcoal/Cms/ConfigInterface.php +++ b/packages/cms/src/Charcoal/Cms/ConfigInterface.php @@ -1,5 +1,7 @@ defaultData(...))) { $this->setData($this->defaultData()); } } /** * CategoryTrait > itemType() - * - * @return string */ - public function itemType() + public function itemType(): string { return Event::class; } @@ -48,7 +46,7 @@ public function itemType() /** * @return \Charcoal\Model\Collection|array */ - public function loadCategoryItems() + public function loadCategoryItems(): array { return []; } @@ -63,9 +61,8 @@ public function name() /** * @param mixed $name The category name. - * @return self */ - public function setName($name) + public function setName($name): static { $this->name = $this->translator()->translation($name); @@ -74,9 +71,9 @@ public function setName($name) /** * @param ValidatorInterface $v Optional. A custom validator object to use for validation. If null, use object's. - * @return boolean */ - public function validate(ValidatorInterface &$v = null) + #[\Override] + public function validate(?ValidatorInterface &$v = null): bool { parent::validate($v); diff --git a/packages/cms/src/Charcoal/Cms/EventInterface.php b/packages/cms/src/Charcoal/Cms/EventInterface.php index 01426e2e7..2934c471a 100644 --- a/packages/cms/src/Charcoal/Cms/EventInterface.php +++ b/packages/cms/src/Charcoal/Cms/EventInterface.php @@ -1,5 +1,7 @@ categoryType() - * - * @return string */ - public function categoryType() + public function categoryType(): string { return FaqCategory::class; } diff --git a/packages/cms/src/Charcoal/Cms/FaqCategory.php b/packages/cms/src/Charcoal/Cms/FaqCategory.php index fd1d9b41a..180ac340b 100644 --- a/packages/cms/src/Charcoal/Cms/FaqCategory.php +++ b/packages/cms/src/Charcoal/Cms/FaqCategory.php @@ -1,5 +1,7 @@ itemType() - * - * @return string */ - public function itemType() + public function itemType(): string { return Faq::class; } @@ -27,7 +27,7 @@ public function itemType() /** * @return \Charcoal\Model\Collection|array */ - public function loadCategoryItems() + public function loadCategoryItems(): array { return []; } diff --git a/packages/cms/src/Charcoal/Cms/FaqInterface.php b/packages/cms/src/Charcoal/Cms/FaqInterface.php index 04c82a310..ce78ec5e0 100644 --- a/packages/cms/src/Charcoal/Cms/FaqInterface.php +++ b/packages/cms/src/Charcoal/Cms/FaqInterface.php @@ -1,5 +1,7 @@ data() as $lang => $val) { + foreach ($meta->data() as $val) { if ($val && $val != '') { $empty = false; } diff --git a/packages/cms/src/Charcoal/Cms/Mixin/HasContentBlocksInterface.php b/packages/cms/src/Charcoal/Cms/Mixin/HasContentBlocksInterface.php index d5685c7ce..31218c3be 100644 --- a/packages/cms/src/Charcoal/Cms/Mixin/HasContentBlocksInterface.php +++ b/packages/cms/src/Charcoal/Cms/Mixin/HasContentBlocksInterface.php @@ -1,5 +1,7 @@ numContentBlocks()); + return (bool) $this->numContentBlocks(); } /** * Count the number of content blocks associated to this object. - * - * @return integer */ - public function numContentBlocks() + public function numContentBlocks(): int { return count($this->contentBlocks()); } @@ -78,9 +74,7 @@ private function metaDescFromAttachments() if ($attachment->isText()) { $content = $attachment->description(); - $content = $this->ellipsis($content); - - return $content; + return $this->ellipsis($content); } } @@ -122,7 +116,7 @@ private function ellipsis($content, $length = 200) abstract public function getAttachments( $group = null, $type = null, - callable $before = null, - callable $after = null + ?callable $before = null, + ?callable $after = null ); } diff --git a/packages/cms/src/Charcoal/Cms/News.php b/packages/cms/src/Charcoal/Cms/News.php index c1bf4913c..842615668 100644 --- a/packages/cms/src/Charcoal/Cms/News.php +++ b/packages/cms/src/Charcoal/Cms/News.php @@ -1,5 +1,7 @@ categoryType() - * - * @return string */ - public function categoryType() + public function categoryType(): string { return NewsCategory::class; } diff --git a/packages/cms/src/Charcoal/Cms/NewsCategory.php b/packages/cms/src/Charcoal/Cms/NewsCategory.php index f09b9337a..e1afce7bc 100644 --- a/packages/cms/src/Charcoal/Cms/NewsCategory.php +++ b/packages/cms/src/Charcoal/Cms/NewsCategory.php @@ -26,21 +26,19 @@ class NewsCategory extends Content implements CategoryInterface * Section constructor. * @param array $data Init data. */ - public function __construct(array $data = null) + public function __construct(?array $data = null) { parent::__construct($data); - if (is_callable([ $this, 'defaultData' ])) { + if (is_callable($this->defaultData(...))) { $this->setData($this->defaultData()); } } /** * CategoryTrait > itemType() - * - * @return string */ - public function itemType() + public function itemType(): string { return News::class; } @@ -48,7 +46,7 @@ public function itemType() /** * @return \Charcoal\Model\Collection|array */ - public function loadCategoryItems() + public function loadCategoryItems(): array { return []; } @@ -63,9 +61,8 @@ public function name() /** * @param mixed $name The category name. - * @return self */ - public function setName($name) + public function setName($name): static { $this->name = $this->translator()->translation($name); @@ -74,14 +71,14 @@ public function setName($name) /** * @param ValidatorInterface $v Optional. A custom validator object to use for validation. If null, use object's. - * @return boolean */ - public function validate(ValidatorInterface &$v = null) + #[\Override] + public function validate(?ValidatorInterface &$v = null): bool { parent::validate($v); foreach ($this->translator()->locales() as $locale => $value) { - if (!(string)$this['name'][$locale]) { + if ((string)$this['name'][$locale] === '' || (string)$this['name'][$locale] === '0') { $this->validator()->error( (string)$this->translator()->translation([ 'fr' => 'Le NOM doit être rempli dans toutes les langues.', diff --git a/packages/cms/src/Charcoal/Cms/NewsInterface.php b/packages/cms/src/Charcoal/Cms/NewsInterface.php index ba3fc7b4f..cc785d5e8 100644 --- a/packages/cms/src/Charcoal/Cms/NewsInterface.php +++ b/packages/cms/src/Charcoal/Cms/NewsInterface.php @@ -1,5 +1,7 @@ path = ltrim($data['path'], '/'); + $this->path = ltrim((string) $data['path'], '/'); } /** * Determine if the URI path resolves to an object. * * @param Container $container A DI (Pimple) container. - * @return boolean */ - public function pathResolvable(Container $container) + public function pathResolvable(Container $container): bool { $event = $this->loadEventFromPath($container); return ($event instanceof EventInterface) && $event->id(); @@ -72,6 +67,7 @@ public function pathResolvable(Container $container) * @param ResponseInterface $response A PSR-7 compatible Response instance. * @return ResponseInterface */ + #[\Override] public function __invoke( Container $container, RequestInterface $request, @@ -87,11 +83,11 @@ public function __invoke( $templateIdent = (string)$event['templateIdent']; $templateController = (string)$event['templateIdent']; - if (!$templateController) { + if ($templateController === '' || $templateController === '0') { $container['logger']->warning(sprintf( '[%s] Missing template controller on model [%s] for ID [%s]', - get_class($this), - get_class($event), + static::class, + $event::class, $event['id'] )); return $response->withStatus(500); @@ -110,8 +106,8 @@ public function __invoke( if ($templateContent === $templateIdent || $templateContent === '') { $container['logger']->warning(sprintf( '[%s] Missing or bad template identifier on model [%s] for ID [%s]', - get_class($this), - get_class($event), + static::class, + $event::class, $templateIdent )); return $response->withStatus(500); @@ -131,7 +127,7 @@ protected function loadEventFromPath(Container $container) { if ($this->event === null) { $config = $this->config(); - $objType = (isset($config['obj_type']) ? $config['obj_type'] : $this->objType); + $objType = ($config['obj_type'] ?? $this->objType); try { $model = $container['model/factory']->create($objType); @@ -146,11 +142,11 @@ protected function loadEventFromPath(Container $container) $this->event = $model; return $model; } - } catch (Exception $e) { + } catch (Exception) { $container['logger']->debug(sprintf( '[%s] Unable to load model [%s] for path [%s]', - get_class($this), - get_class($model), + static::class, + $model::class, $this->path )); } diff --git a/packages/cms/src/Charcoal/Cms/Route/GenericRoute.php b/packages/cms/src/Charcoal/Cms/Route/GenericRoute.php index 08e66ee41..df4f4c1a8 100644 --- a/packages/cms/src/Charcoal/Cms/Route/GenericRoute.php +++ b/packages/cms/src/Charcoal/Cms/Route/GenericRoute.php @@ -58,17 +58,13 @@ class GenericRoute extends TemplateRoute /** * Store the factory instance for the current class. - * - * @var FactoryInterface */ - private $modelFactory; + private ?\Charcoal\Factory\FactoryInterface $modelFactory = null; /** * Store the collection loader for the current class. - * - * @var CollectionLoader */ - private $collectionLoader; + private ?\Charcoal\Loader\CollectionLoader $collectionLoader = null; /** * Track the state of required dependencies. @@ -103,7 +99,7 @@ public function __construct(array $data) { parent::__construct($data); - $this->setPath(ltrim($data['path'], '/')); + $this->setPath(ltrim((string) $data['path'], '/')); } /** @@ -124,11 +120,7 @@ public function pathResolvable(Container $container) } $contextObject = $this->getContextObject(); - if (!$contextObject || !$this->isValidContextObject($contextObject)) { - return false; - } - - return true; + return $contextObject && $this->isValidContextObject($contextObject); } /** @@ -139,6 +131,7 @@ public function pathResolvable(Container $container) * @param ResponseInterface $response A PSR-7 compatible Response instance. * @return ResponseInterface */ + #[\Override] public function __invoke( Container $container, RequestInterface $request, @@ -173,8 +166,8 @@ public function __invoke( if ($templateContent === $templateIdent || $templateContent === '') { $container['logger']->warning(sprintf( '[%s] Missing or bad template identifier on model [%s] for ID [%s]', - get_class($this), - get_class($this->getContextObject()), + static::class, + $this->getContextObject()::class, $templateIdent )); return $response->withStatus(500); @@ -193,9 +186,7 @@ public function __invoke( */ public function createRouteObject() { - $route = $this->modelFactory()->create($this->objectRouteClass()); - - return $route; + return $this->modelFactory()->create($this->objectRouteClass()); } /** @@ -253,10 +244,7 @@ protected function resolveLatestObjectRoute( return $response; } - /** - * @return self - */ - protected function resolveTemplateContextObject() + protected function resolveTemplateContextObject(): static { $config = $this->config(); @@ -320,10 +308,8 @@ protected function resolveTemplateContextObject() } // Overwrite from custom object template_options - if ($contextObject instanceof TemplateableInterface) { - if (!empty($contextObject['templateOptions'])) { - $templateOptions = $contextObject['templateOptions']; - } + if ($contextObject instanceof TemplateableInterface && !empty($contextObject['templateOptions'])) { + $templateOptions = $contextObject['templateOptions']; } if (isset($templateOptions) && $templateOptions) { @@ -347,6 +333,7 @@ protected function resolveTemplateContextObject() * @param RequestInterface $request The request to intialize the template with. * @return string */ + #[\Override] protected function createTemplate(Container $container, RequestInterface $request) { $template = parent::createTemplate($container, $request); @@ -362,9 +349,8 @@ protected function createTemplate(Container $container, RequestInterface $reques * * @param string $className The class name of the object route model. * @throws InvalidArgumentException If the class name is not a string. - * @return self */ - protected function setObjectRouteClass($className) + protected function setObjectRouteClass($className): static { if (!is_string($className)) { throw new InvalidArgumentException( @@ -402,16 +388,7 @@ protected function isValidContextObject(RoutableInterface $contextObject) if (!$contextObject->id()) { return false; } - - if ($contextObject instanceof RoutableInterface) { - return $contextObject->isActiveRoute(); - } - - if (isset($contextObject['active'])) { - return (bool)$contextObject['active']; - } - - return true; + return $contextObject->isActiveRoute(); } /** @@ -467,9 +444,8 @@ protected function assertValidObjectRoute(ObjectRouteInterface $route) * Determine if the object route is valid. * * @param ObjectRouteInterface $route An object route to test. - * @return boolean */ - protected function isValidObjectRoute(ObjectRouteInterface $route) + protected function isValidObjectRoute(ObjectRouteInterface $route): bool { return ($route->id() && $route->getRouteObjType() && $route->getRouteObjId()); } @@ -552,14 +528,12 @@ public function getLatestObjectPathHistory(ObjectRouteInterface $route) /** * SETTERS */ - /** * Set the specified URI path. * * @param string $path The path to use for route resolution. - * @return self */ - protected function setPath($path) + protected function setPath($path): static { $this->path = $path; @@ -570,9 +544,8 @@ protected function setPath($path) * Set an object model factory. * * @param FactoryInterface $factory The model factory, to create objects. - * @return self */ - protected function setModelFactory(FactoryInterface $factory) + protected function setModelFactory(FactoryInterface $factory): static { $this->modelFactory = $factory; @@ -583,9 +556,8 @@ protected function setModelFactory(FactoryInterface $factory) * Set a model collection loader. * * @param CollectionLoader $loader The collection loader. - * @return self */ - public function setCollectionLoader(CollectionLoader $loader) + public function setCollectionLoader(CollectionLoader $loader): static { $this->collectionLoader = $loader; @@ -617,14 +589,14 @@ protected function setLocale($langCode) $choices = (array)$locale['locales']; array_push($locales, ...$choices); } elseif (isset($locale['locale'])) { - array_push($locales, $locale['locale']); + $locales[] = $locale['locale']; } } } $locales = array_unique($locales); - if ($locales) { + if ($locales !== []) { setlocale(LC_ALL, $locales); } } @@ -647,13 +619,12 @@ protected function path() * Retrieve the object model factory. * * @throws RuntimeException If the model factory was not previously set. - * @return FactoryInterface */ - public function modelFactory() + public function modelFactory(): \Charcoal\Factory\FactoryInterface { - if (!isset($this->modelFactory)) { + if (!$this->modelFactory instanceof \Charcoal\Factory\FactoryInterface) { throw new RuntimeException( - sprintf('Model Factory is not defined for "%s"', get_class($this)) + sprintf('Model Factory is not defined for "%s"', static::class) ); } @@ -664,13 +635,12 @@ public function modelFactory() * Retrieve the model collection loader. * * @throws RuntimeException If the collection loader was not previously set. - * @return CollectionLoader */ - protected function collectionLoader() + protected function collectionLoader(): \Charcoal\Loader\CollectionLoader { - if (!isset($this->collectionLoader)) { + if (!$this->collectionLoader instanceof \Charcoal\Loader\CollectionLoader) { throw new RuntimeException( - sprintf('Collection Loader is not defined for "%s"', get_class($this)) + sprintf('Collection Loader is not defined for "%s"', static::class) ); } @@ -680,6 +650,7 @@ protected function collectionLoader() /** * @return boolean */ + #[\Override] protected function cacheEnabled() { $obj = $this->getContextObject(); @@ -689,16 +660,15 @@ protected function cacheEnabled() /** * @return integer */ + #[\Override] protected function cacheTtl() { $obj = $this->getContextObject(); return $obj['cache_ttl'] ?: 0; } - /** - * @return string - */ - protected function cacheIdent() + #[\Override] + protected function cacheIdent(): string { $obj = $this->getContextObject(); return $obj->objType() . '.' . $obj->id(); diff --git a/packages/cms/src/Charcoal/Cms/Route/NewsRoute.php b/packages/cms/src/Charcoal/Cms/Route/NewsRoute.php index dc33433e5..a919baead 100644 --- a/packages/cms/src/Charcoal/Cms/Route/NewsRoute.php +++ b/packages/cms/src/Charcoal/Cms/Route/NewsRoute.php @@ -26,10 +26,8 @@ class NewsRoute extends TemplateRoute /** * URI path. - * - * @var string */ - private $path; + private string $path; /** * The news entry matching the URI path. @@ -40,10 +38,8 @@ class NewsRoute extends TemplateRoute /** * The news entry model. - * - * @var string */ - private $objType = 'charcoal/cms/news'; + private string $objType = 'charcoal/cms/news'; /** * @param array $data Class depdendencies. @@ -51,16 +47,15 @@ class NewsRoute extends TemplateRoute public function __construct(array $data) { parent::__construct($data); - $this->path = ltrim($data['path'], '/'); + $this->path = ltrim((string) $data['path'], '/'); } /** * Determine if the URI path resolves to an object. * * @param Container $container A DI (Pimple) container. - * @return boolean */ - public function pathResolvable(Container $container) + public function pathResolvable(Container $container): bool { $news = $this->loadNewsFromPath($container); return ($news instanceof NewsInterface) && $news->id(); @@ -72,6 +67,7 @@ public function pathResolvable(Container $container) * @param ResponseInterface $response A PSR-7 compatible Response instance. * @return ResponseInterface */ + #[\Override] public function __invoke( Container $container, RequestInterface $request, @@ -87,11 +83,11 @@ public function __invoke( $templateIdent = (string)$news['templateIdent']; $templateController = (string)$news['templateIdent']; - if (!$templateController) { + if ($templateController === '' || $templateController === '0') { $container['logger']->warning(sprintf( '[%s] Missing template controller on model [%s] for ID [%s]', - get_class($this), - get_class($news), + static::class, + $news::class, $news['id'] )); return $response->withStatus(500); @@ -110,8 +106,8 @@ public function __invoke( if ($templateContent === $templateIdent || $templateContent === '') { $container['logger']->warning(sprintf( '[%s] Missing or bad template identifier on model [%s] for ID [%s]', - get_class($this), - get_class($news), + static::class, + $news::class, $templateIdent )); return $response->withStatus(500); @@ -131,7 +127,7 @@ protected function loadNewsFromPath(Container $container) { if ($this->news === null) { $config = $this->config(); - $objType = (isset($config['obj_type']) ? $config['obj_type'] : $this->objType); + $objType = ($config['obj_type'] ?? $this->objType); try { $model = $container['model/factory']->create($objType); @@ -146,11 +142,11 @@ protected function loadNewsFromPath(Container $container) $this->news = $model; return $model; } - } catch (Exception $e) { + } catch (Exception) { $container['logger']->debug(sprintf( '[%s] Unable to load model [%s] for path [%s]', - get_class($this), - get_class($model), + static::class, + $model::class, $this->path )); } diff --git a/packages/cms/src/Charcoal/Cms/Route/SectionRoute.php b/packages/cms/src/Charcoal/Cms/Route/SectionRoute.php index 797233234..aa3f7a4ef 100644 --- a/packages/cms/src/Charcoal/Cms/Route/SectionRoute.php +++ b/packages/cms/src/Charcoal/Cms/Route/SectionRoute.php @@ -26,10 +26,8 @@ class SectionRoute extends TemplateRoute /** * URI path. - * - * @var string */ - private $path; + private string $path; /** * The section object matching the URI path. @@ -40,10 +38,8 @@ class SectionRoute extends TemplateRoute /** * The section model. - * - * @var string */ - private $objType = 'charcoal/cms/section'; + private string $objType = 'charcoal/cms/section'; /** * @param array $data Class depdendencies. @@ -52,16 +48,15 @@ public function __construct(array $data) { parent::__construct($data); - $this->path = ltrim($data['path'], '/'); + $this->path = ltrim((string) $data['path'], '/'); } /** * Determine if the URI path resolves to an object. * * @param Container $container A DI (Pimple) container. - * @return boolean */ - public function pathResolvable(Container $container) + public function pathResolvable(Container $container): bool { $section = $this->loadSectionFromPath($container); return ($section instanceof SectionInterface) && $section->id(); @@ -73,6 +68,7 @@ public function pathResolvable(Container $container) * @param ResponseInterface $response A PSR-7 compatible Response instance. * @return ResponseInterface */ + #[\Override] public function __invoke( Container $container, RequestInterface $request, @@ -88,11 +84,11 @@ public function __invoke( $templateIdent = (string)$section['templateIdent']; $templateController = (string)$section['templateIdent']; - if (!$templateController) { + if ($templateController === '' || $templateController === '0') { $container['logger']->warning(sprintf( '[%s] Missing template controller on model [%s] for ID [%s]', - get_class($this), - get_class($section), + static::class, + $section::class, $section['id'] )); return $response->withStatus(500); @@ -112,8 +108,8 @@ public function __invoke( if ($templateContent === $templateIdent || $templateContent === '') { $container['logger']->warning(sprintf( '[%s] Missing or bad template identifier on model [%s] for ID [%s]', - get_class($this), - get_class($section), + static::class, + $section::class, $templateIdent )); return $response->withStatus(500); @@ -133,7 +129,7 @@ protected function loadSectionFromPath(Container $container) { if ($this->section === null) { $config = $this->config(); - $objType = (isset($config['obj_type']) ? $config['obj_type'] : $this->objType); + $objType = ($config['obj_type'] ?? $this->objType); try { $model = $container['model/factory']->create($objType); @@ -148,11 +144,11 @@ protected function loadSectionFromPath(Container $container) $this->section = $model; return $model; } - } catch (Exception $e) { + } catch (Exception) { $container['logger']->debug(sprintf( '[%s] Unable to load model [%s] for path [%s]', - get_class($this), - get_class($model), + static::class, + $model::class, $this->path )); } diff --git a/packages/cms/src/Charcoal/Cms/SearchableInterface.php b/packages/cms/src/Charcoal/Cms/SearchableInterface.php index 89c794a6e..57aa639c9 100644 --- a/packages/cms/src/Charcoal/Cms/SearchableInterface.php +++ b/packages/cms/src/Charcoal/Cms/SearchableInterface.php @@ -1,5 +1,7 @@ externalUrl = $this->translator()->translation($url); @@ -31,6 +33,7 @@ public function setExternalUrl($url) /** * @return Translation|string|null */ + #[\Override] public function externalUrl() { return $this->externalUrl; diff --git a/packages/cms/src/Charcoal/Cms/SectionInterface.php b/packages/cms/src/Charcoal/Cms/SectionInterface.php index e4a924d5b..982de3435 100644 --- a/packages/cms/src/Charcoal/Cms/SectionInterface.php +++ b/packages/cms/src/Charcoal/Cms/SectionInterface.php @@ -1,5 +1,7 @@ modelFactory = $factory; @@ -85,9 +84,9 @@ protected function setModelFactory(FactoryInterface $factory) */ public function modelFactory() { - if (!isset($this->modelFactory)) { + if ($this->modelFactory === null) { throw new RuntimeException( - sprintf('Model Factory is not defined for "%s"', get_class($this)) + sprintf('Model Factory is not defined for "%s"', static::class) ); } @@ -98,9 +97,8 @@ public function modelFactory() * Set a model collection loader. * * @param CollectionLoader $loader The collection loader. - * @return self */ - protected function setCollectionLoader(CollectionLoader $loader) + protected function setCollectionLoader(CollectionLoader $loader): static { $this->collectionLoader = $loader; @@ -115,9 +113,9 @@ protected function setCollectionLoader(CollectionLoader $loader) */ public function collectionLoader() { - if (!isset($this->collectionLoader)) { + if ($this->collectionLoader === null) { throw new RuntimeException( - sprintf('Collection Loader is not defined for "%s"', get_class($this)) + sprintf('Collection Loader is not defined for "%s"', static::class) ); } diff --git a/packages/cms/src/Charcoal/Cms/Service/Loader/EventLoader.php b/packages/cms/src/Charcoal/Cms/Service/Loader/EventLoader.php index c536ab4e2..730a04a85 100644 --- a/packages/cms/src/Charcoal/Cms/Service/Loader/EventLoader.php +++ b/packages/cms/src/Charcoal/Cms/Service/Loader/EventLoader.php @@ -137,7 +137,7 @@ public function objType() * @param string $lifespan The lifespan of events. * @return self Chainable */ - public function setLifespan($lifespan) + public function setLifespan($lifespan): static { $this->lifespan = $lifespan; @@ -148,7 +148,7 @@ public function setLifespan($lifespan) * @param object $objType The object type. * @return self Chainable */ - public function setObjType($objType) + public function setObjType($objType): static { $this->objType = $objType; @@ -159,7 +159,7 @@ public function setObjType($objType) * @param mixed $date The date to convert. * @return DateTime */ - private function parseAsDate($date) + private function parseAsDate($date): \DateTimeInterface|\DateTime { if ($date instanceof DateTimeInterface) { return $date; diff --git a/packages/cms/src/Charcoal/Cms/Service/Loader/NewsLoader.php b/packages/cms/src/Charcoal/Cms/Service/Loader/NewsLoader.php index 11a21edda..57dfc0737 100644 --- a/packages/cms/src/Charcoal/Cms/Service/Loader/NewsLoader.php +++ b/packages/cms/src/Charcoal/Cms/Service/Loader/NewsLoader.php @@ -21,6 +21,7 @@ class NewsLoader extends AbstractLoader /** * @var object $objType The object to load. */ + #[\Override] protected $objType; /** @@ -75,9 +76,7 @@ public function expired() */ public function upcoming() { - $loader = $this->published(); - - return $loader; + return $this->published(); } /** @@ -86,9 +85,7 @@ public function upcoming() */ public function archive() { - $loader = $this->expired(); - - return $loader; + return $this->expired(); } /** @@ -109,9 +106,8 @@ public function objType() /** * @param string $median The median between upcoming and archive. - * @return self */ - public function setMedian($median) + public function setMedian($median): static { $this->median = $median; @@ -120,9 +116,8 @@ public function setMedian($median) /** * @param object $objType The object type. - * @return self */ - public function setObjType($objType) + public function setObjType($objType): static { $this->objType = $objType; diff --git a/packages/cms/src/Charcoal/Cms/Service/Loader/SectionLoader.php b/packages/cms/src/Charcoal/Cms/Service/Loader/SectionLoader.php index 47c67e1e8..16d406d19 100644 --- a/packages/cms/src/Charcoal/Cms/Service/Loader/SectionLoader.php +++ b/packages/cms/src/Charcoal/Cms/Service/Loader/SectionLoader.php @@ -77,7 +77,7 @@ public function all() /** * @return \ArrayAccess|\Traversable */ - public function masters() + public function masters(): \ArrayAccess|array { $loader = $this->all(); $operator = []; @@ -92,7 +92,7 @@ public function masters() /** * @return \ArrayAccess|\Traversable */ - public function children() + public function children(): array { $masters = $this->masters(); @@ -144,7 +144,7 @@ public function sectionRoutes() $loader->setModel($proto); $filters = []; - foreach ($sectionTypes as $key => $val) { + foreach ($sectionTypes as $val) { $filters[] = 'route_obj_type = \'' . $val . '\''; } $q = 'SELECT * FROM `' . $proto->source()->table() . '` @@ -213,9 +213,7 @@ public function resolveSectionId($route) return ''; } - $sId = $routes['routes'][$route]; - - return $sId; + return $routes['routes'][$route]; } /** @@ -244,9 +242,8 @@ public function sectionTypes() /** * @param object $objType The object type. - * @return self */ - public function setObjType($objType) + public function setObjType($objType): static { $this->objType = $objType; @@ -255,9 +252,8 @@ public function setObjType($objType) /** * @param integer $baseSection The base section id. - * @return self */ - public function setBaseSection($baseSection) + public function setBaseSection($baseSection): static { $this->baseSection = $baseSection; @@ -266,9 +262,8 @@ public function setBaseSection($baseSection) /** * @param array|null $sectionTypes Available section types. - * @return self */ - public function setSectionTypes(array $sectionTypes = null) + public function setSectionTypes(?array $sectionTypes = null): static { $this->sectionTypes = $sectionTypes; @@ -282,7 +277,7 @@ public function setSectionTypes(array $sectionTypes = null) * @param string $delimiter The word delimiter. * @return string */ - public static function snake($value, $delimiter = '-') + public static function snake($value, string $delimiter = '-') { $key = $value; if (isset(static::$snakeCache[$key][$delimiter])) { @@ -290,7 +285,7 @@ public static function snake($value, $delimiter = '-') } if (!ctype_lower($value)) { $value = preg_replace('/\s+/u', '', $value); - $value = mb_strtolower(preg_replace('/(.)(?=[A-Z])/u', '$1' . $delimiter, $value), 'UTF-8'); + $value = mb_strtolower((string) preg_replace('/(.)(?=[A-Z])/u', '$1' . $delimiter, (string) $value), 'UTF-8'); } static::$snakeCache[$key][$delimiter] = $value; diff --git a/packages/cms/src/Charcoal/Cms/Service/Manager/AbstractManager.php b/packages/cms/src/Charcoal/Cms/Service/Manager/AbstractManager.php index daaac2faf..1a20fb22a 100644 --- a/packages/cms/src/Charcoal/Cms/Service/Manager/AbstractManager.php +++ b/packages/cms/src/Charcoal/Cms/Service/Manager/AbstractManager.php @@ -47,13 +47,13 @@ public function __construct(array $data) if (!isset($data['factory'])) { throw new Exception(sprintf( 'Model Factory must be defined in the %s constructor.', - get_called_class() + static::class )); } if (!isset($data['loader'])) { throw new Exception(sprintf( 'CollectionLoader must be defined in the %s constructor.', - get_called_class() + static::class )); } if (!isset($data['cms/config'])) { @@ -73,9 +73,8 @@ public function __construct(array $data) * Set an object model factory. * * @param FactoryInterface $factory The model factory, to create objects. - * @return self */ - protected function setModelFactory(FactoryInterface $factory) + protected function setModelFactory(FactoryInterface $factory): static { $this->modelFactory = $factory; @@ -90,9 +89,9 @@ protected function setModelFactory(FactoryInterface $factory) */ public function modelFactory() { - if (!isset($this->modelFactory)) { + if ($this->modelFactory === null) { throw new RuntimeException( - sprintf('Model Factory is not defined for "%s"', get_class($this)) + sprintf('Model Factory is not defined for "%s"', static::class) ); } @@ -103,9 +102,8 @@ public function modelFactory() * Set a model collection loader. * * @param CollectionLoader $loader The collection loader. - * @return self */ - protected function setCollectionLoader(CollectionLoader $loader) + protected function setCollectionLoader(CollectionLoader $loader): static { $this->collectionLoader = $loader; @@ -120,9 +118,9 @@ protected function setCollectionLoader(CollectionLoader $loader) */ public function collectionLoader() { - if (!isset($this->collectionLoader)) { + if ($this->collectionLoader === null) { throw new RuntimeException( - sprintf('Collection Loader is not defined for "%s"', get_class($this)) + sprintf('Collection Loader is not defined for "%s"', static::class) ); } @@ -131,9 +129,8 @@ public function collectionLoader() /** * @param mixed $adminConfig The admin configuration. - * @return self */ - public function setAdminConfig($adminConfig) + public function setAdminConfig($adminConfig): static { $this->adminConfig = $adminConfig; diff --git a/packages/cms/src/Charcoal/Cms/Service/Manager/EventManager.php b/packages/cms/src/Charcoal/Cms/Service/Manager/EventManager.php index c6442b579..9bb9e6833 100644 --- a/packages/cms/src/Charcoal/Cms/Service/Manager/EventManager.php +++ b/packages/cms/src/Charcoal/Cms/Service/Manager/EventManager.php @@ -28,13 +28,13 @@ class EventManager extends AbstractManager private $currentEvent; /** @var integer $currentPage The current Page. */ - private $currentPage; + private int|float|null $currentPage = null; /** @var integer $numPerPage Events by page. */ private $numPerPage = 0; /** @var integer $numPage How many pages. */ - private $numPage; + private float|int|null $numPage = null; /** @var boolean $entryCycle Does the pager can cycle indefinitely. */ private $entryCycle = false; @@ -55,13 +55,13 @@ class EventManager extends AbstractManager private $all = []; /** @var EventInterface[] $entries The event collection. */ - private $entries = []; + private array $entries = []; /** @var EventInterface[] $archive The archive events collection. */ - private $archive = []; + private array $archive = []; /** @var EventInterface $entry An event. */ - private $entry; + private ?array $entry = null; /** @var object $objType The event object model. */ private $objType; @@ -76,19 +76,19 @@ class EventManager extends AbstractManager private $loader; /** @var array $mapEvents The events mapped per [year][month][date]. */ - private $mapEvents = []; + private array $mapEvents = []; /** @var datetime $date Datetime filter */ - private $date; + private ?\DateTime $date = null; /** @var mixed $year Year filter. */ - private $year; + private int|float|string|bool|null $year = null; /** @var mixed $month Month filter. */ - private $month; + private int|float|string|bool|null $month = null; /** @var mixed $day Day filter. */ - private $day; + private int|float|string|bool|null $day = null; /** * EventManager constructor. @@ -149,7 +149,7 @@ public function entries() } // Get event from specific date. - if ($date) { + if ($date instanceof \DateTime) { $loader = $this->loader()->all(); $proto = $this->loader()->proto(); $table = $proto->source()->table(); @@ -161,9 +161,7 @@ public function entries() AND active = 1' . $extraSql; - $collection = $loader->loadFromQuery($q); - - return $collection; + return $loader->loadFromQuery($q); } // YEAR only filter. @@ -179,9 +177,7 @@ public function entries() AND active = 1' . $extraSql; - $collection = $loader->loadFromQuery($q); - - return $collection; + return $loader->loadFromQuery($q); } // Year AND month filter. @@ -200,15 +196,11 @@ public function entries() AND active = 1' . $extraSql; - $collection = $loader->loadFromQuery($q); - - return $collection; + return $loader->loadFromQuery($q); } - if (isset($this->entries[$cat])) { - if (isset($this->entries[$cat][$page])) { - return $this->entries[$cat][$page]; - } + if (isset($this->entries[$cat]) && isset($this->entries[$cat][$page])) { + return $this->entries[$cat][$page]; } if ($this->category()) { @@ -264,7 +256,7 @@ public function all() /** * @return CategoryInterface[]|Collection The category collection. */ - public function loadCategoryItems() + public function loadCategoryItems(): \ArrayAccess|array { /** @var Model $model */ $model = $this->modelFactory(); @@ -306,7 +298,7 @@ public function featList(array $options = []) throw new Exception(sprintf( 'The featured news ident "%s" doesn\'t exist the class "%s"', $ident, - get_class($config) + $config::class )); } $ids = $config->{$ident}(); @@ -315,32 +307,30 @@ public function featList(array $options = []) return null; } - $ids = explode(',', $ids); + $ids = explode(',', (string) $ids); $loader->addFilter('id', $ids, [ 'operator' => 'in' ]) ->addOrder('id', 'values', [ 'values' => $ids ]); - if (count($options) > 0) { - foreach ($options as $key => $option) { - switch ($key) { - case 'filters': - $filters = $option; - foreach ($filters as $f) { - $filter[] = $f['property'] ?: ''; - $filter[] = $f['value'] ?: ''; - $filter[] = $f['options'] ?: ''; - $filter = join(',', $filter); - - $loader->addFilter($filter); - } - break; - case 'page': - $loader->setPage($option); - break; - case 'numPerPage': - $loader->setNumPerPage($option); - break; - } + foreach ($options as $key => $option) { + switch ($key) { + case 'filters': + $filters = $option; + foreach ($filters as $f) { + $filter[] = $f['property'] ?: ''; + $filter[] = $f['value'] ?: ''; + $filter[] = $f['options'] ?: ''; + $filter = implode(',', $filter); + + $loader->addFilter($filter); + } + break; + case 'page': + $loader->setPage($option); + break; + case 'numPerPage': + $loader->setNumPerPage($option); + break; } } @@ -356,10 +346,8 @@ public function archive() { $page = $this->page(); $cat = $this->category(); - if (isset($this->archive[$cat])) { - if (isset($this->archive[$cat][$page])) { - return $this->archive[$cat][$page]; - } + if (isset($this->archive[$cat]) && isset($this->archive[$cat][$page])) { + return $this->archive[$cat][$page]; } $loader = $this->loader()->archive(); @@ -420,7 +408,7 @@ public function next() /** * @return float|integer The current event index page ident. */ - public function currentPage() + public function currentPage(): float|int { if ($this->currentPage) { return $this->currentPage; @@ -459,11 +447,7 @@ public function getEventsByDate($date) $month = $date->format('m'); $day = $date->format('d'); - if (isset($map[$year][$month][$day])) { - return $map[$year][$month][$day]; - } - - return []; + return $map[$year][$month][$day] ?? []; } /** @@ -498,38 +482,29 @@ public function entryCycle() * Amount of event (total) * @return integer How many event? */ - public function numEvent() + public function numEvent(): bool { - return !!(count($this->entries())); + return (bool) count($this->entries()); } /** * The total amount of pages. * @return float */ - public function numPages() + public function numPages(): float|int { - if ($this->numPage) { - $this->numPage; - }; - $entries = $this->entries(); $count = count($entries); - if ($this->numPerPage()) { - $this->numPage = ceil($count / $this->numPerPage()); - } else { - $this->numPage = 1; - } + $this->numPage = $this->numPerPage() ? ceil($count / $this->numPerPage()) : 1; return $this->numPage; } /** * Is there a pager. - * @return boolean */ - public function hasPager() + public function hasPager(): bool { return ($this->numPages() > 1); } @@ -578,7 +553,7 @@ public function loader() * Datetime object OR null. * @return mixed Datetime or null. */ - public function date() + public function date(): ?\DateTime { return $this->date; } @@ -587,7 +562,7 @@ public function date() * Full year * @return integer Full year. */ - public function year() + public function year(): int|float|string|bool|null { return $this->year; } @@ -596,7 +571,7 @@ public function year() * Month * @return mixed month. */ - public function month() + public function month(): int|float|string|bool|null { return $this->month; } @@ -605,16 +580,15 @@ public function month() * Day * @return mixed day. */ - public function day() + public function day(): int|float|string|bool|null { return $this->day; } /** * @param mixed $currentEvent The current event context. - * @return self */ - public function setCurrentEvent($currentEvent) + public function setCurrentEvent($currentEvent): static { $this->currentEvent = $currentEvent; @@ -623,9 +597,8 @@ public function setCurrentEvent($currentEvent) /** * @param integer $numPerPage The number of event per page. - * @return self */ - public function setNumPerPage($numPerPage) + public function setNumPerPage($numPerPage): static { $this->numPerPage = $numPerPage; @@ -634,9 +607,8 @@ public function setNumPerPage($numPerPage) /** * @param boolean $entryCycle Next and Prev cycles indefinitely. - * @return self */ - public function setEntryCycle($entryCycle) + public function setEntryCycle($entryCycle): static { $this->entryCycle = $entryCycle; @@ -645,9 +617,8 @@ public function setEntryCycle($entryCycle) /** * @param integer $page The page number to load. - * @return self */ - public function setPage($page) + public function setPage($page): static { $this->page = $page; @@ -656,9 +627,8 @@ public function setPage($page) /** * @param integer $category The current entry category. - * @return self */ - public function setCategory($category) + public function setCategory($category): static { $this->category = $category; @@ -667,9 +637,8 @@ public function setCategory($category) /** * @param mixed $objType The object type. - * @return self */ - public function setObjType($objType) + public function setObjType($objType): static { $this->objType = $objType; @@ -678,9 +647,8 @@ public function setObjType($objType) /** * @param mixed $featIdent The featured list ident. - * @return self */ - public function setFeatIdent($featIdent) + public function setFeatIdent($featIdent): static { $this->featIdent = $featIdent; @@ -689,9 +657,8 @@ public function setFeatIdent($featIdent) /** * @param EventLoader|null $loader The event loader provider. - * @return self */ - public function setLoader($loader) + public function setLoader($loader): static { $this->loader = $loader; @@ -701,9 +668,8 @@ public function setLoader($loader) /** * Set date filter. * @param DateTime $date Date filter. - * @return self */ - public function setDate(DateTime $date) + public function setDate(DateTime $date): static { $this->date = $date; @@ -716,7 +682,7 @@ public function setDate(DateTime $date) * @throws Exception If argument is not scalar. * @return EventManager */ - public function setYear($year) + public function setYear($year): int|float|string|bool { if (!is_scalar($year)) { throw new Exception('Year must be a string or an integer in EventManager setYear method.'); @@ -730,9 +696,8 @@ public function setYear($year) * Month. * @param mixed $month Specific month. * @throws Exception If argument is not scalar. - * @return EventManager */ - public function setMonth($month) + public function setMonth($month): static { if (!is_scalar($month)) { throw new Exception('Month must be a string or an integer in EventManager setMonth method.'); @@ -746,9 +711,8 @@ public function setMonth($month) * Day. * @param mixed $day Specific day. * @throws Exception If argument is not scalar. - * @return EventManager */ - public function setDay($day) + public function setDay($day): static { if (!is_scalar($day)) { throw new Exception('Day must be a string or an integer in EventManager setDay method.'); @@ -762,7 +726,7 @@ public function setDay($day) * Set the Prev and Next event * @return $this */ - public function setPrevNext() + public function setPrevNext(): static { if ($this->prevEvent && $this->nextEvent) { return $this; @@ -815,7 +779,7 @@ public function setPrevNext() * Mapping between events and dates * @return array The array containing events stored as [$year][$month][$day][event] */ - public function mapEvents() + public function mapEvents(): array { if ($this->mapEvents) { return $this->mapEvents; diff --git a/packages/cms/src/Charcoal/Cms/Service/Manager/NewsManager.php b/packages/cms/src/Charcoal/Cms/Service/Manager/NewsManager.php index 09c86071d..8b10fa3af 100644 --- a/packages/cms/src/Charcoal/Cms/Service/Manager/NewsManager.php +++ b/packages/cms/src/Charcoal/Cms/Service/Manager/NewsManager.php @@ -109,10 +109,8 @@ public function entries() { $page = $this->page(); $cat = $this->category(); - if (isset($this->entries[$cat])) { - if (isset($this->entries[$cat][$page])) { - return $this->entries[$cat][$page]; - } + if (isset($this->entries[$cat]) && isset($this->entries[$cat][$page])) { + return $this->entries[$cat][$page]; } $loader = $this->entriesLoader(); @@ -136,7 +134,7 @@ public function entriesLoader() if ($this->numPerPage()) { $loader->setPage($this->page()); - $numPerPage = !!($this->page()) ? $this->numPerPage() : 0; + $numPerPage = $this->page() ? $this->numPerPage() : 0; $loader->setNumPerPage($numPerPage); } @@ -179,7 +177,7 @@ public function all() /** * @return CategoryInterface[]|Collection The category collection. */ - public function loadCategoryItems() + public function loadCategoryItems(): \ArrayAccess|array { $proto = $this->modelFactory()->create($this->categoryItemType()); $loader = $this->collectionLoader()->setModel($proto); @@ -223,7 +221,7 @@ public function featList(array $options = []) throw new Exception(sprintf( 'The featured news ident "%s" doesn\'t exist the class "%s"', $ident, - get_class($config) + $config::class )); } $ids = $config->{$ident}(); @@ -232,32 +230,30 @@ public function featList(array $options = []) return null; } - $ids = explode(',', $ids); + $ids = explode(',', (string) $ids); $loader->addFilter('id', $ids, [ 'operator' => 'in' ]) ->addOrder('id', 'values', [ 'values' => $ids ]); - if (count($options) > 0) { - foreach ($options as $key => $option) { - switch ($key) { - case 'filters': - $filters = $option; - foreach ($filters as $f) { - $filter[] = $f['property'] ?: ''; - $filter[] = $f['value'] ?: ''; - $filter[] = $f['options'] ?: ''; - $filter = join(',', $filter); - - $loader->addFilter($filter); - } - break; - case 'page': - $loader->setPage($option); - break; - case 'numPerPage': - $loader->setNumPerPage($option); - break; - } + foreach ($options as $key => $option) { + switch ($key) { + case 'filters': + $filters = $option; + foreach ($filters as $f) { + $filter[] = $f['property'] ?: ''; + $filter[] = $f['value'] ?: ''; + $filter[] = $f['options'] ?: ''; + $filter = implode(',', $filter); + + $loader->addFilter($filter); + } + break; + case 'page': + $loader->setPage($option); + break; + case 'numPerPage': + $loader->setNumPerPage($option); + break; } } @@ -273,10 +269,8 @@ public function archive() { $page = $this->page(); $cat = $this->category(); - if (isset($this->archive[$cat])) { - if (isset($this->archive[$cat][$page])) { - return $this->archive[$cat][$page]; - } + if (isset($this->archive[$cat]) && isset($this->archive[$cat][$page])) { + return $this->archive[$cat][$page]; } $loader = $this->loader()->archive(); @@ -394,25 +388,23 @@ public function entryCycle() * Amount of news (total) * @return integer How many news? */ - public function numNews() + public function numNews(): bool { - return !!(count($this->entries())); + return (bool) count($this->entries()); } /** * The total amount of pages. - * @return float */ - public function numPages() + public function numPages(): float { return ceil($this->entriesLoader()->loadCount() / $this->numPerPage()); } /** * Is there a pager. - * @return boolean */ - public function hasPager() + public function hasPager(): bool { return ($this->numPages() > 1); } @@ -461,7 +453,7 @@ public function loader() * @param mixed $currentNews The current news context. * @return self . */ - public function setCurrentNews($currentNews) + public function setCurrentNews($currentNews): static { $this->currentNews = $currentNews; @@ -470,9 +462,8 @@ public function setCurrentNews($currentNews) /** * @param integer $numPerPage The number of news per page. - * @return self */ - public function setNumPerPage($numPerPage) + public function setNumPerPage($numPerPage): static { $this->numPerPage = $numPerPage; @@ -481,9 +472,8 @@ public function setNumPerPage($numPerPage) /** * @param boolean $entryCycle Next and Prev cycles indefinitely. - * @return self */ - public function setEntryCycle($entryCycle) + public function setEntryCycle($entryCycle): static { $this->entryCycle = $entryCycle; @@ -492,9 +482,8 @@ public function setEntryCycle($entryCycle) /** * @param integer $page The page number to load. - * @return self */ - public function setPage($page) + public function setPage($page): static { $this->page = $page; @@ -503,9 +492,8 @@ public function setPage($page) /** * @param integer $category The current news category. - * @return self */ - public function setCategory($category) + public function setCategory($category): static { $this->category = $category; @@ -514,9 +502,8 @@ public function setCategory($category) /** * @param mixed $objType The object type. - * @return self */ - public function setObjType($objType) + public function setObjType($objType): static { $this->objType = $objType; @@ -525,9 +512,8 @@ public function setObjType($objType) /** * @param mixed $featIdent The featured list ident. - * @return self */ - public function setFeatIdent($featIdent) + public function setFeatIdent($featIdent): static { $this->featIdent = $featIdent; @@ -536,9 +522,8 @@ public function setFeatIdent($featIdent) /** * @param NewsLoader $loader The news loader provider. - * @return self */ - public function setLoader(NewsLoader $loader) + public function setLoader(NewsLoader $loader): static { $this->loader = $loader; @@ -549,7 +534,7 @@ public function setLoader(NewsLoader $loader) * Set the Prev and Next news * @return $this */ - public function setPrevNext() + public function setPrevNext(): static { if ($this->nextNews && $this->prevNews) { return $this; diff --git a/packages/cms/src/Charcoal/Cms/ServiceProvider/CmsServiceProvider.php b/packages/cms/src/Charcoal/Cms/ServiceProvider/CmsServiceProvider.php index d5dd3ef4d..108940b62 100644 --- a/packages/cms/src/Charcoal/Cms/ServiceProvider/CmsServiceProvider.php +++ b/packages/cms/src/Charcoal/Cms/ServiceProvider/CmsServiceProvider.php @@ -36,9 +36,8 @@ class CmsServiceProvider implements ServiceProviderInterface * It should not get services. * * @param \Pimple\Container $container Pimple DI Container. - * @return void */ - public function register(Container $container) + public function register(Container $container): void { $this->registerConfig($container); $this->reggisterDateHelper($container); @@ -49,15 +48,14 @@ public function register(Container $container) /** * @param Container $container Pimple DI Container. - * @return void */ - private function registerConfig(Container $container) + private function registerConfig(Container $container): void { /** * @param Container $container Pimple DI Container. * @return CmsConfig Website configurations (from cms.json). */ - $container['cms/config'] = function (Container $container) { + $container['cms/config'] = function (Container $container): \Charcoal\Cms\Config\CmsConfig { $appConfig = $container['config']; $cms = $appConfig->get('cms'); @@ -73,7 +71,7 @@ private function registerConfig(Container $container) $model = $container['model/factory']->create($configType); $model->load($configId); - if (!!$model->id()) { + if ((bool) $model->id()) { $cmsConfig->addModel($model); } } @@ -84,21 +82,18 @@ private function registerConfig(Container $container) /** * @param Container $container Pimple DI Container. - * @return void */ - private function reggisterDateHelper(Container $container) + private function reggisterDateHelper(Container $container): void { /** * @param Container $container Pimple DI Container. * @return DateHelper */ - $container['cms/date/helper'] = function (Container $container) { - return new DateHelper([ - 'date_formats' => $container['cms/config']->get('date_formats'), - 'time_formats' => $container['cms/config']->get('time_formats'), - 'translator' => $container['translator'] - ]); - }; + $container['cms/date/helper'] = (fn(Container $container): \Charcoal\Cms\Support\Helpers\DateHelper => new DateHelper([ + 'date_formats' => $container['cms/config']->get('date_formats'), + 'time_formats' => $container['cms/config']->get('time_formats'), + 'translator' => $container['translator'] + ])); /** * @param Container $container Pimple DI Container. @@ -117,29 +112,26 @@ private function reggisterDateHelper(Container $container) /** * @param Container $container Pimple DI Container. - * @return void */ - private function registerSectionServices(Container $container) + private function registerSectionServices(Container $container): void { /** * @param Container $container Pimple DI Container. * @return Factory */ - $container['cms/section/factory'] = function (Container $container) { - return new Factory([ - 'base_class' => SectionInterface::class, - 'arguments' => $container['model/factory']->arguments(), - 'resolver_options' => [ - 'suffix' => 'Section' - ] - ]); - }; + $container['cms/section/factory'] = (fn(Container $container): \Charcoal\Factory\GenericFactory => new Factory([ + 'base_class' => SectionInterface::class, + 'arguments' => $container['model/factory']->arguments(), + 'resolver_options' => [ + 'suffix' => 'Section' + ] + ])); /** * @param Container $container Pimple DI Container. * @return SectionLoader */ - $container['cms/section/loader'] = function (Container $container) { + $container['cms/section/loader'] = function (Container $container): \Charcoal\Cms\Service\Loader\SectionLoader { $sectionLoader = new SectionLoader([ 'loader' => $container['model/collection/loader'], 'factory' => $container['model/factory'], @@ -160,15 +152,14 @@ private function registerSectionServices(Container $container) /** * @param Container $container Pimple DI Container. - * @return void */ - private function registerNewsServices(Container $container) + private function registerNewsServices(Container $container): void { /** * @param Container $container Pimple DI Container. * @return NewsLoader */ - $container['cms/news/loader'] = function (Container $container) { + $container['cms/news/loader'] = function (Container $container): \Charcoal\Cms\Service\Loader\NewsLoader { $newsLoader = new NewsLoader([ 'loader' => $container['model/collection/loader'], 'factory' => $container['model/factory'], @@ -189,32 +180,26 @@ private function registerNewsServices(Container $container) * @param Container $container * @return NewsManager */ - $container['cms/news/manager'] = function (Container $container) { - - $newsManager = new NewsManager([ - 'loader' => $container['model/collection/loader'], - 'factory' => $container['model/factory'], - 'news/loader' => $container['cms/news/loader'], - 'cache' => $container['cache'], - 'cms/config' => $container['cms/config'], - 'translator' => $container['translator'] - ]); - - return $newsManager; - }; + $container['cms/news/manager'] = (fn(Container $container): \Charcoal\Cms\Service\Manager\NewsManager => new NewsManager([ + 'loader' => $container['model/collection/loader'], + 'factory' => $container['model/factory'], + 'news/loader' => $container['cms/news/loader'], + 'cache' => $container['cache'], + 'cms/config' => $container['cms/config'], + 'translator' => $container['translator'] + ])); } /** * @param Container $container Pimple DI Container. - * @return void */ - private function registerEventServices(Container $container) + private function registerEventServices(Container $container): void { /** * @param Container $container Pimple DI Container. * @return EventLoader */ - $container['cms/event/loader'] = function (Container $container) { + $container['cms/event/loader'] = function (Container $container): \Charcoal\Cms\Service\Loader\EventLoader { $eventLoader = new EventLoader([ 'loader' => $container['model/collection/loader'], 'factory' => $container['model/factory'], @@ -238,18 +223,13 @@ private function registerEventServices(Container $container) * @param Container $container * @return EventManager */ - $container['cms/event/manager'] = function (Container $container) { - - $eventManager = new EventManager([ - 'loader' => $container['model/collection/loader'], - 'factory' => $container['model/factory'], - 'event/loader' => $container['cms/event/loader'], - 'cache' => $container['cache'], - 'cms/config' => $container['cms/config'], - 'translator' => $container['translator'] - ]); - - return $eventManager; - }; + $container['cms/event/manager'] = (fn(Container $container): \Charcoal\Cms\Service\Manager\EventManager => new EventManager([ + 'loader' => $container['model/collection/loader'], + 'factory' => $container['model/factory'], + 'event/loader' => $container['cms/event/loader'], + 'cache' => $container['cache'], + 'cms/config' => $container['cms/config'], + 'translator' => $container['translator'] + ])); } } diff --git a/packages/cms/src/Charcoal/Cms/Support/ContextualTemplateTrait.php b/packages/cms/src/Charcoal/Cms/Support/ContextualTemplateTrait.php index 02e23d9b3..a83449822 100644 --- a/packages/cms/src/Charcoal/Cms/Support/ContextualTemplateTrait.php +++ b/packages/cms/src/Charcoal/Cms/Support/ContextualTemplateTrait.php @@ -138,7 +138,7 @@ protected function createGenericContext() $base = $uri->getBasePath(); $path = $uri->getPath(); - $path = $base . '/' . ltrim($path, '/'); + $path = $base . '/' . ltrim((string) $path, '/'); $endpoint[$lang] = $path; } @@ -181,7 +181,7 @@ public function setRouteGroup($path) $group = $this->translator()->translation($path); foreach ($this->translator()->availableLocales() as $lang) { - $group[$lang] = trim($group[$lang], '/'); + $group[$lang] = trim((string) $group[$lang], '/'); } $this->routeGroup = $group; @@ -200,7 +200,7 @@ public function setRouteEndpoint($path) $endpoint = $this->translator()->translation($path); foreach ($this->translator()->availableLocales() as $lang) { - $endpoint[$lang] = trim($endpoint[$lang], '/'); + $endpoint[$lang] = trim((string) $endpoint[$lang], '/'); } $this->routeEndpoint = $endpoint; diff --git a/packages/cms/src/Charcoal/Cms/Support/DocumentTrait.php b/packages/cms/src/Charcoal/Cms/Support/DocumentTrait.php index 0779daec5..56556f458 100644 --- a/packages/cms/src/Charcoal/Cms/Support/DocumentTrait.php +++ b/packages/cms/src/Charcoal/Cms/Support/DocumentTrait.php @@ -14,7 +14,7 @@ trait DocumentTrait * * @return string[] */ - protected function documentTitleParts() + protected function documentTitleParts(): array { return [ 'title' => $this->title(), @@ -24,23 +24,19 @@ protected function documentTitleParts() /** * Retrieve the document title separator. - * - * @return string */ - protected function documentTitleSeparator() + protected function documentTitleSeparator(): string { return '—'; } /** * Parse the document title separator. - * - * @return string */ - protected function parseDocumentTitleSeparator() + protected function parseDocumentTitleSeparator(): string { $delim = trim($this->documentTitleSeparator()); - if (empty($delim)) { + if ($delim === '' || $delim === '0') { return ''; } @@ -53,13 +49,12 @@ protected function parseDocumentTitleSeparator() * @param array $parts The document title parts. * @return string The concatenated title. */ - protected function parseDocumentTitle(array $parts) + protected function parseDocumentTitle(array $parts): string { $parts = $this->parseDocumentTitleParts($parts); $delim = $this->parseDocumentTitleSeparator(); - $title = implode($delim, $parts); - return $title; + return implode($delim, $parts); } /** @@ -73,7 +68,7 @@ protected function parseDocumentTitle(array $parts) * @param array $parts The document title parts. * @return array The parsed and filtered segments. */ - protected function parseDocumentTitleParts(array $parts) + protected function parseDocumentTitleParts(array $parts): array { $segments = []; foreach ($parts as $key => $value) { diff --git a/packages/cms/src/Charcoal/Cms/Support/Helpers/DateHelper.php b/packages/cms/src/Charcoal/Cms/Support/Helpers/DateHelper.php index 5428878d5..dc18a25c2 100644 --- a/packages/cms/src/Charcoal/Cms/Support/Helpers/DateHelper.php +++ b/packages/cms/src/Charcoal/Cms/Support/Helpers/DateHelper.php @@ -79,21 +79,20 @@ public function __construct(array $data) * DateTimeInterface * string. * @param string $format The format to use. - * @return string */ - public function formatDate($date, $format = 'default') + public function formatDate($date, $format = 'default'): string { $this->dateFormat = $format; if (is_array($date)) { $this->from = $this->parseAsDate($date[0]); - $this->to = !!($date[1]) ? $this->parseAsDate($date[1]) : null; + $this->to = $date[1] ? $this->parseAsDate($date[1]) : null; } else { $this->from = $this->parseAsDate($date); $this->to = null; } - return (string)$this->formatDateFromCase($this->getDateCase()); + return $this->formatDateFromCase($this->getDateCase()); } /** @@ -102,9 +101,8 @@ public function formatDate($date, $format = 'default') * DateTimeInterface * string. * @param string $format The format to use. - * @return string */ - public function formatTime($date, $format = 'default') + public function formatTime($date, $format = 'default'): string { $this->timeFormat = $format; @@ -123,7 +121,7 @@ public function formatTime($date, $format = 'default') * Get the usage case by comparing two dates. * @return string */ - private function getDateCase() + private function getDateCase(): ?string { $from = $this->from; $to = $this->to; @@ -148,16 +146,15 @@ private function getDateCase() $case = null; $case = $fromDate['day'] !== $toDate['day'] ? 'different_day' : $case; $case = $fromDate['month'] !== $toDate['month'] ? 'different_month' : $case; - $case = $fromDate['year'] !== $toDate['year'] ? 'different_year' : $case; - return $case; + return $fromDate['year'] !== $toDate['year'] ? 'different_year' : $case; } /** * Get the usage case by comparing two hours. * @return string */ - private function getTimeCase() + private function getTimeCase(): ?string { $from = $this->from; $to = $this->to; @@ -184,16 +181,14 @@ private function getTimeCase() $case = null; $case = $fromTime['hour'] !== $toTime['hour'] ? 'different_time' : $case; $case = $fromTime['minute'] == 0 ? 'different_time_round' : $case; - $case = $fromTime['minute'] != $toTime['minute'] ? 'different_time' : $case; - return $case; + return $fromTime['minute'] != $toTime['minute'] ? 'different_time' : $case; } /** * @param string $case The use case. - * @return string */ - private function formatDateFromCase($case) + private function formatDateFromCase(array $case): string { $dateFormats = $this->dateFormats; $case = $dateFormats[$this->dateFormat][$case]; @@ -248,9 +243,8 @@ private function formatDateFromCase($case) /** * @param string $case The use case. - * @return string */ - private function formatTimeFromCase($case) + private function formatTimeFromCase(array $case): string { $timeFormats = $this->timeFormats; $case = $timeFormats[$this->timeFormat][$case]; @@ -258,7 +252,7 @@ private function formatTimeFromCase($case) $content = $this->translator()->translation($case['content']); $formats['from'] = $case['formats']['from']; - $formats['to'] = isset($case['formats']['to']) ? $case['formats']['to'] : null; + $formats['to'] = $case['formats']['to'] ?? null; $formats['from'] = $this->translator()->translation($formats['from']); $formats['to'] = $this->translator()->translation($formats['to']); @@ -305,7 +299,7 @@ private function formatTimeFromCase($case) * @param mixed $date The date to convert. * @return DateTime */ - private function parseAsDate($date) + private function parseAsDate($date): \DateTimeInterface|\DateTime { if ($date instanceof \DateTimeInterface) { return $date; diff --git a/packages/cms/src/Charcoal/Cms/Support/Interfaces/EventManagerAwareInterface.php b/packages/cms/src/Charcoal/Cms/Support/Interfaces/EventManagerAwareInterface.php index d2a32e452..13a04b3d8 100644 --- a/packages/cms/src/Charcoal/Cms/Support/Interfaces/EventManagerAwareInterface.php +++ b/packages/cms/src/Charcoal/Cms/Support/Interfaces/EventManagerAwareInterface.php @@ -1,5 +1,7 @@ locales()); } @@ -117,10 +114,8 @@ protected function availableLanguages() * Build the alternate translations associated with the current route. * * This method _excludes_ the current route's canonical URI. - * - * @return array */ - protected function buildAlternateTranslations() + protected function buildAlternateTranslations(): array { $translations = []; @@ -171,7 +166,7 @@ protected function getAlternateTranslations() * @param array $localeStruct The currently iterated language. * @return array Returns a link structure. */ - protected function formatAlternateTranslation($context, array $localeStruct) + protected function formatAlternateTranslation(array $context, array $localeStruct): array { return [ 'id' => ($context['id']) ? : $this->templateName(), @@ -222,10 +217,8 @@ public function alternateTranslations() /** * Determine if there exists alternate translations associated with the current route. - * - * @return boolean */ - public function hasAlternateTranslations() + public function hasAlternateTranslations(): bool { return !empty($this->getAlternateTranslations()); } diff --git a/packages/cms/src/Charcoal/Cms/Support/Traits/DateHelperAwareTrait.php b/packages/cms/src/Charcoal/Cms/Support/Traits/DateHelperAwareTrait.php index f4a657ab4..0fe5cd202 100644 --- a/packages/cms/src/Charcoal/Cms/Support/Traits/DateHelperAwareTrait.php +++ b/packages/cms/src/Charcoal/Cms/Support/Traits/DateHelperAwareTrait.php @@ -26,7 +26,7 @@ protected function dateHelper() if (!$this->dateHelper instanceof DateHelper) { throw new ContainerException(sprintf( 'Missing dependency for %s: %s', - get_called_class(), + static::class, DateHelper::class )); } diff --git a/packages/cms/src/Charcoal/Cms/Support/Traits/EventManagerAwareTrait.php b/packages/cms/src/Charcoal/Cms/Support/Traits/EventManagerAwareTrait.php index c29b95372..ab12c04b6 100644 --- a/packages/cms/src/Charcoal/Cms/Support/Traits/EventManagerAwareTrait.php +++ b/packages/cms/src/Charcoal/Cms/Support/Traits/EventManagerAwareTrait.php @@ -231,7 +231,7 @@ protected function getEventTimeFormat(EventInterface $event) * @param EventInterface $event Charcoal\Cms\EventInterface. * @return array The needed event properties. */ - protected function eventFormatShort(EventInterface $event) + protected function eventFormatShort(EventInterface $event): array { return [ 'title' => (string)$event->title(), @@ -251,7 +251,7 @@ protected function eventFormatShort(EventInterface $event) * @param EventInterface $event Charcoal\Cms\EventInterface. * @return array The needed event properties. */ - protected function eventFormatNav(EventInterface $event) + protected function eventFormatNav(EventInterface $event): array { return [ 'startDate' => $this->getEventStartDateFormat($event), @@ -269,7 +269,7 @@ protected function eventFormatNav(EventInterface $event) * @param EventInterface $event The current event. * @return array The needed properties. */ - protected function eventFormatFull(EventInterface $event) + protected function eventFormatFull(EventInterface $event): array { $contentBlocks = $event->getAttachments('content-blocks'); $gallery = $event->getAttachments('image-gallery'); @@ -288,11 +288,11 @@ protected function eventFormatFull(EventInterface $event) 'date' => $this->getEventDateFormat($event), 'time' => $this->getEventTimeFormat($event), 'contentBlocks' => $contentBlocks, - 'hasContentBlocks' => !!(count($contentBlocks)), + 'hasContentBlocks' => (bool) count($contentBlocks), 'documents' => $documents, - 'hasDocuments' => !!(count($documents)), + 'hasDocuments' => (bool) count($documents), 'gallery' => $gallery, - 'hasGallery' => !!(count($gallery)), + 'hasGallery' => (bool) count($gallery), 'url' => $event->url(), 'metaTitle' => (string)$event->metaTitle(), 'locationName' => (string)$event->locationName(), @@ -305,7 +305,7 @@ protected function eventFormatFull(EventInterface $event) * @param CategoryInterface $category The category item. * @return array The formatted category item. */ - protected function eventFormatCategory(CategoryInterface $category) + protected function eventFormatCategory(CategoryInterface $category): array { return [ 'id' => $category->id(), @@ -326,7 +326,7 @@ protected function eventManager() if (!$this->eventManager instanceof EventManager) { throw new ContainerException(sprintf( 'Missing dependency for %s: %s', - get_called_class(), + static::class, EventManager::class )); } diff --git a/packages/cms/src/Charcoal/Cms/Support/Traits/NewsManagerAwareTrait.php b/packages/cms/src/Charcoal/Cms/Support/Traits/NewsManagerAwareTrait.php index 92cb9c80f..7ab611683 100644 --- a/packages/cms/src/Charcoal/Cms/Support/Traits/NewsManagerAwareTrait.php +++ b/packages/cms/src/Charcoal/Cms/Support/Traits/NewsManagerAwareTrait.php @@ -190,7 +190,7 @@ protected function getNewsDateFormat(NewsInterface $news) * @param NewsInterface $news A single news. * @return array The needed news properties. */ - protected function newsFormatShort(NewsInterface $news) + protected function newsFormatShort(NewsInterface $news): array { return [ 'title' => (string)$news->title(), @@ -208,7 +208,7 @@ protected function newsFormatShort(NewsInterface $news) * @param NewsInterface $news A single news. * @return array The needed news properties. */ - protected function newsFormatNav(NewsInterface $news) + protected function newsFormatNav(NewsInterface $news): array { return [ 'date' => $this->getNewsDateFormat($news), @@ -224,7 +224,7 @@ protected function newsFormatNav(NewsInterface $news) * @param NewsInterface $news The current news. * @return array The needed properties. */ - protected function newsFormatFull(NewsInterface $news) + protected function newsFormatFull(NewsInterface $news): array { $contentBlocks = $news->getAttachments('content-blocks'); $gallery = $news->getAttachments('image-gallery'); @@ -239,11 +239,11 @@ protected function newsFormatFull(NewsInterface $news) 'date' => $this->getNewsDateFormat($news), 'dateTime' => $news->newsDate()->format('Y-m-d h:i'), 'contentBlocks' => $contentBlocks, - 'hasContentBlocks' => !!(count($contentBlocks)), + 'hasContentBlocks' => (bool) count($contentBlocks), 'documents' => $documents, - 'hasDocuments' => !!(count($documents)), + 'hasDocuments' => (bool) count($documents), 'gallery' => $gallery, - 'hasGallery' => !!(count($gallery)), + 'hasGallery' => (bool) count($gallery), 'url' => $news->url(), 'metaTitle' => (string)$news->metaTitle(), 'category' => $news->category(), @@ -255,7 +255,7 @@ protected function newsFormatFull(NewsInterface $news) * @param CategoryInterface $category The category item. * @return array The formatted category item. */ - protected function newsFormatCategory(CategoryInterface $category) + protected function newsFormatCategory(CategoryInterface $category): array { return [ 'id' => $category->id(), @@ -276,7 +276,7 @@ protected function newsManager() if (!$this->newsManager instanceof NewsManager) { throw new ContainerException(sprintf( 'Missing dependency for %s: %s', - get_called_class(), + static::class, NewsManager::class )); } diff --git a/packages/cms/src/Charcoal/Cms/Support/Traits/SectionLoaderAwareTrait.php b/packages/cms/src/Charcoal/Cms/Support/Traits/SectionLoaderAwareTrait.php index bf6aa89ae..3ed31cafd 100644 --- a/packages/cms/src/Charcoal/Cms/Support/Traits/SectionLoaderAwareTrait.php +++ b/packages/cms/src/Charcoal/Cms/Support/Traits/SectionLoaderAwareTrait.php @@ -81,9 +81,7 @@ public function childrenSections() */ public function routes() { - return function ($arg) { - return $this->sectionLoader()->resolveRoute($arg); - }; + return fn($arg) => $this->sectionLoader()->resolveRoute($arg); } /** @@ -149,7 +147,7 @@ protected function sectionLoader() if (!$this->sectionLoader instanceof SectionLoader) { throw new ContainerException(sprintf( 'Missing dependency for %s: %s', - get_called_class(), + static::class, SectionLoader::class )); } @@ -171,12 +169,10 @@ protected function setSectionLoader(SectionLoader $loader) // ========================================================================== // FORMATTER // ========================================================================== - /** * @param SectionInterface $section The section to format. - * @return array */ - protected function formatSection(SectionInterface $section) + protected function formatSection(SectionInterface $section): array { $contentBlocks = $section->getAttachments('content-blocks'); $gallery = $section->getAttachments('image-gallery'); diff --git a/packages/cms/src/Charcoal/Cms/Support/Traits/SocialNetworksAwareTrait.php b/packages/cms/src/Charcoal/Cms/Support/Traits/SocialNetworksAwareTrait.php index 4735e1257..52f184890 100644 --- a/packages/cms/src/Charcoal/Cms/Support/Traits/SocialNetworksAwareTrait.php +++ b/packages/cms/src/Charcoal/Cms/Support/Traits/SocialNetworksAwareTrait.php @@ -21,10 +21,8 @@ trait SocialNetworksAwareTrait /** * Determine if the website has a social presence. - * - * @return integer|boolean */ - public function hasSocialNetworks() + public function hasSocialNetworks(): int { return count($this->socialNetworks()); } @@ -41,7 +39,7 @@ public function socialNetworks() return $this->socialNetworks; } - $socials = json_decode($this->cmsConfig()['social_medias'], true); + $socials = json_decode((string) $this->cmsConfig()['social_medias'], true); $configMeta = $this->configModel()->p('social_medias')->structureMetadata(); foreach ($socials as $ident => $account) { diff --git a/packages/cms/src/Charcoal/Cms/Tag.php b/packages/cms/src/Charcoal/Cms/Tag.php index 670e0ea09..30e2ef6b1 100644 --- a/packages/cms/src/Charcoal/Cms/Tag.php +++ b/packages/cms/src/Charcoal/Cms/Tag.php @@ -1,5 +1,7 @@ property($key); $val = $this->propertyValue($key); - $obj = $prop->structureVal($val, $this->templateOptionsMetadata()); - return $obj; + return $prop->structureVal($val, $this->templateOptionsMetadata()); } /** @@ -238,7 +235,7 @@ final protected function assertValidTemplateStructureDependencies() if (!$this instanceof TemplateableInterface) { throw new RuntimeException(sprintf( 'Class [%s] must implement [%s]', - get_class($this), + $this::class, TemplateableInterface::class )); } @@ -260,7 +257,7 @@ final protected function assertValidTemplateStructureDependencies() * @param boolean $recursive Whether we should traverse structure properties. * @return ModelInterface The localized object. */ - protected function translateTemplateOptionsModel(ModelInterface $obj, $recursive = false) + protected function translateTemplateOptionsModel(ModelInterface $obj, $recursive = false): ModelInterface { unset($recursive); foreach ($obj->properties() as $propertyIdent => $property) { @@ -271,7 +268,7 @@ protected function translateTemplateOptionsModel(ModelInterface $obj, $recursive $struct = $property->structureVal($obj[$propertyIdent]); /** Provide support for dynamically wrapping translation sets. */ - if (in_array(get_class($struct), [ Model::class, StructureModel::class ])) { + if (in_array($struct::class, [ Model::class, StructureModel::class ])) { $struct = $this->translateTemplateOptionsModel($struct); } @@ -287,7 +284,7 @@ protected function translateTemplateOptionsModel(ModelInterface $obj, $recursive * * @return string[] */ - protected function defaultTemplateProperties() + protected function defaultTemplateProperties(): array { return [ 'template_ident' @@ -303,7 +300,7 @@ protected function defaultTemplateProperties() * @param PropertyInterface|string ...$properties The properties to lookup. * @return string[]|null */ - protected function extractTemplateInterfacesFrom(...$properties) + protected function extractTemplateInterfacesFrom(...$properties): array { $interfaces = []; foreach ($properties as $property) { @@ -321,10 +318,8 @@ protected function extractTemplateInterfacesFrom(...$properties) if (isset($choice[$key])) { $interface = $choice[$key]; - if ($key === 'template' || $key === 'controller') { - if (substr($interface, -9) !== '-template') { - $interface .= '-template'; - } + if (($key === 'template' || $key === 'controller') && !str_ends_with((string) $interface, '-template')) { + $interface .= '-template'; } $interfaces[] = $interface; @@ -345,9 +340,8 @@ protected function extractTemplateInterfacesFrom(...$properties) * * @uses self::assertValidTemplateStructureDependencies() Validates that the model meets requirements. * @param (PropertyInterface|string)[]|null $templateIdentProperties The template key properties to parse. - * @return boolean */ - protected function prepareTemplateOptions(array $templateIdentProperties = null) + protected function prepareTemplateOptions(?array $templateIdentProperties = null): bool { $this->assertValidTemplateStructureDependencies(); @@ -384,7 +378,7 @@ protected function prepareTemplateOptions(array $templateIdentProperties = null) * @param (PropertyInterface|string)[]|null $properties The template properties to parse. * @return void */ - protected function saveTemplateOptions(array $properties = null) + protected function saveTemplateOptions(?array $properties = null) { if ($properties === null) { $properties = $this->defaultTemplateProperties(); diff --git a/packages/cms/src/Charcoal/Property/TemplateOptionsProperty.php b/packages/cms/src/Charcoal/Property/TemplateOptionsProperty.php index f9346e6df..3e36ad1b1 100644 --- a/packages/cms/src/Charcoal/Property/TemplateOptionsProperty.php +++ b/packages/cms/src/Charcoal/Property/TemplateOptionsProperty.php @@ -14,10 +14,9 @@ class TemplateOptionsProperty extends ModelStructureProperty { /** * Retrieve the property's type identifier. - * - * @return string */ - public function type() + #[\Override] + public function type(): string { return 'template-options'; } @@ -28,13 +27,13 @@ public function type() * @see StructureProperty::addStructureInterface() * @param string $interface A metadata interface to use. * @throws InvalidArgumentException If the template property value is invalid. - * @return TemplateOptionsProperty */ - public function addStructureInterface($interface) + #[\Override] + public function addStructureInterface($interface): static { if ($interface instanceof TemplateProperty) { $interface = (string)$interface; - if (empty($interface)) { + if ($interface === '0') { throw new InvalidArgumentException( 'Invalid template structure interface' ); diff --git a/packages/cms/src/Charcoal/Property/TemplateProperty.php b/packages/cms/src/Charcoal/Property/TemplateProperty.php index fbdb492ea..44759d9df 100644 --- a/packages/cms/src/Charcoal/Property/TemplateProperty.php +++ b/packages/cms/src/Charcoal/Property/TemplateProperty.php @@ -23,15 +23,10 @@ class TemplateProperty extends AbstractProperty implements SelectablePropertyInt /** * The available selectable templates. - * - * @var array|null */ - private $availableTemplates; + private ?array $availableTemplates = null; - /** - * @return string - */ - public function type() + public function type(): string { return 'template'; } @@ -41,9 +36,8 @@ public function type() * * @param string $choiceIdent The choice identifier (will be key / default ident). * @param string|array $choice A string representing the choice label or a structure. - * @return self */ - public function addChoice($choiceIdent, $choice) + public function addChoice($choiceIdent, $choice): static { $choice = $this->parseTemplateChoice($choice, strval($choiceIdent)); @@ -79,7 +73,7 @@ protected function parseTemplateChoice($choice, $choiceIdent) $choice )); } - } elseif (is_bool($choice) && $choice === true) { + } elseif (is_bool($choice) && $choice) { if (isset($this->availableTemplates[$choiceIdent])) { return $this->availableTemplates[$choiceIdent]; } else { @@ -122,6 +116,7 @@ protected function parseTemplateChoice($choice, $choiceIdent) * @param array $options Optional display options. * @return string */ + #[\Override] public function displayVal($val, array $options = []) { if ($val === null || $val === '') { @@ -143,10 +138,8 @@ public function displayVal($val, array $options = []) $separator = $this->multipleSeparator(); /** Parse multiple values / ensure they are of array type. */ - if ($this['multiple']) { - if (!is_array($propertyValue)) { - $propertyValue = explode($separator, $propertyValue); - } + if ($this['multiple'] && !is_array($propertyValue)) { + $propertyValue = explode($separator, (string) $propertyValue); } if ($separator === ',') { @@ -175,10 +168,9 @@ public function displayVal($val, array $options = []) /** * Retrieve the selected template's FQCN. - * - * @return string */ - public function __toString() + #[\Override] + public function __toString(): string { $val = $this->val(); if ($this->hasChoice($val)) { @@ -186,7 +178,7 @@ public function __toString() $keys = [ 'controller', 'template', 'class' ]; foreach ($keys as $key) { if (isset($choice[$key])) { - return $choice[$key]; + return (string) $choice[$key]; } } } @@ -194,10 +186,7 @@ public function __toString() return ''; } - /** - * @return string - */ - public function sqlExtra() + public function sqlExtra(): string { return ''; } @@ -207,7 +196,7 @@ public function sqlExtra() * * @return string The SQL type */ - public function sqlType() + public function sqlType(): string { if ($this['multiple']) { return 'TEXT'; @@ -216,10 +205,7 @@ public function sqlType() return 'VARCHAR(255)'; } - /** - * @return integer - */ - public function sqlPdoType() + public function sqlPdoType(): int { return PDO::PARAM_STR; } @@ -230,6 +216,7 @@ public function sqlPdoType() * @param Container $container A dependencies container instance. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); diff --git a/packages/cms/tests/Charcoal/AbstractTestCase.php b/packages/cms/tests/Charcoal/AbstractTestCase.php index 59ba12ea0..80f1772c4 100644 --- a/packages/cms/tests/Charcoal/AbstractTestCase.php +++ b/packages/cms/tests/Charcoal/AbstractTestCase.php @@ -1,5 +1,7 @@ getContainer(); @@ -56,10 +54,8 @@ protected function getModelDependenciesWithContainer() /** * Retrieve the property's mock dependencies with the service locator. - * - * @return array */ - protected function getPropertyDependenciesWithContainer() + protected function getPropertyDependenciesWithContainer(): array { $container = $this->getContainer(); @@ -97,9 +93,8 @@ protected function getContainerProvider() /** * @see ContainerProvider - * @return void */ - private function setupContainer() + private function setupContainer(): void { $provider = new ContainerProvider(); $container = new Container(); diff --git a/packages/cms/tests/Charcoal/Cms/ContainerProvider.php b/packages/cms/tests/Charcoal/Cms/ContainerProvider.php index a5d9c3930..61739d673 100644 --- a/packages/cms/tests/Charcoal/Cms/ContainerProvider.php +++ b/packages/cms/tests/Charcoal/Cms/ContainerProvider.php @@ -45,9 +45,8 @@ class ContainerProvider { /** * @param Container $container A DI container. - * @return void */ - public function registerBaseServices(Container $container) + public function registerBaseServices(Container $container): void { $this->registerLogger($container); $this->registerCache($container); @@ -59,9 +58,8 @@ public function registerBaseServices(Container $container) /** * @param Container $container A DI container. - * @return void */ - public function registerModelDependencies(Container $container) + public function registerModelDependencies(Container $container): void { $this->registerDatabase($container); $this->registerViewServices($container); @@ -70,44 +68,40 @@ public function registerModelDependencies(Container $container) /** * @param Container $container A DI container. - * @return void */ - public function registerConfig(Container $container) + public function registerConfig(Container $container): void { - $container['config'] = function () { - return new AppConfig([ - 'base_path' => realpath(__DIR__ . '/../../..'), - 'templates' => [], - 'metadata' => [ - 'paths' => [ - 'metadata', - 'tests/Charcoal/Cms/Fixture/metadata', - // Standalone - 'vendor/charcoal/object/metadata', - // Monorepo - '/../object/metadata', - ], + $container['config'] = (fn(): \Charcoal\App\AppConfig => new AppConfig([ + 'base_path' => realpath(__DIR__ . '/../../..'), + 'templates' => [], + 'metadata' => [ + 'paths' => [ + 'metadata', + 'tests/Charcoal/Cms/Fixture/metadata', + // Standalone + 'vendor/charcoal/object/metadata', + // Monorepo + '/../object/metadata', ], - 'view' => [ - 'paths' => [ - 'views', - 'tests/Charcoal/Cms/Fixture/views', - ], - 'default_controller' => GenericTemplate::class, + ], + 'view' => [ + 'paths' => [ + 'views', + 'tests/Charcoal/Cms/Fixture/views', ], - ]); - }; + 'default_controller' => GenericTemplate::class, + ], + ])); } /** * Extend the application configset for a unilingual setup. * * @param Container $container A DI container. - * @return void */ - public function withUnilingualConfig(Container $container) + public function withUnilingualConfig(Container $container): void { - $container->extend('config', function (AppConfig $config) { + $container->extend('config', function (AppConfig $config): \Charcoal\App\AppConfig { $config['locales'] = [ 'languages' => [ 'en' => [ @@ -133,11 +127,10 @@ public function withUnilingualConfig(Container $container) * Extend the application configset for a multilingual setup. * * @param Container $container A DI container. - * @return void */ - public function withMultilingualConfig(Container $container) + public function withMultilingualConfig(Container $container): void { - $container->extend('config', function (AppConfig $config) { + $container->extend('config', function (AppConfig $config): \Charcoal\App\AppConfig { $config['locales'] = [ 'languages' => [ 'en' => [ @@ -194,11 +187,10 @@ public function withMultilingualConfig(Container $container) * Extend the application configset with templates. * * @param Container $container A DI container. - * @return void */ - public function withTemplatesConfig(Container $container) + public function withTemplatesConfig(Container $container): void { - $container->extend('config', function (AppConfig $config) { + $container->extend('config', function (AppConfig $config): \Charcoal\App\AppConfig { $config['templates'] = [ [ 'value' => 'foo', @@ -239,11 +231,10 @@ public function withTemplatesConfig(Container $container) /** * @param Container $container A DI container. - * @return void */ - public function registerDatabase(Container $container) + public function registerDatabase(Container $container): void { - $container['database'] = function () { + $container['database'] = function (): \PDO { $pdo = new PDO('sqlite::memory:'); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); @@ -253,33 +244,26 @@ public function registerDatabase(Container $container) /** * @param Container $container A DI container. - * @return void */ - public function registerLogger(Container $container) + public function registerLogger(Container $container): void { - $container['logger'] = function () { - return new NullLogger(); - }; + $container['logger'] = (fn(): \Psr\Log\NullLogger => new NullLogger()); } /** * @param Container $container A DI container. - * @return void */ - public function registerCache(Container $container) + public function registerCache(Container $container): void { - $container['cache'] = function () { - return new Pool(); - }; + $container['cache'] = (fn(): \Stash\Pool => new Pool()); } /** * Register the admin services. * * @param Container $container A DI container. - * @return void */ - public function registerModelServices(Container $container) + public function registerModelServices(Container $container): void { static $provider = null; @@ -294,9 +278,8 @@ public function registerModelServices(Container $container) * Register the admin services. * * @param Container $container A DI container. - * @return void */ - public function registerAuthServices(Container $container) + public function registerAuthServices(Container $container): void { static $provider = null; @@ -311,9 +294,8 @@ public function registerAuthServices(Container $container) * Setup the application's translator service. * * @param Container $container A DI container. - * @return void */ - public function registerTranslatorServices(Container $container) + public function registerTranslatorServices(Container $container): void { static $provider = null; @@ -328,9 +310,8 @@ public function registerTranslatorServices(Container $container) * Setup the framework's view renderer. * * @param Container $container A DI container. - * @return void */ - public function registerViewServices(Container $container) + public function registerViewServices(Container $container): void { static $provider = null; @@ -343,48 +324,39 @@ public function registerViewServices(Container $container) /** * @param Container $container A DI container. - * @return void */ - public function registerCmsConfig(Container $container) + public function registerCmsConfig(Container $container): void { - $container['cms/config'] = function () { - return new CmsConfig(); - }; + $container['cms/config'] = (fn(): \Charcoal\Cms\Config\CmsConfig => new CmsConfig()); } /** * @param Container $container A DI container. - * @return void */ - public function registerDateHelper(Container $container) + public function registerDateHelper(Container $container): void { - $container['date/helper'] = function () { - return new DateHelper([ - 'date_formats' => '', - 'time_formats' => '', - ]); - }; + $container['date/helper'] = (fn(): \Charcoal\Cms\Support\Helpers\DateHelper => new DateHelper([ + 'date_formats' => '', + 'time_formats' => '', + ])); } /** * @param Container $container A DI container. - * @return void */ - public function registerTemplateFactory(Container $container) + public function registerTemplateFactory(Container $container): void { - $container['template/factory'] = function (Container $container) { - return new Factory([ - 'base_class' => TemplateInterface::class, - 'resolver_options' => [ - 'suffix' => 'Template', - ], - 'arguments' => [ - [ - 'container' => $container, - 'logger' => $container['logger'], - ], + $container['template/factory'] = (fn(Container $container): \Charcoal\Factory\GenericFactory => new Factory([ + 'base_class' => TemplateInterface::class, + 'resolver_options' => [ + 'suffix' => 'Template', + ], + 'arguments' => [ + [ + 'container' => $container, + 'logger' => $container['logger'], ], - ]); - }; + ], + ])); } } diff --git a/packages/cms/tests/Charcoal/Cms/EventCategoryTest.php b/packages/cms/tests/Charcoal/Cms/EventCategoryTest.php index cc74529db..856d5e933 100644 --- a/packages/cms/tests/Charcoal/Cms/EventCategoryTest.php +++ b/packages/cms/tests/Charcoal/Cms/EventCategoryTest.php @@ -17,15 +17,11 @@ class EventCategoryTest extends AbstractTestCase /** * Tested Class. - * - * @var EventCategory */ - private $obj; + private \Charcoal\Cms\EventCategory $obj; /** * Set up the test. - * - * @return void */ protected function setUp(): void { @@ -34,18 +30,12 @@ protected function setUp(): void $this->obj = new EventCategory($dependencies); } - /** - * @return void - */ - public function testItemType() + public function testItemType(): void { $this->assertEquals(Event::class, $this->obj->itemType()); } - /** - * @return void - */ - public function testValidate() + public function testValidate(): void { $this->assertFalse($this->obj->validate()); $this->obj->setName([ 'fr' => 'Titre' ]); diff --git a/packages/cms/tests/Charcoal/Cms/EventTest.php b/packages/cms/tests/Charcoal/Cms/EventTest.php index 86178a2eb..a03c935c9 100644 --- a/packages/cms/tests/Charcoal/Cms/EventTest.php +++ b/packages/cms/tests/Charcoal/Cms/EventTest.php @@ -22,15 +22,11 @@ class EventTest extends AbstractTestCase /** * Tested Class. - * - * @var Event */ - private $obj; + private \Charcoal\Cms\Event|array $obj; /** * Set up the test. - * - * @return void */ protected function setUp(): void { @@ -46,10 +42,7 @@ protected function setUp(): void $this->obj = new Event($dependencies); } - /** - * @return void - */ - public function testSetData() + public function testSetData(): void { $ret = $this->obj->setData([ 'title' => 'Example title', @@ -80,10 +73,7 @@ public function testSetData() $this->assertEquals(50, $this->obj->ticketPriceMax()); } - /** - * @return void - */ - public function testSetTitle() + public function testSetTitle(): void { $this->assertEquals('', (string)$this->obj->title()); $ret = $this->obj->setTitle('Foo bar'); @@ -97,10 +87,7 @@ public function testSetTitle() $this->assertEquals('Hello', (string)$this->obj['title']); } - /** - * @return void - */ - public function testSetSubtitle() + public function testSetSubtitle(): void { $this->assertEquals('', (string)$this->obj->subtitle()); $ret = $this->obj->setSubtitle('Bar foo'); @@ -114,10 +101,7 @@ public function testSetSubtitle() $this->assertEquals('foo', (string)$this->obj['subtitle']); } - /** - * @return void - */ - public function testSetSummary() + public function testSetSummary(): void { $this->assertEquals('', (string)$this->obj->summary()); $ret = $this->obj->setSummary('Bar foo baz'); @@ -131,10 +115,7 @@ public function testSetSummary() $this->assertEquals('foo', (string)$this->obj['summary']); } - /** - * @return void - */ - public function testSetContent() + public function testSetContent(): void { $this->assertEquals('', (string)$this->obj->content()); $ret = $this->obj->setContent('Bar foo'); @@ -148,10 +129,7 @@ public function testSetContent() $this->assertEquals('foo', (string)$this->obj['content']); } - /** - * @return void - */ - public function testSetStartDate() + public function testSetStartDate(): void { $this->assertEquals(null, $this->obj->startDate()); $ret = $this->obj->setStartDate('2016-02-02'); @@ -165,19 +143,13 @@ public function testSetStartDate() $this->obj->setStartDate([]); } - /** - * @return void - */ - public function testSetStartDateInvalidString() + public function testSetStartDateInvalidString(): void { $this->expectException('\Exception'); $this->obj->setStartDate('foo.bar'); } - /** - * @return void - */ - public function testSetEndDate() + public function testSetEndDate(): void { $this->assertEquals(null, $this->obj->endDate()); $ret = $this->obj->setEndDate('2016-02-02'); @@ -191,27 +163,18 @@ public function testSetEndDate() $this->obj->setEndDate([]); } - /** - * @return void - */ - public function testSetEndDateInvalidString() + public function testSetEndDateInvalidString(): void { $this->expectException('\Exception'); $this->obj->setEndDate('foo.bar'); } - /** - * @return void - */ - public function testCategoryType() + public function testCategoryType(): void { $this->assertEquals(EventCategory::class, $this->obj->categoryType()); } - /** - * @return void - */ - public function testMetaTitleDefaultsToTitle() + public function testMetaTitleDefaultsToTitle(): void { $this->assertEquals('', (string)$this->obj->metaTitle()); @@ -223,10 +186,7 @@ public function testMetaTitleDefaultsToTitle() $this->assertEquals('Barfoo', (string)$this->obj->metaTitle()); } - /** - * @return void - */ - public function testMetaDescriptionDefaultsToDescription() + public function testMetaDescriptionDefaultsToDescription(): void { $this->assertEquals('', (string)$this->obj->metaDescription()); @@ -238,27 +198,20 @@ public function testMetaDescriptionDefaultsToDescription() $this->assertEquals('Barfoo', (string)$this->obj->metaDescription()); } - /** - * @return void - */ /* public function testMetaImageDefaultsToImage() { $this->assertEquals('', (string)$this->obj->metaImage()); - + $this->obj->setImage('Foo.png'); $this->assertSame($this->obj->image(), $this->obj->metaImage()); $this->assertEquals('Foo.png', (string)$this->obj->metaImage()); - + $this->obj->setMetaImage('Bar.jpg'); $this->assertEquals('Bar.jpg', (string)$this->obj->metaImage()); } */ - - /** - * @return void - */ - public function testSaveGeneratesSlug() + public function testSaveGeneratesSlug(): void { $this->assertEquals('', $this->obj['slug']); $this->obj->setData([ @@ -269,10 +222,7 @@ public function testSaveGeneratesSlug() $this->assertEquals('en/events/foo', (string)$this->obj['slug']); } - /** - * @return void - */ - public function testUpdateGeneratesSlug() + public function testUpdateGeneratesSlug(): void { $this->assertEquals('', $this->obj['slug']); $this->obj->setData([ diff --git a/packages/cms/tests/Charcoal/Cms/FaqCategoryTest.php b/packages/cms/tests/Charcoal/Cms/FaqCategoryTest.php index a7e5a54c7..ccfebb70e 100644 --- a/packages/cms/tests/Charcoal/Cms/FaqCategoryTest.php +++ b/packages/cms/tests/Charcoal/Cms/FaqCategoryTest.php @@ -17,15 +17,11 @@ class FaqCategoryTest extends AbstractTestCase /** * Tested Class. - * - * @var FaqCategory */ - private $obj; + private \Charcoal\Cms\FaqCategory $obj; /** * Set up the test. - * - * @return void */ protected function setUp(): void { @@ -34,10 +30,7 @@ protected function setUp(): void $this->obj = new FaqCategory($dependencies); } - /** - * @return void - */ - public function testItemType() + public function testItemType(): void { $this->assertEquals(Faq::class, $this->obj->itemType()); } diff --git a/packages/cms/tests/Charcoal/Cms/FaqTest.php b/packages/cms/tests/Charcoal/Cms/FaqTest.php index 95422a5ac..95cfa2c83 100644 --- a/packages/cms/tests/Charcoal/Cms/FaqTest.php +++ b/packages/cms/tests/Charcoal/Cms/FaqTest.php @@ -17,15 +17,11 @@ class FaqTest extends AbstractTestCase /** * Tested Class. - * - * @var Faq */ - private $obj; + private \Charcoal\Cms\Faq $obj; /** * Set up the test. - * - * @return void */ protected function setUp(): void { @@ -34,10 +30,7 @@ protected function setUp(): void $this->obj = new Faq($dependencies); } - /** - * @return void - */ - public function testSetData() + public function testSetData(): void { $ret = $this->obj->setData([ 'question' => 'Foo?', @@ -48,30 +41,21 @@ public function testSetData() $this->assertEquals('Bar', (string)$this->obj->answer()); } - /** - * @return void - */ - public function testSetQuestion() + public function testSetQuestion(): void { $ret = $this->obj->setQuestion('Foo?'); $this->assertSame($ret, $this->obj); $this->assertEquals('Foo?', $this->obj->question()); } - /** - * @return void - */ - public function testSetAnswer() + public function testSetAnswer(): void { $ret = $this->obj->setAnswer('Bar'); $this->assertSame($ret, $this->obj); $this->assertEquals('Bar', $this->obj->answer()); } - /** - * @return void - */ - public function testCategoryType() + public function testCategoryType(): void { $this->assertEquals(FaqCategory::class, $this->obj->categoryType()); } diff --git a/packages/cms/tests/Charcoal/Cms/MetatagTraitTest.php b/packages/cms/tests/Charcoal/Cms/MetatagTraitTest.php index f66380b25..1762f4695 100644 --- a/packages/cms/tests/Charcoal/Cms/MetatagTraitTest.php +++ b/packages/cms/tests/Charcoal/Cms/MetatagTraitTest.php @@ -23,9 +23,6 @@ class MetatagTraitTest extends AbstractTestCase */ public $obj; - /** - * @return void - */ protected function setUp(): void { $container = $this->getContainer(); @@ -42,19 +39,14 @@ protected function setUp(): void /** * Asserts that the object implements MetatagInterface. - * - * @coversNothing - * @return void */ - public function testMetatagInterface() + #[\PHPUnit\Framework\Attributes\CoversNothing] + public function testMetatagInterface(): void { $this->assertInstanceOf(MetatagInterface::class, $this->obj); } - /** - * @return void - */ - public function testSaveGeneratesMetaTags() + public function testSaveGeneratesMetaTags(): void { $this->assertEquals('', (string)$this->obj->metaTitle()); $this->assertEquals('', (string)$this->obj->metaDescription()); @@ -72,10 +64,7 @@ public function testSaveGeneratesMetaTags() $this->assertEquals('x.jpg', (string)$this->obj->metaImage()); } - /** - * @return void - */ - public function testUpdateGeneratesMetaTags() + public function testUpdateGeneratesMetaTags(): void { $this->assertEquals('', (string)$this->obj->metaTitle()); $this->assertEquals('', (string)$this->obj->metaDescription()); diff --git a/packages/cms/tests/Charcoal/Cms/Mock/BrokenTemplate.php b/packages/cms/tests/Charcoal/Cms/Mock/BrokenTemplate.php index f04e6ae62..be6c9c688 100644 --- a/packages/cms/tests/Charcoal/Cms/Mock/BrokenTemplate.php +++ b/packages/cms/tests/Charcoal/Cms/Mock/BrokenTemplate.php @@ -1,5 +1,7 @@ event; } /** * @param EventInterface $event The current event. - * @return self */ - public function setEvent(EventInterface $event) + public function setEvent(EventInterface $event): static { $this->event = $event; @@ -52,16 +44,15 @@ public function setEvent(EventInterface $event) /** * @return NewsInterface */ - public function news() + public function news(): ?\Charcoal\Cms\NewsInterface { return $this->news; } /** * @param NewsInterface $news The current news. - * @return self */ - public function setNews(NewsInterface $news) + public function setNews(NewsInterface $news): static { $this->news = $news; @@ -71,16 +62,15 @@ public function setNews(NewsInterface $news) /** * @return SectionInterface */ - public function section() + public function section(): ?\Charcoal\Cms\SectionInterface { return $this->section; } /** * @param SectionInterface $section The current section. - * @return self */ - public function setSection(SectionInterface $section) + public function setSection(SectionInterface $section): static { $this->section = $section; diff --git a/packages/cms/tests/Charcoal/Cms/Mock/EventTemplate.php b/packages/cms/tests/Charcoal/Cms/Mock/EventTemplate.php index d99e5b990..3d2a94c9c 100644 --- a/packages/cms/tests/Charcoal/Cms/Mock/EventTemplate.php +++ b/packages/cms/tests/Charcoal/Cms/Mock/EventTemplate.php @@ -1,5 +1,7 @@ event; } /** * @param EventInterface $event The current event. - * @return self */ - public function setEvent(EventInterface $event) + public function setEvent(EventInterface $event): static { $this->event = $event; diff --git a/packages/cms/tests/Charcoal/Cms/Mock/GenericTemplate.php b/packages/cms/tests/Charcoal/Cms/Mock/GenericTemplate.php index 16b7ae678..dccd43c7c 100644 --- a/packages/cms/tests/Charcoal/Cms/Mock/GenericTemplate.php +++ b/packages/cms/tests/Charcoal/Cms/Mock/GenericTemplate.php @@ -1,5 +1,7 @@ section; } /** * @param SectionInterface $section The current section. - * @return self */ - public function setSection(SectionInterface $section) + public function setSection(SectionInterface $section): static { $this->section = $section; diff --git a/packages/cms/tests/Charcoal/Cms/Mock/HomeTemplate.php b/packages/cms/tests/Charcoal/Cms/Mock/HomeTemplate.php index cabc511b1..3c8969c04 100644 --- a/packages/cms/tests/Charcoal/Cms/Mock/HomeTemplate.php +++ b/packages/cms/tests/Charcoal/Cms/Mock/HomeTemplate.php @@ -1,5 +1,7 @@ section; } /** * @param SectionInterface $section The current section. - * @return self */ - public function setSection(SectionInterface $section) + public function setSection(SectionInterface $section): static { $this->section = $section; diff --git a/packages/cms/tests/Charcoal/Cms/Mock/NewsTemplate.php b/packages/cms/tests/Charcoal/Cms/Mock/NewsTemplate.php index cab71e68f..ea2114d92 100644 --- a/packages/cms/tests/Charcoal/Cms/Mock/NewsTemplate.php +++ b/packages/cms/tests/Charcoal/Cms/Mock/NewsTemplate.php @@ -1,5 +1,7 @@ news; } /** * @param NewsInterface $news The current news. - * @return self */ - public function setNews(NewsInterface $news) + public function setNews(NewsInterface $news): static { $this->news = $news; diff --git a/packages/cms/tests/Charcoal/Cms/Mock/TemplateableModel.php b/packages/cms/tests/Charcoal/Cms/Mock/TemplateableModel.php index c32909ae1..90dde6fa6 100644 --- a/packages/cms/tests/Charcoal/Cms/Mock/TemplateableModel.php +++ b/packages/cms/tests/Charcoal/Cms/Mock/TemplateableModel.php @@ -1,5 +1,7 @@ saveTemplateOptions(); @@ -36,7 +39,8 @@ public function preSave() * @param array $properties Optional. The list of properties to update. * @return boolean */ - public function preUpdate(array $properties = null) + #[\Override] + public function preUpdate(?array $properties = null) { if ($properties === null || array_search('template_options', $properties)) { $this->saveTemplateOptions(); diff --git a/packages/cms/tests/Charcoal/Cms/Mock/WebPage.php b/packages/cms/tests/Charcoal/Cms/Mock/WebPage.php index efba18a59..f7a18c6ab 100644 --- a/packages/cms/tests/Charcoal/Cms/Mock/WebPage.php +++ b/packages/cms/tests/Charcoal/Cms/Mock/WebPage.php @@ -1,5 +1,7 @@ generateDefaultMetaTags(); @@ -26,9 +27,9 @@ public function preSave() * Update object in storage. * * @param array $properties Optional. The list of properties to update. - * @return boolean */ - public function preUpdate(array $properties = null) + #[\Override] + public function preUpdate(?array $properties = null): bool { $this->generateDefaultMetaTags(); diff --git a/packages/cms/tests/Charcoal/Cms/NewsCategoryTest.php b/packages/cms/tests/Charcoal/Cms/NewsCategoryTest.php index ce606199d..0632a25b9 100644 --- a/packages/cms/tests/Charcoal/Cms/NewsCategoryTest.php +++ b/packages/cms/tests/Charcoal/Cms/NewsCategoryTest.php @@ -17,15 +17,11 @@ class NewsCategoryTest extends AbstractTestCase /** * Tested Class. - * - * @var NewsCategory */ - private $obj; + private \Charcoal\Cms\NewsCategory $obj; /** * Set up the test. - * - * @return void */ protected function setUp(): void { @@ -34,10 +30,7 @@ protected function setUp(): void $this->obj = new NewsCategory($dependencies); } - /** - * @return void - */ - public function testItemType() + public function testItemType(): void { $this->assertEquals(News::class, $this->obj->itemType()); } diff --git a/packages/cms/tests/Charcoal/Cms/NewsTest.php b/packages/cms/tests/Charcoal/Cms/NewsTest.php index 89b992c62..bbad38b48 100644 --- a/packages/cms/tests/Charcoal/Cms/NewsTest.php +++ b/packages/cms/tests/Charcoal/Cms/NewsTest.php @@ -22,15 +22,11 @@ class NewsTest extends AbstractTestCase /** * Tested Class. - * - * @var News */ - private $obj; + private \Charcoal\Cms\News|array $obj; /** * Set up the test. - * - * @return void */ protected function setUp(): void { @@ -46,10 +42,7 @@ protected function setUp(): void $this->obj = new News($dependencies); } - /** - * @return void - */ - public function testSetData() + public function testSetData(): void { $ret = $this->obj->setData([ 'title' => 'Example title', @@ -65,10 +58,7 @@ public function testSetData() $this->assertEquals(new DateTime('2015-01-01 20:00:00'), $this->obj->newsDate()); } - /** - * @return void - */ - public function testSetTitle() + public function testSetTitle(): void { $this->assertEquals('', (string)$this->obj->title()); $ret = $this->obj->setTitle('Foo bar'); @@ -82,10 +72,7 @@ public function testSetTitle() $this->assertEquals('Hello', (string)$this->obj['title']); } - /** - * @return void - */ - public function testSetSubtitle() + public function testSetSubtitle(): void { $this->assertEquals('', (string)$this->obj->subtitle()); $ret = $this->obj->setSubtitle('Bar foo'); @@ -99,10 +86,7 @@ public function testSetSubtitle() $this->assertEquals('foo', (string)$this->obj['subtitle']); } - /** - * @return void - */ - public function testSetSummary() + public function testSetSummary(): void { $this->assertEquals('', (string)$this->obj->summary()); $ret = $this->obj->setSummary('Bar foo'); @@ -116,10 +100,7 @@ public function testSetSummary() $this->assertEquals('foo', (string)$this->obj['summary']); } - /** - * @return void - */ - public function testSetContent() + public function testSetContent(): void { $this->assertEquals('', (string)$this->obj->content()); $ret = $this->obj->setContent('Bar foo'); @@ -133,10 +114,7 @@ public function testSetContent() $this->assertEquals('foo', (string)$this->obj['content']); } - /** - * @return void - */ - public function testSetNewsDate() + public function testSetNewsDate(): void { $this->assertEquals(null, $this->obj->newsDate()); $ret = $this->obj->setNewsDate('2016-02-02'); @@ -150,19 +128,13 @@ public function testSetNewsDate() $this->obj->setNewsDate([]); } - /** - * @return void - */ - public function testSetNewsDateInvalidString() + public function testSetNewsDateInvalidString(): void { $this->expectException('\Exception'); $this->obj->setNewsDate('foo.bar'); } - /** - * @return void - */ - public function testMetaTitleDefaultsToTitle() + public function testMetaTitleDefaultsToTitle(): void { $this->assertEquals('', (string)$this->obj->metaTitle()); @@ -174,10 +146,7 @@ public function testMetaTitleDefaultsToTitle() $this->assertEquals('Barfoo', (string)$this->obj->metaTitle()); } - /** - * @return void - */ - public function testMetaDescriptionDefaultsToDescription() + public function testMetaDescriptionDefaultsToDescription(): void { $this->assertEquals('', (string)$this->obj->metaDescription()); @@ -189,45 +158,31 @@ public function testMetaDescriptionDefaultsToDescription() $this->assertEquals('Barfoo', (string)$this->obj->metaDescription()); } - /** - * @return void - */ /* public function testMetaImageDefaultsToImage() { $this->assertEquals('', (string)$this->obj->metaImage()); - + $this->obj->setImage('Foo.png'); $this->assertSame($this->obj->image(), $this->obj->metaImage()); $this->assertEquals('Foo.png', (string)$this->obj->metaImage()); - + $this->obj->setMetaImage('Bar.jpg'); $this->assertEquals('Bar.jpg', (string)$this->obj->metaImage()); } */ - - /** - * @return void - */ - public function testCategoryType() + public function testCategoryType(): void { $this->assertEquals(NewsCategory::class, $this->obj->categoryType()); } - /** - * @return void - */ /* public function testSave() { $this->obj->save(); } */ - - /** - * @return void - */ - public function testSaveGeneratesSlug() + public function testSaveGeneratesSlug(): void { $this->assertEquals('', $this->obj['slug']); $this->obj->setData([ @@ -238,10 +193,7 @@ public function testSaveGeneratesSlug() $this->assertEquals('en/news/foo', (string)$this->obj['slug']); } - /** - * @return void - */ - public function testUpdateGeneratesSlug() + public function testUpdateGeneratesSlug(): void { $this->assertEquals('', $this->obj['slug']); $this->obj->setData([ diff --git a/packages/cms/tests/Charcoal/Cms/Route/AbstractRouteTestCase.php b/packages/cms/tests/Charcoal/Cms/Route/AbstractRouteTestCase.php index 9c541127b..09a7b4e1a 100644 --- a/packages/cms/tests/Charcoal/Cms/Route/AbstractRouteTestCase.php +++ b/packages/cms/tests/Charcoal/Cms/Route/AbstractRouteTestCase.php @@ -26,8 +26,6 @@ abstract class AbstractRouteTestCase extends AbstractTestCase /** * Set up the test. - * - * @return void */ protected function setUp(): void { @@ -86,10 +84,8 @@ protected function createHttpResponse() /** * Assertion when given an empty path. - * - * @return void */ - public function testPathResolvableOnEmptyPath() + public function testPathResolvableOnEmptyPath(): void { $container = $this->getContainer(); $router = $this->createRouter([ diff --git a/packages/cms/tests/Charcoal/Cms/Route/EventRouteTest.php b/packages/cms/tests/Charcoal/Cms/Route/EventRouteTest.php index dbf5924d3..45015660a 100644 --- a/packages/cms/tests/Charcoal/Cms/Route/EventRouteTest.php +++ b/packages/cms/tests/Charcoal/Cms/Route/EventRouteTest.php @@ -16,10 +16,8 @@ class EventRouteTest extends AbstractRouteTestCase /** * Asserts that `EventRoute::__invoke()` method returns an HTTP Response object * with a 404 status code if the path does not resolve to any routable model. - * - * @return void */ - public function testInvokeOnNonexistentModel() + public function testInvokeOnNonexistentModel(): void { $container = $this->getContainer(); $router = $this->createRouter([ @@ -41,10 +39,8 @@ public function testInvokeOnNonexistentModel() * if the resolved model has a nonexistent template controller. * * The "template/factory" service throws an Exception when a model's template controller can not be found. - * - * @return void */ - public function testInvokeOnExistingModelWithMissingTemplateController() + public function testInvokeOnExistingModelWithMissingTemplateController(): void { $this->insertMockRoutableContextObjects([ 'templateIdent' => 'nonexistent', @@ -62,16 +58,14 @@ public function testInvokeOnExistingModelWithMissingTemplateController() $response = $this->createHttpResponse(); $this->expectException(InvalidArgumentException::class); - $response = $router($container, $request, $response); + $router($container, $request, $response); } /** * Asserts that `EventRoute::__invoke()` method returns an HTTP Response object * with a 500 status code if the resolved model does not have a template identifier. - * - * @return void */ - public function testInvokeOnExistingModelWithoutTemplateIdent() + public function testInvokeOnExistingModelWithoutTemplateIdent(): void { $this->insertMockRoutableContextObjects([ 'templateIdent' => '', @@ -95,10 +89,8 @@ public function testInvokeOnExistingModelWithoutTemplateIdent() /** * Asserts that `EventRoute::__invoke()` method returns an HTTP Response object * with a 500 status code if the resolved model does not have a rendered template view. - * - * @return void */ - public function testInvokeOnExistingModelWithBadTemplateIdent() + public function testInvokeOnExistingModelWithBadTemplateIdent(): void { $this->insertMockRoutableContextObjects([ 'templateIdent' => 'charcoal/tests/cms/mock/broken', @@ -122,10 +114,8 @@ public function testInvokeOnExistingModelWithBadTemplateIdent() /** * Asserts that `EventRoute::__invoke()` method returns an HTTP Response object * with a 2XX status code if the path does resolve to a specific routable model. - * - * @return void */ - public function testInvokeOnExistingModelWithTemplateIdent() + public function testInvokeOnExistingModelWithTemplateIdent(): void { $this->insertMockRoutableContextObjects([ 'templateIdent' => 'charcoal/tests/cms/mock/event', @@ -151,9 +141,8 @@ public function testInvokeOnExistingModelWithTemplateIdent() * Create the dynamic route to test. * * @param array $data The dynamic route dependencies. - * @return EventRoute */ - protected function createRouter(array $data = []) + protected function createRouter(array $data = []): \Charcoal\Cms\Route\EventRoute { return new EventRoute($data + [ 'config' => [], diff --git a/packages/cms/tests/Charcoal/Cms/Route/GenericRouteTest.php b/packages/cms/tests/Charcoal/Cms/Route/GenericRouteTest.php index 160ece978..3359a4a14 100644 --- a/packages/cms/tests/Charcoal/Cms/Route/GenericRouteTest.php +++ b/packages/cms/tests/Charcoal/Cms/Route/GenericRouteTest.php @@ -21,10 +21,9 @@ class GenericRouteTest extends SectionRouteTest * with a 500 status code if the resolved model does not have a template identifier. * * The route's config throws an Exception when a model's template controller is invalid. - * - * @return void */ - public function testInvokeOnExistingModelWithoutTemplateIdent() + #[\Override] + public function testInvokeOnExistingModelWithoutTemplateIdent(): void { $this->insertMockRoutableContextObjects([ 'templateIdent' => '', @@ -43,16 +42,14 @@ public function testInvokeOnExistingModelWithoutTemplateIdent() $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Route view controller must be a string.'); - $response = $router($container, $request, $response); + $router($container, $request, $response); } /** * Asserts that `SectionRoute::__invoke()` method returns an HTTP Response object * with a 404 status code if the path does resolve but the routable model does not. - * - * @return void */ - public function testInvokeOnExistingObjectRouteWithMissingModel() + public function testInvokeOnExistingObjectRouteWithMissingModel(): void { $container = $this->getContainer(); @@ -85,7 +82,8 @@ public function testInvokeOnExistingObjectRouteWithMissingModel() * @param array $data The dynamic route dependencies. * @return GenericRoute */ - protected function createRouter(array $data = []) + #[\Override] + protected function createRouter(array $data = []): \Charcoal\Cms\Route\SectionRoute { return new GenericRoute($data + [ 'config' => [], diff --git a/packages/cms/tests/Charcoal/Cms/Route/NewsRouteTest.php b/packages/cms/tests/Charcoal/Cms/Route/NewsRouteTest.php index 3448d8f52..31675ca68 100644 --- a/packages/cms/tests/Charcoal/Cms/Route/NewsRouteTest.php +++ b/packages/cms/tests/Charcoal/Cms/Route/NewsRouteTest.php @@ -16,10 +16,8 @@ class NewsRouteTest extends AbstractRouteTestCase /** * Asserts that `NewsRoute::__invoke()` method returns an HTTP Response object * with a 404 status code if the path does not resolve to any routable model. - * - * @return void */ - public function testInvokeOnNonexistentModel() + public function testInvokeOnNonexistentModel(): void { $container = $this->getContainer(); $router = $this->createRouter([ @@ -41,10 +39,8 @@ public function testInvokeOnNonexistentModel() * if the resolved model has a nonexistent template controller. * * The "template/factory" service throws an Exception when a model's template controller can not be found. - * - * @return void */ - public function testInvokeOnExistingModelWithMissingTemplateController() + public function testInvokeOnExistingModelWithMissingTemplateController(): void { $this->insertMockRoutableContextObjects([ 'templateIdent' => 'nonexistent', @@ -62,16 +58,14 @@ public function testInvokeOnExistingModelWithMissingTemplateController() $response = $this->createHttpResponse(); $this->expectException(InvalidArgumentException::class); - $response = $router($container, $request, $response); + $router($container, $request, $response); } /** * Asserts that `NewsRoute::__invoke()` method returns an HTTP Response object * with a 500 status code if the resolved model does not have a template identifier. - * - * @return void */ - public function testInvokeOnExistingModelWithoutTemplateIdent() + public function testInvokeOnExistingModelWithoutTemplateIdent(): void { $this->insertMockRoutableContextObjects([ 'templateIdent' => '', @@ -95,10 +89,8 @@ public function testInvokeOnExistingModelWithoutTemplateIdent() /** * Asserts that `NewsRoute::__invoke()` method returns an HTTP Response object * with a 500 status code if the resolved model does not have a rendered template view. - * - * @return void */ - public function testInvokeOnExistingModelWithBadTemplateIdent() + public function testInvokeOnExistingModelWithBadTemplateIdent(): void { $this->insertMockRoutableContextObjects([ 'templateIdent' => 'charcoal/tests/cms/mock/broken', @@ -122,10 +114,8 @@ public function testInvokeOnExistingModelWithBadTemplateIdent() /** * Asserts that `NewsRoute::__invoke()` method returns an HTTP Response object * with a 2XX status code if the path does resolve to a specific routable model. - * - * @return void */ - public function testInvokeOnExistingModelWithTemplateIdent() + public function testInvokeOnExistingModelWithTemplateIdent(): void { $this->insertMockRoutableContextObjects([ 'templateIdent' => 'charcoal/tests/cms/mock/news', @@ -151,9 +141,8 @@ public function testInvokeOnExistingModelWithTemplateIdent() * Create the dynamic route to test. * * @param array $data The dynamic route dependencies. - * @return NewsRoute */ - protected function createRouter(array $data = []) + protected function createRouter(array $data = []): \Charcoal\Cms\Route\NewsRoute { return new NewsRoute($data + [ 'config' => [], diff --git a/packages/cms/tests/Charcoal/Cms/Route/SectionRouteTest.php b/packages/cms/tests/Charcoal/Cms/Route/SectionRouteTest.php index 1c79b174a..513cfa97a 100644 --- a/packages/cms/tests/Charcoal/Cms/Route/SectionRouteTest.php +++ b/packages/cms/tests/Charcoal/Cms/Route/SectionRouteTest.php @@ -16,10 +16,8 @@ class SectionRouteTest extends AbstractRouteTestCase /** * Asserts that `SectionRoute::__invoke()` method returns an HTTP Response object * with a 404 status code if the path does not resolve to any routable model. - * - * @return void */ - public function testInvokeOnNonexistentModel() + public function testInvokeOnNonexistentModel(): void { $container = $this->getContainer(); $router = $this->createRouter([ @@ -43,10 +41,8 @@ public function testInvokeOnNonexistentModel() * * The "config.view.defaultController" option ensures the "template/factory" service * does not throw an Exception when a model's template controller can not be found. - * - * @return void */ - public function testInvokeOnExistingModelWithMissingTemplateController() + public function testInvokeOnExistingModelWithMissingTemplateController(): void { $this->insertMockRoutableContextObjects([ 'templateIdent' => 'nonexistent', @@ -70,10 +66,8 @@ public function testInvokeOnExistingModelWithMissingTemplateController() /** * Asserts that `SectionRoute::__invoke()` method returns an HTTP Response object * with a 500 status code if the resolved model does not have a template identifier. - * - * @return void */ - public function testInvokeOnExistingModelWithoutTemplateIdent() + public function testInvokeOnExistingModelWithoutTemplateIdent(): void { $this->insertMockRoutableContextObjects([ 'templateIdent' => '', @@ -97,10 +91,8 @@ public function testInvokeOnExistingModelWithoutTemplateIdent() /** * Asserts that `SectionRoute::__invoke()` method returns an HTTP Response object * with a 500 status code if the resolved model does not have a rendered template view. - * - * @return void */ - public function testInvokeOnExistingModelWithBadTemplateIdent() + public function testInvokeOnExistingModelWithBadTemplateIdent(): void { $this->insertMockRoutableContextObjects([ 'templateIdent' => 'charcoal/tests/cms/mock/broken', @@ -124,10 +116,8 @@ public function testInvokeOnExistingModelWithBadTemplateIdent() /** * Asserts that `SectionRoute::__invoke()` method returns an HTTP Response object * with a 2XX status code if the path does resolve to a specific routable model. - * - * @return void */ - public function testInvokeOnExistingModelWithTemplateIdent() + public function testInvokeOnExistingModelWithTemplateIdent(): void { $this->insertMockRoutableContextObjects([ 'templateIdent' => 'charcoal/tests/cms/mock/home', @@ -153,9 +143,8 @@ public function testInvokeOnExistingModelWithTemplateIdent() * Create the dynamic route to test. * * @param array $data The dynamic route dependencies. - * @return SectionRoute */ - protected function createRouter(array $data = []) + protected function createRouter(array $data = []): \Charcoal\Cms\Route\SectionRoute { return new SectionRoute($data + [ 'config' => [], diff --git a/packages/cms/tests/Charcoal/Cms/SectionTest.php b/packages/cms/tests/Charcoal/Cms/SectionTest.php index 24dfe7aca..e9043194c 100644 --- a/packages/cms/tests/Charcoal/Cms/SectionTest.php +++ b/packages/cms/tests/Charcoal/Cms/SectionTest.php @@ -19,15 +19,11 @@ class SectionTest extends AbstractTestCase /** * Tested Class. - * - * @var Section */ - private $obj; + private \Charcoal\Cms\Section|array $obj; /** * Set up the test. - * - * @return void */ protected function setUp():void { @@ -43,10 +39,7 @@ protected function setUp():void $this->obj = new Section($dependencies); } - /** - * @return void - */ - public function testSetData() + public function testSetData(): void { $obj = $this->obj; $ret = $obj->setData([ @@ -71,10 +64,7 @@ public function testSetData() $this->assertEquals([ 'x' => 'y' ], $obj->templateOptions()); } - /** - * @return void - */ - public function testSetSectionType() + public function testSetSectionType(): void { $ret = $this->obj->setSectionType(Section::TYPE_EMPTY); $this->assertSame($ret, $this->obj); @@ -84,10 +74,7 @@ public function testSetSectionType() $this->obj->setSectionType(false); } - /** - * @return void - */ - public function testSetTitle() + public function testSetTitle(): void { $this->assertEquals('', (string)$this->obj->title()); $ret = $this->obj->setTitle('Foo bar'); @@ -101,10 +88,7 @@ public function testSetTitle() $this->assertEquals('Hello', (string)$this->obj['title']); } - /** - * @return void - */ - public function testSetSubtitle() + public function testSetSubtitle(): void { $this->assertEquals('', (string)$this->obj->subtitle()); $ret = $this->obj->setSubtitle('Bar foo'); @@ -118,10 +102,7 @@ public function testSetSubtitle() $this->assertEquals('foo', (string)$this->obj['subtitle']); } - /** - * @return void - */ - public function testSetContent() + public function testSetContent(): void { $this->assertEquals('', (string)$this->obj->content()); $ret = $this->obj->setContent('Bar foo'); @@ -135,10 +116,7 @@ public function testSetContent() $this->assertEquals('foo', (string)$this->obj['content']); } - /** - * @return void - */ - public function testSetImage() + public function testSetImage(): void { $this->assertEquals('', (string)$this->obj->image()); $ret = $this->obj->setImage('foo.png'); @@ -152,10 +130,7 @@ public function testSetImage() $this->assertEquals('foo.webp', $this->obj['image']); } - /** - * @return void - */ - public function testMetaTitleDefaultsToTitle() + public function testMetaTitleDefaultsToTitle(): void { $this->assertEquals('', (string)$this->obj->metaTitle()); @@ -167,10 +142,7 @@ public function testMetaTitleDefaultsToTitle() $this->assertEquals('Barfoo', (string)$this->obj->metaTitle()); } - /** - * @return void - */ - public function testMetaDescriptionDefaultsToDescription() + public function testMetaDescriptionDefaultsToDescription(): void { $this->assertEquals('', (string)$this->obj->metaDescription()); @@ -182,10 +154,7 @@ public function testMetaDescriptionDefaultsToDescription() $this->assertEquals('Barfoo', (string)$this->obj->metaDescription()); } - /** - * @return void - */ - public function testMetaImageDefaultsToImage() + public function testMetaImageDefaultsToImage(): void { $this->assertEquals('', (string)$this->obj->metaImage()); @@ -197,10 +166,7 @@ public function testMetaImageDefaultsToImage() $this->assertEquals('Bar.jpg', (string)$this->obj->metaImage()); } - /** - * @return void - */ - public function testSaveGeneratesSlug() + public function testSaveGeneratesSlug(): void { $this->assertEquals('', $this->obj['slug']); $this->obj->setData([ @@ -211,10 +177,7 @@ public function testSaveGeneratesSlug() $this->assertEquals('en/foo', (string)$this->obj['slug']); } - /** - * @return void - */ - public function testUpdateGeneratesSlug() + public function testUpdateGeneratesSlug(): void { $this->assertEquals('', $this->obj['slug']); $this->obj->setData([ diff --git a/packages/cms/tests/Charcoal/Cms/TagTest.php b/packages/cms/tests/Charcoal/Cms/TagTest.php index 7b57b57b8..7fb77c686 100644 --- a/packages/cms/tests/Charcoal/Cms/TagTest.php +++ b/packages/cms/tests/Charcoal/Cms/TagTest.php @@ -17,15 +17,11 @@ class TagTest extends AbstractTestCase /** * Tested Class. - * - * @var Tag */ - private $obj; + private \Charcoal\Cms\Tag $obj; /** * Set up the test. - * - * @return void */ protected function setUp(): void { @@ -34,10 +30,7 @@ protected function setUp(): void $this->obj = new Tag($dependencies); } - /** - * @return void - */ - public function testSetData() + public function testSetData(): void { $ret = $this->obj->setData([ 'name' => 'Foo?', @@ -54,34 +47,28 @@ public function testSetData() $this->assertEquals(42, $this->obj->searchWeight()); } - /** - * @return void - */ - public function testSetName() + public function testSetName(): void { $ret = $this->obj->setName('Foo?'); $this->assertSame($ret, $this->obj); $this->assertEquals('Foo?', $this->obj->name()); } - /** - * @return void - */ - public function testSetColor() + public function testSetColor(): void { $ret = $this->obj->setColor('Bar'); $this->assertSame($ret, $this->obj); $this->assertEquals('Bar', $this->obj->color()); } - public function testSetVariations() + public function testSetVariations(): void { $ret = $this->obj->setVariations('foo,bar,baz'); $this->assertSame($ret, $this->obj); $this->assertEquals('foo,bar,baz', $this->obj->variations()); } - public function testSetSearchWeight() + public function testSetSearchWeight(): void { $ret = $this->obj->setSearchWeight(1984); $this->assertSame($ret, $this->obj); diff --git a/packages/cms/tests/Charcoal/Cms/TemplateableTraitTest.php b/packages/cms/tests/Charcoal/Cms/TemplateableTraitTest.php index a53c7aaad..7f0fd9289 100644 --- a/packages/cms/tests/Charcoal/Cms/TemplateableTraitTest.php +++ b/packages/cms/tests/Charcoal/Cms/TemplateableTraitTest.php @@ -29,8 +29,6 @@ class TemplateableTraitTest extends AbstractTestCase /** * Set up the test. - * - * @return void */ protected function setUp(): void { @@ -58,8 +56,6 @@ protected function setUp(): void * Tear down the test. * * Drop any existing SQL table. - * - * @return void */ protected function tearDown(): void { @@ -68,10 +64,8 @@ protected function tearDown(): void /** * Drop the SQL table. - * - * @return void */ - private function dropTable() + private function dropTable(): void { $container = $this->getContainer(); @@ -80,10 +74,8 @@ private function dropTable() /** * Retrieve the model's mock metadata. - * - * @return array */ - public function getModelMetadata() + public function getModelMetadata(): array { return [ 'properties' => [ @@ -124,30 +116,21 @@ public function getModelMetadata() ]; } - /** - * @return void - */ - public function testMissingPropertyDependency() + public function testMissingPropertyDependency(): void { $this->expectException(RuntimeException::class); $obj = new TemplateableModel($this->getModelDependencies()); $obj->templateOptionsStructure(); } - /** - * @return void - */ - public function testMissingInterfaceDependency() + public function testMissingInterfaceDependency(): void { $this->expectException(RuntimeException::class); $obj = $this->getMockForTrait(TemplateableTrait::class); $obj->templateOptionsStructure(); } - /** - * @return void - */ - public function testTemplateIdent() + public function testTemplateIdent(): void { $this->assertEmpty($this->obj->templateIdent()); @@ -155,10 +138,7 @@ public function testTemplateIdent() $this->assertEquals('foobar', $this->obj->templateIdent()); } - /** - * @return void - */ - public function testControllerIdent() + public function testControllerIdent(): void { $this->assertNull($this->obj->controllerIdent()); @@ -166,10 +146,7 @@ public function testControllerIdent() $this->assertEquals('foobar', $this->obj->controllerIdent()); } - /** - * @return void - */ - public function testTemplateOptions() + public function testTemplateOptions(): void { $this->assertIsArray($this->obj->templateOptions()); $this->assertEmpty($this->obj->templateOptions()); @@ -194,10 +171,7 @@ public function testTemplateOptions() $this->assertEquals($obj, $this->obj->templateOptions()); } - /** - * @return void - */ - public function testSavingTemplateOptions() + public function testSavingTemplateOptions(): void { $obj = $this->obj; $obj->setTemplateIdent('foo'); @@ -214,10 +188,7 @@ public function testSavingTemplateOptions() $this->assertTrue($result); } - /** - * @return void - */ - public function testTemplateOptionsStructure() + public function testTemplateOptionsStructure(): void { $templateData = [ 'foo' => 'Huxley' ]; $basePath = $this->getContainer()['config']['base_path']; diff --git a/packages/cms/tests/Charcoal/Property/TemplateOptionsPropertyTest.php b/packages/cms/tests/Charcoal/Property/TemplateOptionsPropertyTest.php index cc33465de..7154d21fd 100644 --- a/packages/cms/tests/Charcoal/Property/TemplateOptionsPropertyTest.php +++ b/packages/cms/tests/Charcoal/Property/TemplateOptionsPropertyTest.php @@ -26,8 +26,6 @@ class TemplateOptionsPropertyTest extends AbstractTestCase /** * Set up the test. - * - * @return void */ protected function setUp(): void { @@ -52,18 +50,12 @@ protected function setUp(): void $this->obj = new TemplateOptionsProperty($dependencies); } - /** - * @return void - */ - public function testType() + public function testType(): void { $this->assertEquals('template-options', $this->obj->type()); } - /** - * @return void - */ - public function testAddStructureInterface() + public function testAddStructureInterface(): void { $container = $this->getContainer(); $property = $container['property/factory']->create(TemplateProperty::class); @@ -76,10 +68,7 @@ public function testAddStructureInterface() $this->assertEquals([ 'charcoal/tests/cms/mock/generic' ], $interfaces); } - /** - * @return void - */ - public function testAddStructureInterfaceException() + public function testAddStructureInterfaceException(): void { $container = $this->getContainer(); $property = $container['property/factory']->create(TemplateProperty::class); diff --git a/packages/cms/tests/Charcoal/Property/TemplatePropertyTest.php b/packages/cms/tests/Charcoal/Property/TemplatePropertyTest.php index 79ed83b9e..7b45bbb3d 100644 --- a/packages/cms/tests/Charcoal/Property/TemplatePropertyTest.php +++ b/packages/cms/tests/Charcoal/Property/TemplatePropertyTest.php @@ -27,8 +27,6 @@ class TemplatePropertyTest extends AbstractTestCase /** * Set up the test. - * - * @return void */ protected function setUp(): void { @@ -43,26 +41,17 @@ protected function setUp(): void $this->obj = new TemplateProperty($dependencies); } - /** - * @return void - */ - public function testType() + public function testType(): void { $this->assertEquals('template', $this->obj->type()); } - /** - * @return void - */ - public function testSqlExtra() + public function testSqlExtra(): void { $this->assertEquals('', $this->obj->sqlExtra()); } - /** - * @return void - */ - public function testSqlType() + public function testSqlType(): void { $this->obj->setMultiple(false); $this->assertEquals('VARCHAR(255)', $this->obj->sqlType()); @@ -71,18 +60,12 @@ public function testSqlType() $this->assertEquals('TEXT', $this->obj->sqlType()); } - /** - * @return void - */ - public function testSqlPdoType() + public function testSqlPdoType(): void { $this->assertEquals(PDO::PARAM_STR, $this->obj->sqlPdoType()); } - /** - * @return void - */ - public function testChoices() + public function testChoices(): void { $container = $this->getContainer(); $templates = $container['config']['templates']; @@ -109,59 +92,40 @@ public function testChoices() $this->assertArrayHasKey('zyx', $this->obj->choices()); } - /** - * @return void - */ - public function testChoicesInvalidKey() + public function testChoicesInvalidKey(): void { $this->expectException(InvalidArgumentException::class); $this->obj->addChoice(3, 'boo'); } - /** - * @return void - */ - public function testChoicesInvalidString() + public function testChoicesInvalidString(): void { $this->expectException(InvalidArgumentException::class); $this->obj->addChoice('boo', 'boo'); } - /** - * @return void - */ - public function testChoicesInvalidBoolean() + public function testChoicesInvalidBoolean(): void { $this->expectException(InvalidArgumentException::class); $this->obj->addChoice('boo', true); } - /** - * @return void - */ - public function testChoicesInvalidArray() + public function testChoicesInvalidArray(): void { $this->expectException(InvalidArgumentException::class); $this->obj->addChoice('boo', [ 'foo' => 'boo' ]); } - /** - * @return void - */ - public function testChoicesInvalidType() + public function testChoicesInvalidType(): void { $this->expectException(InvalidArgumentException::class); $this->obj->addChoice('xyz', null); } - /** - * @return void - */ - public function testDisplayVal() + public function testDisplayVal(): void { $container = $this->getContainer(); $translator = $container['translator']; - $templates = $container['config']['templates']; $this->assertEquals('', $this->obj->displayVal(null)); $this->assertEquals('', $this->obj->displayVal('')); @@ -189,10 +153,7 @@ public function testDisplayVal() $this->assertEquals('Oofoof, Zabzab, Xuqxuq', $this->obj->displayVal('foo,baz,qux', [ 'lang' => 'fr' ])); } - /** - * @return void - */ - public function testToString() + public function testToString(): void { $this->assertEquals('', (string)$this->obj); diff --git a/packages/config/composer.json b/packages/config/composer.json index 1b5268893..92307928d 100644 --- a/packages/config/composer.json +++ b/packages/config/composer.json @@ -1,8 +1,15 @@ { "name": "charcoal/config", - "type": "library", "description": "Charcoal component for configuration data and object modeling", - "keywords": ["charcoal", "configuration", "json", "xml", "yml", "yaml", "ini"], + "keywords": [ + "charcoal", + "configuration", + "json", + "xml", + "yml", + "yaml", + "ini" + ], "homepage": "https://locomotivemtl.github.io/charcoal-config/", "license": "MIT", "authors": [ @@ -15,26 +22,19 @@ "homepage": "https://locomotive.ca" } ], - "extra": { - "branch-alias": { - "dev-main": "5.x-dev" - } - }, + "type": "library", "require": { - "php": "^7.4 || ^8.0", + "php": "^8.3", "ext-json": "*", "ext-spl": "*", "psr/container": "^1.0" }, "require-dev": { "symfony/yaml": "^3.0", - "phpunit/phpunit": "^9.5", + "phpunit/phpunit": "^12.5", "squizlabs/php_codesniffer": "^3.5", "php-coveralls/php-coveralls": "^2.2" }, - "suggest": { - "symfony/yaml": "To load and parse configuration files in yaml format." - }, "autoload": { "psr-4": { "Charcoal\\": "src/Charcoal/" @@ -45,8 +45,10 @@ "Charcoal\\Tests\\": "tests/Charcoal/" } }, - "replace": { - "locomotivemtl/charcoal-config": "*" + "extra": { + "branch-alias": { + "dev-main": "5.x-dev" + } }, "scripts": { "test": [ @@ -62,6 +64,12 @@ "phpcbf": "php vendor/bin/phpcbf -ps --colors src/ tests/", "phpunit": "php vendor/bin/phpunit --coverage-text" }, + "suggest": { + "symfony/yaml": "To load and parse configuration files in yaml format." + }, + "replace": { + "locomotivemtl/charcoal-config": "*" + }, "minimum-stability": "dev", "prefer-stable": true } diff --git a/packages/config/src/Charcoal/Config/AbstractConfig.php b/packages/config/src/Charcoal/Config/AbstractConfig.php index d9a33d9e8..b61320372 100644 --- a/packages/config/src/Charcoal/Config/AbstractConfig.php +++ b/packages/config/src/Charcoal/Config/AbstractConfig.php @@ -41,7 +41,7 @@ abstract class AbstractConfig extends AbstractEntity implements * @param EntityInterface[] $delegates An array of delegates (config) to set. * @throws InvalidArgumentException If $data is invalid. */ - final public function __construct($data = null, array $delegates = null) + final public function __construct($data = null, ?array $delegates = null) { // Always set the default chaining notation $this->setSeparator(self::DEFAULT_SEPARATOR); @@ -110,7 +110,7 @@ public function merge($data) * @see IteratorAggregate * @return ArrayIterator */ - public function getIterator() + public function getIterator(): ArrayIterator { return new ArrayIterator($this->data()); } @@ -131,7 +131,8 @@ public function getIterator() * @throws InvalidArgumentException If the $key is not a string or is a numeric value. * @return boolean TRUE if $key exists and has a value other than NULL, FALSE otherwise. */ - public function offsetExists($key) + #[\Override] + public function offsetExists($key): bool { if (is_numeric($key)) { throw new InvalidArgumentException( @@ -192,7 +193,8 @@ public function offsetExists($key) * @throws InvalidArgumentException If the $key is not a string or is a numeric value. * @return mixed Value of the requested $key on success, NULL if the $key is not set. */ - public function offsetGet($key) + #[\Override] + public function offsetGet($key): mixed { if (is_numeric($key)) { throw new InvalidArgumentException( @@ -228,13 +230,8 @@ public function offsetGet($key) if ($this->mutatorCache[$key]) { return $this->{$key}(); } - // -- END DEPRECATED - - if (isset($this->{$key})) { - return $this->{$key}; - } - return $this->getInDelegates($key); + return $this->{$key} ?? $this->getInDelegates($key); } /** @@ -249,9 +246,9 @@ public function offsetGet($key) * @param string $key The data key to assign $value to. * @param mixed $value The data value to assign to $key. * @throws InvalidArgumentException If the $key is not a string or is a numeric value. - * @return void */ - public function offsetSet($key, $value) + #[\Override] + public function offsetSet($key, $value): void { if (is_numeric($key)) { throw new InvalidArgumentException( @@ -298,9 +295,8 @@ public function offsetSet($key, $value) * @param string $key The data key to assign or merge $value to. * @param mixed $value The data value to assign to or merge with $key. * @throws InvalidArgumentException If the $key is not a string or is a numeric value. - * @return void */ - public function offsetReplace($key, $value) + public function offsetReplace($key, $value): void { if (is_numeric($key)) { throw new InvalidArgumentException( diff --git a/packages/config/src/Charcoal/Config/AbstractEntity.php b/packages/config/src/Charcoal/Config/AbstractEntity.php index d14fab59e..b5c8b4ca4 100644 --- a/packages/config/src/Charcoal/Config/AbstractEntity.php +++ b/packages/config/src/Charcoal/Config/AbstractEntity.php @@ -2,6 +2,7 @@ namespace Charcoal\Config; +use AllowDynamicProperties; use ArrayAccess; use InvalidArgumentException; @@ -17,6 +18,7 @@ * - A key-value pair is internally passed to a (non-private / non-static) setter method (if present) * or assigned to a (non-private / non-static) property (declared or not) and tracks affected keys. */ +#[AllowDynamicProperties] abstract class AbstractEntity implements EntityInterface { /** @@ -58,7 +60,7 @@ public function keys() * @param string[] $keys Optional. Extracts only the requested data. * @return array Key-value array of data, excluding pairs with NULL values. */ - public function data(array $keys = null) + public function data(?array $keys = null) { if ($keys === null) { $keys = $this->keys(); @@ -66,7 +68,7 @@ public function data(array $keys = null) $data = []; foreach ($keys as $key) { - if (strtolower($key) === 'data') { + if (strtolower((string) $key) === 'data') { /** @internal Edge Case: Avoid recursive call */ continue; } @@ -88,7 +90,7 @@ public function data(array $keys = null) public function setData(array $data) { foreach ($data as $key => $value) { - if (strtolower($key) === 'data') { + if (strtolower((string) $key) === 'data') { /** @internal Edge Case: Avoid recursive call */ continue; } @@ -150,7 +152,7 @@ public function set($key, $value) * @throws InvalidArgumentException If the $key is not a string or is a numeric value. * @return boolean TRUE if $key exists and has a value other than NULL, FALSE otherwise. */ - public function offsetExists($key) + public function offsetExists($key): bool { if (is_numeric($key)) { throw new InvalidArgumentException( @@ -183,12 +185,7 @@ public function offsetExists($key) return ($this->{$key}() !== null); } // -- END DEPRECATED - - if (isset($this->{$key})) { - return true; - } - - return false; + return isset($this->{$key}); } /** @@ -205,7 +202,7 @@ public function offsetExists($key) * @throws InvalidArgumentException If the $key is not a string or is a numeric value. * @return mixed Value of the requested $key on success, NULL if the $key is not set. */ - public function offsetGet($key) + public function offsetGet($key): mixed { if (is_numeric($key)) { throw new InvalidArgumentException( @@ -237,13 +234,8 @@ public function offsetGet($key) if ($this->mutatorCache[$key]) { return $this->{$key}(); } - // -- END DEPRECATED - - if (isset($this->{$key})) { - return $this->{$key}; - } - return null; + return $this->{$key} ?? null; } /** @@ -259,9 +251,8 @@ public function offsetGet($key) * @param string $key The data key to assign $value to. * @param mixed $value The data value to assign to $key. * @throws InvalidArgumentException If the $key is not a string or is a numeric value. - * @return void */ - public function offsetSet($key, $value) + public function offsetSet($key, $value): void { if (is_numeric($key)) { throw new InvalidArgumentException( @@ -301,9 +292,8 @@ public function offsetSet($key, $value) * @uses self::offsetSet() * @param string $key The data key to remove. * @throws InvalidArgumentException If the $key is not a string or is a numeric value. - * @return void */ - public function offsetUnset($key) + public function offsetUnset($key): void { if (is_numeric($key)) { throw new InvalidArgumentException( @@ -328,7 +318,7 @@ public function offsetUnset($key) * @see \JsonSerializable * @return array Key-value array of data. */ - public function jsonSerialize() + public function jsonSerialize(): mixed { return $this->data(); } @@ -349,9 +339,8 @@ public function serialize() * * @see \Serializable * @param string $data The serialized data to extract. - * @return void */ - public function unserialize($data) + public function unserialize($data): void { $data = unserialize($data); $this->setData($data); @@ -371,8 +360,8 @@ final protected function camelize($value) return static::$camelCache[$key]; } - if (strpos($value, '_') !== false) { - $value = implode('', array_map('ucfirst', explode('_', $value))); + if (str_contains($value, '_')) { + $value = implode('', array_map(ucfirst(...), explode('_', $value))); } static::$camelCache[$key] = lcfirst($value); diff --git a/packages/config/src/Charcoal/Config/ConfigInterface.php b/packages/config/src/Charcoal/Config/ConfigInterface.php index 780dcd384..8d1d19dba 100644 --- a/packages/config/src/Charcoal/Config/ConfigInterface.php +++ b/packages/config/src/Charcoal/Config/ConfigInterface.php @@ -1,5 +1,7 @@ delegates as $delegate) { if (isset($delegate[$key])) { diff --git a/packages/config/src/Charcoal/Config/EntityInterface.php b/packages/config/src/Charcoal/Config/EntityInterface.php index b64045c0a..66ec2161c 100644 --- a/packages/config/src/Charcoal/Config/EntityInterface.php +++ b/packages/config/src/Charcoal/Config/EntityInterface.php @@ -1,5 +1,7 @@ getMessage()); - throw new UnexpectedValueException($message, 0, $e); - } catch (Throwable $e) { + } catch (Exception|Throwable $e) { $message = sprintf('PHP file "%s" could not be parsed: %s', $path, $e->getMessage()); throw new UnexpectedValueException($message, 0, $e); } - if (is_array($data) || ($data instanceof Traversable)) { + if (is_iterable($data)) { return $data; } @@ -156,9 +153,9 @@ private function loadPhpFile($path) * @return array An array on success. * If the file is parsed as any other type, an empty array is returned. */ - private function loadYamlFile($path) + private function loadYamlFile(string $path): array { - if (!class_exists('Symfony\Component\Yaml\Parser')) { + if (!class_exists(\Symfony\Component\Yaml\Parser::class)) { throw new LogicException('YAML format requires the Symfony YAML component'); } diff --git a/packages/config/src/Charcoal/Config/GenericConfig.php b/packages/config/src/Charcoal/Config/GenericConfig.php index 6be9b26eb..cb9a17927 100644 --- a/packages/config/src/Charcoal/Config/GenericConfig.php +++ b/packages/config/src/Charcoal/Config/GenericConfig.php @@ -1,5 +1,7 @@ separator, $key); diff --git a/packages/config/tests/Charcoal/AbstractTestCase.php b/packages/config/tests/Charcoal/AbstractTestCase.php index 3fdbb598d..a382decca 100644 --- a/packages/config/tests/Charcoal/AbstractTestCase.php +++ b/packages/config/tests/Charcoal/AbstractTestCase.php @@ -1,5 +1,7 @@ assertCount(count($expected), $haystack, $message); $this->assertEquals($expected, $haystack, $message); @@ -27,9 +26,8 @@ public function assertArrayEquals(array $expected, array $haystack, $message = ' * @param array $expected The expected haystack. * @param array $haystack The actual haystack. * @param string $message The error to report. - * @return void */ - public function assertArrayContains(array $expected, array $haystack, $message = '') + public function assertArrayContains(array $expected, array $haystack, $message = ''): void { foreach ($expected as $item) { $this->assertContains($item, $haystack, $message); @@ -42,9 +40,8 @@ public function assertArrayContains(array $expected, array $haystack, $message = * @param array $expected The expected haystack. * @param array $haystack The actual haystack. * @param string $message The error to report. - * @return void */ - public function assertArrayHasKeys(array $expected, array $haystack, $message = '') + public function assertArrayHasKeys(array $expected, array $haystack, $message = ''): void { foreach ($expected as $item) { $this->assertArrayHasKey($item, $haystack, $message); @@ -58,14 +55,13 @@ public function assertArrayHasKeys(array $expected, array $haystack, $message = * @param array $haystack The actual haystack. * @param boolean $strict Whether to check for object identity. * @param string $message The error to report. - * @return void */ public function assertArraySubsets( array $expected, array $haystack, $strict = false, $message = '' - ) { + ): void { foreach ($expected as $key => $val) { $this->assertArraySubset([ $key => $val ], $haystack, $strict, $message); } diff --git a/packages/config/tests/Charcoal/Config/Config/AbstractConfigTestCase.php b/packages/config/tests/Charcoal/Config/Config/AbstractConfigTestCase.php index 427c6edf8..c16512695 100644 --- a/packages/config/tests/Charcoal/Config/Config/AbstractConfigTestCase.php +++ b/packages/config/tests/Charcoal/Config/Config/AbstractConfigTestCase.php @@ -22,7 +22,7 @@ abstract class AbstractConfigTestCase extends AbstractTestCase * @param array $delegates Delegates to pre-populate the object. * @return MacroConfig */ - public function createConfig($data = null, array $delegates = null) + public function createConfig($data = null, ?array $delegates = null) { return new MacroConfig($data, $delegates); } diff --git a/packages/config/tests/Charcoal/Config/Config/ConfigArrayAccessTest.php b/packages/config/tests/Charcoal/Config/Config/ConfigArrayAccessTest.php index 1b957e0ac..7a72d9325 100644 --- a/packages/config/tests/Charcoal/Config/Config/ConfigArrayAccessTest.php +++ b/packages/config/tests/Charcoal/Config/Config/ConfigArrayAccessTest.php @@ -12,9 +12,14 @@ /** * Test ArrayAccess implementation in AbstractConfig - * - * @coversDefaultClass \Charcoal\Config\AbstractConfig */ +#[\PHPUnit\Framework\Attributes\CoversClass(\Charcoal\Config\AbstractConfig::class)] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractConfig::class, 'offsetExists()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractConfig::class, 'offsetGet()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractConfig::class, 'offsetSet()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractConfig::class, 'offsetUnset()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Tests\Config\Mock\MacroConfig::class, 'foo()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Tests\Config\Mock\MacroConfig::class, 'setFoo()')] class ConfigArrayAccessTest extends AbstractConfigTestCase { use ArrayAccessTestTrait; @@ -26,8 +31,6 @@ class ConfigArrayAccessTest extends AbstractConfigTestCase /** * Create a concrete MacroConfig instance. - * - * @return void */ protected function setUp(): void { @@ -41,9 +44,9 @@ protected function setUp(): void /** * Asserts that the object implements ArrayAccess. * - * @coversNothing * @return MacroConfig */ + #[\PHPUnit\Framework\Attributes\CoversNothing] public function testArrayAccess() { $this->assertInstanceOf(ArrayAccess::class, $this->cfg); @@ -54,17 +57,12 @@ public function testArrayAccess() // Test ArrayAccess on non-private properties // ========================================================================= - - /** - * @covers ::offsetExists() - * @return void - */ - public function testOffsetExists() + public function testOffsetExists(): void { $cfg = $this->cfg; // MacroConfig::$name - $this->assertObjectHasAttribute('name', $cfg); + $this->assertTrue(property_exists($cfg, 'name')); $this->assertTrue(isset($cfg['name'])); // MacroConfig::foo() @@ -74,11 +72,7 @@ public function testOffsetExists() $this->assertTrue(isset($cfg['erd'])); } - /** - * @covers ::offsetGet() - * @return void - */ - public function testOffsetGet() + public function testOffsetGet(): void { $cfg = $this->cfg; @@ -92,29 +86,21 @@ public function testOffsetGet() $this->assertEquals(true, $cfg['erd']); } - /** - * @covers ::offsetSet() - * @return void - */ - public function testOffsetSet() + public function testOffsetSet(): void { $cfg = $this->cfg; $cfg['baz'] = 'waldo'; - $this->assertObjectHasAttribute('baz', $cfg); + $this->assertTrue(property_exists($cfg, 'baz')); $this->assertEquals('waldo', $cfg['baz']); } - /** - * @covers ::offsetUnset() - * @return void - */ - public function testOffsetUnset() + public function testOffsetUnset(): void { $cfg = $this->cfg; unset($cfg['name']); - $this->assertObjectHasAttribute('name', $cfg); + $this->assertTrue(property_exists($cfg, 'name')); $this->assertNull($cfg['name']); } @@ -122,38 +108,22 @@ public function testOffsetUnset() // Test ArrayAccess on encapsulated properties // ========================================================================= - - /** - * @covers \Charcoal\Tests\Config\Mock\MacroConfig::foo() - * @covers ::offsetExists() - * @return void - */ - public function testOffsetExistsOnEncapsulatedMethod() + public function testOffsetExistsOnEncapsulatedMethod(): void { $cfg = $this->cfg; - $this->assertObjectHasAttribute('foo', $cfg); + $this->assertTrue(property_exists($cfg, 'foo')); $this->assertTrue(isset($cfg['foo'])); } - /** - * @covers \Charcoal\Tests\Config\Mock\MacroConfig::foo() - * @covers ::offsetGet() - * @return void - */ - public function testOffsetGetOnEncapsulatedMethod() + public function testOffsetGetOnEncapsulatedMethod(): void { $cfg = $this->cfg; $this->assertEquals('foo is 20', $cfg['foo']); } - /** - * @covers \Charcoal\Tests\Config\Mock\MacroConfig::setFoo() - * @covers ::offsetSet() - * @return void - */ - public function testOffsetSetOnEncapsulatedMethod() + public function testOffsetSetOnEncapsulatedMethod(): void { $cfg = $this->cfg; @@ -161,17 +131,12 @@ public function testOffsetSetOnEncapsulatedMethod() $this->assertEquals('foo is 42', $cfg['foo']); } - /** - * @covers \Charcoal\Tests\Config\Mock\MacroConfig::setFoo() - * @covers ::offsetUnset() - * @return void - */ - public function testOffsetUnsetOnEncapsulatedMethod() + public function testOffsetUnsetOnEncapsulatedMethod(): void { $cfg = $this->cfg; unset($cfg['foo']); - $this->assertObjectHasAttribute('foo', $cfg); + $this->assertTrue(property_exists($cfg, 'foo')); $this->assertEquals('foo is 10', $cfg['foo']); } } diff --git a/packages/config/tests/Charcoal/Config/Config/ConfigArrayMergeTest.php b/packages/config/tests/Charcoal/Config/Config/ConfigArrayMergeTest.php index f286d35e1..d5eabb354 100644 --- a/packages/config/tests/Charcoal/Config/Config/ConfigArrayMergeTest.php +++ b/packages/config/tests/Charcoal/Config/Config/ConfigArrayMergeTest.php @@ -14,9 +14,10 @@ /** * Test data merging in AbstractConfig - * - * @coversDefaultClass \Charcoal\Config\AbstractConfig */ +#[\PHPUnit\Framework\Attributes\CoversClass(\Charcoal\Config\AbstractConfig::class)] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractConfig::class, 'offsetReplace()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractConfig::class, 'merge()')] class ConfigArrayMergeTest extends AbstractConfigTestCase { use AssertionsTrait; @@ -28,8 +29,6 @@ class ConfigArrayMergeTest extends AbstractConfigTestCase /** * Create a concrete GenericConfig instance. - * - * @return void */ protected function setUp(): void { @@ -41,23 +40,18 @@ protected function setUp(): void * * @param mixed $data Data to pre-populate the object. * @param array $delegates Delegates to pre-populate the object. - * @return GenericConfig */ - public function createConfig($data = null, array $delegates = null) + #[\Override] + public function createConfig($data = null, ?array $delegates = null): \Charcoal\Config\GenericConfig { return new GenericConfig($data, $delegates); } // ========================================================================= - /** * Test {@see AbstractEntity::merge()} with array. - * - * @covers ::offsetReplace() - * @covers ::merge() - * @return void */ - public function testMergeDataWithArray() + public function testMergeDataWithArray(): void { $cfg = $this->cfg; @@ -74,12 +68,8 @@ public function testMergeDataWithArray() /** * Test {@see AbstractEntity::merge()} with another Config instance. - * - * @covers ::offsetReplace() - * @covers ::merge() - * @return void */ - public function testMergeDataWithConfigInstance() + public function testMergeDataWithConfigInstance(): void { $cfg = $this->cfg; @@ -95,10 +85,8 @@ public function testMergeDataWithConfigInstance() /** * Gets the intiial Config data. - * - * @return array */ - public function getInitialConfigData() + public function getInitialConfigData(): array { return [ 'name' => 'vendor/my-cool-app', @@ -116,10 +104,8 @@ public function getInitialConfigData() /** * Gets the mutations for the Config. - * - * @return array */ - public function getMutatedConfigData() + public function getMutatedConfigData(): array { return [ 'name' => 'vendor/my-awesome-app', @@ -138,10 +124,8 @@ public function getMutatedConfigData() /** * Gets the expected Config data. - * - * @return array */ - public function getExpectedConfigData() + public function getExpectedConfigData(): array { return [ 'name' => 'vendor/my-awesome-app', @@ -159,15 +143,11 @@ public function getExpectedConfigData() } // ========================================================================= - /** * Asserts that the container assigns a value to the endpoint * {@see SeparatorAwareTrait::setWithSeparator() of the keypath}. - * - * @covers ::offsetReplace() - * @return void */ - public function testOffsetMergeOnEndKeyPath() + public function testOffsetMergeOnEndKeyPath(): void { $cfg = $this->cfg; @@ -185,11 +165,8 @@ public function testOffsetMergeOnEndKeyPath() /** * Asserts that the container assigns a value to the endpoint of a nonexistent midpoint * {@see SeparatorAwareTrait::setWithSeparator() in the keypath}. - * - * @covers ::offsetReplace() - * @return void */ - public function testOffsetMergeOnNonexistentMidKeyPath() + public function testOffsetMergeOnNonexistentMidKeyPath(): void { $cfg = $this->cfg; @@ -206,22 +183,13 @@ public function testOffsetMergeOnNonexistentMidKeyPath() } // ========================================================================= - - /** - * @covers ::offsetReplace() - * @return void - */ - public function testOffsetMergeIgnoredOnZeroLengthKey() + public function testOffsetMergeIgnoredOnZeroLengthKey(): void { $this->cfg->offsetReplace('', 'waldo'); $this->assertNull($this->cfg['']); } - /** - * @covers ::offsetReplace() - * @return void - */ - public function testOffsetMergeIgnoredOnUnderscoreKey() + public function testOffsetMergeIgnoredOnUnderscoreKey(): void { $this->cfg->offsetReplace('_', 'waldo'); $this->assertNull($this->cfg['_']); @@ -229,11 +197,8 @@ public function testOffsetMergeIgnoredOnUnderscoreKey() /** * Asserts that a numeric key throws an exception, when merging a value. - * - * @covers ::offsetReplace() - * @return void */ - public function testOffsetMergeThrowsExceptionOnNumericKey() + public function testOffsetMergeThrowsExceptionOnNumericKey(): void { $this->expectExceptionMessage("Entity array access only supports non-numeric keys"); $this->expectException(InvalidArgumentException::class); diff --git a/packages/config/tests/Charcoal/Config/Config/ConfigDelegatesAwareTest.php b/packages/config/tests/Charcoal/Config/Config/ConfigDelegatesAwareTest.php index b777d94b7..058a28bbf 100644 --- a/packages/config/tests/Charcoal/Config/Config/ConfigDelegatesAwareTest.php +++ b/packages/config/tests/Charcoal/Config/Config/ConfigDelegatesAwareTest.php @@ -10,9 +10,14 @@ /** * Test DelegatesAwareTrait implementation in AbstractConfig - * - * @coversDefaultClass \Charcoal\Config\AbstractConfig */ +#[\PHPUnit\Framework\Attributes\CoversClass(\Charcoal\Config\AbstractConfig::class)] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractConfig::class, '__construct()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractConfig::class, 'setDelegates()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractConfig::class, 'addDelegate()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractConfig::class, 'prependDelegate()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractConfig::class, 'offsetExists()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractConfig::class, 'offsetGet()')] class ConfigDelegatesAwareTest extends AbstractConfigTestCase { /** @@ -27,8 +32,6 @@ class ConfigDelegatesAwareTest extends AbstractConfigTestCase /** * Create a concrete MacroConfig instance. - * - * @return void */ protected function setUp(): void { @@ -62,11 +65,9 @@ protected function setUp(): void /** * Asserts that the object implements DelegatesAwareInterface. - * - * @coversNothing - * @return void */ - public function testDelegatesAwareInterface() + #[\PHPUnit\Framework\Attributes\CoversNothing] + public function testDelegatesAwareInterface(): void { $this->assertInstanceOf(DelegatesAwareInterface::class, $this->cfg); } @@ -75,15 +76,7 @@ public function testDelegatesAwareInterface() // Test Delegate Collecting // ========================================================================= - - /** - * @covers ::__construct() - * @covers ::setDelegates() - * @covers ::addDelegate() - * @covers ::prependDelegate() - * @return void - */ - public function testSetDelegates() + public function testSetDelegates(): void { $cfg = $this->createConfig(null, [ $this->delegates[0] ]); $this->assertEquals(0, $cfg['bop']); @@ -99,100 +92,83 @@ public function testSetDelegates() // Test ArrayAccess on delegated properties // ========================================================================= - /** * Asserts that the delegate container returns TRUE if a data key is found * {@see DelegatesAwareTrait::hasInDelegates() among its delegates}. - * - * @covers ::offsetExists() - * @return void */ - public function testOffsetExistsInDelegates() + public function testOffsetExistsInDelegates(): void { $cfg = $this->cfg; - $this->assertObjectNotHasAttribute('bar', $cfg); - $this->assertObjectHasAttribute('bar', $this->delegates[1]); + $this->assertFalse(property_exists($cfg, 'bar')); + $this->assertTrue(property_exists($this->delegates[1], 'bar')); $this->assertTrue(isset($cfg['bar'])); } /** * Asserts that the delegate container returns FALSE if a data key is nonexistent * {@see DelegatesAwareTrait::hasInDelegates() among its delegates}. - * - * @covers ::offsetExists() - * @return void */ - public function testOffsetExistsReturnsFalseOnNonexistentKeyInDelegates() + public function testOffsetExistsReturnsFalseOnNonexistentKeyInDelegates(): void { $cfg = $this->cfg; - $this->assertObjectNotHasAttribute('zyx', $cfg); + $this->assertFalse(property_exists($cfg, 'zyx')); $this->assertFalse(isset($cfg['zyx'])); } /** * Asserts that the delegate container returns the value of a data key found * {@see DelegatesAwareTrait::getInDelegates() among its delegates}. - * - * @covers ::offsetGet() - * @return void */ - public function testOffsetGetInDelegates() + public function testOffsetGetInDelegates(): void { $cfg = $this->cfg; - $this->assertObjectNotHasAttribute('qux', $cfg); - $this->assertObjectHasAttribute('qux', $this->delegates[2]); + $this->assertFalse(property_exists($cfg, 'qux')); + $this->assertTrue(property_exists($this->delegates[2], 'qux')); $this->assertEquals($this->delegates[2]['qux'], $cfg['qux']); } /** * Asserts that the delegate container returns NULL if a data key is nonexistent * {@see DelegatesAwareTrait::getInDelegates() among its delegates}. - * - * @covers ::offsetExists() - * @return void */ - public function testOffsetGetReturnsNullOnNonexistentKeyInDelegates() + public function testOffsetGetReturnsNullOnNonexistentKeyInDelegates(): void { $cfg = $this->cfg; - $this->assertObjectNotHasAttribute('xyz', $cfg); + $this->assertFalse(property_exists($cfg, 'xyz')); $this->assertNull($cfg['xyz']); } /** * Asserts that attributes in delegates cannot be mutated by the delegate container. - * - * @coversNothing - * @return void */ - public function testOffsetSetDoesNotPerformMutationsInDelegates() + #[\PHPUnit\Framework\Attributes\CoversNothing] + public function testOffsetSetDoesNotPerformMutationsInDelegates(): void { $cfg = $this->cfg; - $this->assertObjectNotHasAttribute('qux', $cfg); - $this->assertObjectHasAttribute('qux', $this->delegates[2]); + $this->assertFalse(property_exists($cfg, 'qux')); + $this->assertTrue(property_exists($this->delegates[2], 'qux')); $cfg['qux'] = 'garply'; - $this->assertObjectHasAttribute('qux', $cfg); + $this->assertTrue(property_exists($cfg, 'qux')); $this->assertEquals('garply', $cfg['qux']); $this->assertEquals('xyzzy', $this->delegates[2]['qux']); } /** * Asserts that attributes in delegates cannot be removed by the delegate container. - * - * @coversNothing - * @return void */ - public function testOffsetUnsetDoesNotPerformMutationsInDelegates() + #[\PHPUnit\Framework\Attributes\CoversNothing] + public function testOffsetUnsetDoesNotPerformMutationsInDelegates(): void { $cfg = $this->cfg; - $this->assertObjectNotHasAttribute('qux', $cfg); - $this->assertObjectHasAttribute('qux', $this->delegates[2]); + $this->assertFalse(property_exists($cfg, 'qux')); + $this->assertTrue(property_exists($this->delegates[2], 'qux')); unset($cfg['qux']); $this->assertEquals($this->delegates[2]['qux'], $cfg['qux']); @@ -201,15 +177,13 @@ public function testOffsetUnsetDoesNotPerformMutationsInDelegates() /** * Asserts that removing a value from the delegate container allows subsequent requests * to lookup a fallback in a delegate. - * - * @coversNothing - * @return void */ - public function testOffsetUnsetOnConfigWithFallbackInDelegates() + #[\PHPUnit\Framework\Attributes\CoversNothing] + public function testOffsetUnsetOnConfigWithFallbackInDelegates(): void { $cfg = $this->cfg; - $this->assertObjectHasAttribute('hud', $cfg); + $this->assertTrue(property_exists($cfg, 'hud')); $this->assertEquals('flob', $cfg['hud']); unset($cfg['hud']); diff --git a/packages/config/tests/Charcoal/Config/Config/ConfigFileAwareTest.php b/packages/config/tests/Charcoal/Config/Config/ConfigFileAwareTest.php index 1648c8040..af45165b9 100644 --- a/packages/config/tests/Charcoal/Config/Config/ConfigFileAwareTest.php +++ b/packages/config/tests/Charcoal/Config/Config/ConfigFileAwareTest.php @@ -18,9 +18,10 @@ * @todo ::addFile() * @todo ::merge() * - * - * @coversDefaultClass \Charcoal\Config\AbstractConfig */ +#[\PHPUnit\Framework\Attributes\CoversClass(\Charcoal\Config\AbstractConfig::class)] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractConfig::class, '__construct()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractConfig::class, 'addFile()')] class ConfigFileAwareTest extends AbstractConfigTestCase { /** @@ -30,8 +31,6 @@ class ConfigFileAwareTest extends AbstractConfigTestCase /** * Create a concrete GenericConfig instance. - * - * @return void */ protected function setUp(): void { @@ -43,31 +42,23 @@ protected function setUp(): void * * @param mixed $data Data to pre-populate the object. * @param array $delegates Delegates to pre-populate the object. - * @return GenericConfig */ - public function createConfig($data = null, array $delegates = null) + #[\Override] + public function createConfig($data = null, ?array $delegates = null): \Charcoal\Config\GenericConfig { return new GenericConfig($data, $delegates); } /** * Asserts that the object implements FileAwareInterface. - * - * @coversNothing - * @return void */ - public function testFileAwareInterface() + #[\PHPUnit\Framework\Attributes\CoversNothing] + public function testFileAwareInterface(): void { $this->assertInstanceOf(FileAwareInterface::class, $this->cfg); } - /** - * @covers ::__construct() - * @covers ::addFile() - * - * @return void - */ - public function testConstructWithSupportedFormat() + public function testConstructWithSupportedFormat(): void { $path = $this->getPathToFixture('pass/valid.json'); $cfg = $this->createConfig($path); @@ -78,14 +69,11 @@ public function testConstructWithSupportedFormat() // Test INI // ========================================================================= - /** * INI: Asserts that the Config supports INI config files. - * - * @coversNothing - * @return void */ - public function testAddIniFile() + #[\PHPUnit\Framework\Attributes\CoversNothing] + public function testAddIniFile(): void { $path = $this->getPathToFixture('pass/valid1.ini'); $this->cfg->addFile($path); @@ -104,11 +92,9 @@ public function testAddIniFile() /** * INI: Asserts that the Config supports key-paths in INI config files. - * - * @coversNothing - * @return void */ - public function testAddIniFileWithDelimitedData() + #[\PHPUnit\Framework\Attributes\CoversNothing] + public function testAddIniFileWithDelimitedData(): void { $path = $this->getPathToFixture('pass/valid2.ini'); $this->cfg->addFile($path); @@ -126,11 +112,8 @@ public function testAddIniFileWithDelimitedData() /** * INI: Asserts that an ordered list is NOT ignored. - * - * @covers ::addFile() - * @return void */ - public function testAddIniFileWithInvalidArray() + public function testAddIniFileWithInvalidArray(): void { $this->expectExceptionMessage('Entity array access only supports non-numeric keys'); $this->expectException(InvalidArgumentException::class); @@ -141,11 +124,8 @@ public function testAddIniFileWithInvalidArray() /** * INI: Asserts that an unparsable file is silently ignored. - * - * @covers ::addFile() - * @return void */ - public function testAddUnparsableIniFile() + public function testAddUnparsableIniFile(): void { // phpcs:disable Generic.PHP.NoSilencedErrors.Discouraged $path = $this->getPathToFixture('pass/unparsable.ini'); @@ -159,14 +139,11 @@ public function testAddUnparsableIniFile() // Test JSON // ========================================================================= - /** * JSON: Asserts that the Config supports JSON config files. - * - * @coversNothing - * @return void */ - public function testAddJsonFile() + #[\PHPUnit\Framework\Attributes\CoversNothing] + public function testAddJsonFile(): void { $path = $this->getPathToFixture('pass/valid.json'); $this->cfg->addFile($path); @@ -185,11 +162,8 @@ public function testAddJsonFile() /** * JSON: Asserts that an ordered list is NOT ignored. - * - * @covers ::addFile() - * @return void */ - public function testAddJsonFileWithInvalidArray() + public function testAddJsonFileWithInvalidArray(): void { $this->expectExceptionMessage('Entity array access only supports non-numeric keys'); $this->expectException(InvalidArgumentException::class); @@ -200,11 +174,8 @@ public function testAddJsonFileWithInvalidArray() /** * JSON: Asserts that an invalid file is silently ignored. - * - * @covers ::addFile() - * @return void */ - public function testAddJsonFileWithInvalidType() + public function testAddJsonFileWithInvalidType(): void { $path = $this->getPathToFixture('pass/invalid2.json'); $this->cfg->addFile($path); @@ -216,14 +187,11 @@ public function testAddJsonFileWithInvalidType() // Test PHP // ========================================================================= - /** * PHP: Asserts that the Config supports PHP config files. - * - * @coversNothing - * @return void */ - public function testAddPhpFile() + #[\PHPUnit\Framework\Attributes\CoversNothing] + public function testAddPhpFile(): void { $path = $this->getPathToFixture('pass/valid1.php'); $this->cfg->addFile($path); @@ -242,11 +210,9 @@ public function testAddPhpFile() /** * PHP: Asserts that the scope of PHP config files is bound to the Config. - * - * @coversNothing - * @return void */ - public function testAddPhpFileThatMutatesContext() + #[\PHPUnit\Framework\Attributes\CoversNothing] + public function testAddPhpFileThatMutatesContext(): void { $path = $this->getPathToFixture('pass/valid2.php'); $this->cfg->addFile($path); @@ -265,11 +231,8 @@ public function testAddPhpFileThatMutatesContext() /** * PHP: Asserts that an ordered list is NOT ignored. - * - * @covers ::addFile() - * @return void */ - public function testAddPhpFileWithInvalidArray() + public function testAddPhpFileWithInvalidArray(): void { $this->expectExceptionMessage('Entity array access only supports non-numeric keys'); $this->expectException(InvalidArgumentException::class); @@ -280,11 +243,8 @@ public function testAddPhpFileWithInvalidArray() /** * PHP: Asserts that an invalid file is silently ignored. - * - * @covers ::addFile() - * @return void */ - public function testAddPhpFileWithInvalidType() + public function testAddPhpFileWithInvalidType(): void { $path = $this->getPathToFixture('pass/invalid2.php'); $this->cfg->addFile($path); @@ -296,14 +256,11 @@ public function testAddPhpFileWithInvalidType() // Test YAML // ========================================================================= - /** * YAML: Asserts that the Config supports '.yml' YAML config files. - * - * @coversNothing - * @return void */ - public function testAddYamlFile() + #[\PHPUnit\Framework\Attributes\CoversNothing] + public function testAddYamlFile(): void { $path = $this->getPathToFixture('pass/valid1.yml'); $this->cfg->addFile($path); @@ -322,11 +279,8 @@ public function testAddYamlFile() /** * YAML: Asserts that an ordered list is NOT ignored. - * - * @covers ::addFile() - * @return void */ - public function testAddYamlFileWithInvalidArray() + public function testAddYamlFileWithInvalidArray(): void { $this->expectExceptionMessage('Entity array access only supports non-numeric keys'); $this->expectException(InvalidArgumentException::class); @@ -337,11 +291,8 @@ public function testAddYamlFileWithInvalidArray() /** * YAML: Asserts that an invalid file is silently ignored. - * - * @covers ::addFile() - * @return void */ - public function testAddYamlFileWithInvalidType() + public function testAddYamlFileWithInvalidType(): void { $path = $this->getPathToFixture('pass/invalid2.yml'); $this->cfg->addFile($path); diff --git a/packages/config/tests/Charcoal/Config/Config/ConfigSeparatorAwareTest.php b/packages/config/tests/Charcoal/Config/Config/ConfigSeparatorAwareTest.php index e9580b4e2..0cfd4b02d 100644 --- a/packages/config/tests/Charcoal/Config/Config/ConfigSeparatorAwareTest.php +++ b/packages/config/tests/Charcoal/Config/Config/ConfigSeparatorAwareTest.php @@ -11,9 +11,14 @@ /** * Test SeparatorAwareTrait implementation in AbstractConfig - * - * @coversDefaultClass \Charcoal\Config\AbstractConfig */ +#[\PHPUnit\Framework\Attributes\CoversClass(\Charcoal\Config\AbstractConfig::class)] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractConfig::class, '__construct()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractConfig::class, 'setSeparator()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractConfig::class, 'separator()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractConfig::class, 'offsetExists()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractConfig::class, 'offsetGet()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractConfig::class, 'offsetSet()')] class ConfigSeparatorAwareTest extends AbstractConfigTestCase { use AssertionsTrait; @@ -30,8 +35,6 @@ class ConfigSeparatorAwareTest extends AbstractConfigTestCase /** * Create a MacroConfig instance. - * - * @return void */ protected function setUp(): void { @@ -62,22 +65,14 @@ protected function setUp(): void /** * Asserts that the object implements SeparatorAwareInterface. - * - * @coversNothing - * @return void */ - public function testSeparatorAwareInterface() + #[\PHPUnit\Framework\Attributes\CoversNothing] + public function testSeparatorAwareInterface(): void { $this->assertInstanceOf(SeparatorAwareInterface::class, $this->cfg); } - /** - * @covers ::__construct() - * @covers ::setSeparator() - * @covers ::separator() - * @return void - */ - public function testDefaultSeparator() + public function testDefaultSeparator(): void { $cfg = $this->createConfig(); $this->assertEquals(AbstractConfig::DEFAULT_SEPARATOR, $cfg->separator()); @@ -87,45 +82,35 @@ public function testDefaultSeparator() // Test ArrayAccess on nested properties // ========================================================================= - /** * Asserts that the container returns TRUE if an endpoint is found * {@see SeparatorAwareTrait::hasWithSeparator() in a keypath}. - * - * @covers ::offsetExists() - * @return void */ - public function testOffsetExistsOnEndKeyPath() + public function testOffsetExistsOnEndKeyPath(): void { $cfg = $this->cfg; - $this->assertObjectHasAttribute('connections', $cfg); + $this->assertTrue(property_exists($cfg, 'connections')); $this->assertTrue(isset($cfg['connections.default.host'])); } /** * Asserts that the container returns TRUE if a midpoint is found * {@see SeparatorAwareTrait::hasWithSeparator() in a keypath}. - * - * @covers ::offsetExists() - * @return void */ - public function testOffsetExistsOnMidKeyPath() + public function testOffsetExistsOnMidKeyPath(): void { $cfg = $this->cfg; - $this->assertObjectHasAttribute('connections', $cfg); + $this->assertTrue(property_exists($cfg, 'connections')); $this->assertTrue(isset($cfg['connections.default'])); } /** * Asserts that the container returns FALSE if an endpoint is nonexistent * {@see SeparatorAwareTrait::hasWithSeparator() in a keypath}. - * - * @covers ::offsetExists() - * @return void */ - public function testOffsetExistsReturnsFalseOnNonexistentEndKeyPath() + public function testOffsetExistsReturnsFalseOnNonexistentEndKeyPath(): void { $cfg = $this->cfg; @@ -135,11 +120,8 @@ public function testOffsetExistsReturnsFalseOnNonexistentEndKeyPath() /** * Asserts that the container returns FALSE if a midpoint is nonexistent * {@see SeparatorAwareTrait::hasWithSeparator() in a keypath}. - * - * @covers ::offsetExists() - * @return void */ - public function testOffsetExistsReturnsFalseOnNonexistentMidKeyPath() + public function testOffsetExistsReturnsFalseOnNonexistentMidKeyPath(): void { $cfg = $this->cfg; @@ -149,11 +131,8 @@ public function testOffsetExistsReturnsFalseOnNonexistentMidKeyPath() /** * Asserts that the container returns the value of the endpoint found * {@see SeparatorAwareTrait::getWithSeparator() in a keypath}. - * - * @covers ::offsetGet() - * @return void */ - public function testOffsetGetOnEndKeyPath() + public function testOffsetGetOnEndKeyPath(): void { $cfg = $this->cfg; @@ -166,11 +145,8 @@ public function testOffsetGetOnEndKeyPath() /** * Asserts that the container returns the value of the midpoint found * {@see SeparatorAwareTrait::getWithSeparator() in a keypath}. - * - * @covers ::offsetGet() - * @return void */ - public function testOffsetGetOnMidKeyPath() + public function testOffsetGetOnMidKeyPath(): void { $cfg = $this->cfg; @@ -183,11 +159,8 @@ public function testOffsetGetOnMidKeyPath() /** * Asserts that the container returns NULL if the endpoint is nonexistent * {@see SeparatorAwareTrait::getWithSeparator() in a keypath}. - * - * @covers ::offsetGet() - * @return void */ - public function testOffsetGetReturnsNullOnNonexistentEndKeyPath() + public function testOffsetGetReturnsNullOnNonexistentEndKeyPath(): void { $cfg = $this->cfg; @@ -197,11 +170,8 @@ public function testOffsetGetReturnsNullOnNonexistentEndKeyPath() /** * Asserts that the container returns NULL if the midpoint is nonexistent * {@see SeparatorAwareTrait::getWithSeparator() in a keypath}. - * - * @covers ::offsetGet() - * @return void */ - public function testOffsetGetReturnsNullOnNonexistentMidKeyPath() + public function testOffsetGetReturnsNullOnNonexistentMidKeyPath(): void { $cfg = $this->cfg; @@ -211,11 +181,8 @@ public function testOffsetGetReturnsNullOnNonexistentMidKeyPath() /** * Asserts that the container assigns a value to the endpoint * {@see SeparatorAwareTrait::setWithSeparator() of the keypath}. - * - * @covers ::offsetSet() - * @return void */ - public function testOffsetSetOnEndKeyPath() + public function testOffsetSetOnEndKeyPath(): void { $cfg = $this->cfg; @@ -226,11 +193,8 @@ public function testOffsetSetOnEndKeyPath() /** * Asserts that the container assigns a value to the endpoint of a nonexistent midpoint * {@see SeparatorAwareTrait::setWithSeparator() in the keypath}. - * - * @covers ::offsetSet() - * @return void */ - public function testOffsetSetOnNonexistentMidKeyPath() + public function testOffsetSetOnNonexistentMidKeyPath(): void { $cfg = $this->cfg; $this->assertNull($cfg['connections.analytics']); @@ -242,11 +206,9 @@ public function testOffsetSetOnNonexistentMidKeyPath() /** * Asserts that the container assigns NULL to the endpoint * {@see SeparatorAwareTrait::setWithSeparator() of the keypath} to "remove". - * - * @coversNothing - * @return void */ - public function testOffsetUnsetOnEndKeyPath() + #[\PHPUnit\Framework\Attributes\CoversNothing] + public function testOffsetUnsetOnEndKeyPath(): void { $cfg = $this->cfg; @@ -257,11 +219,9 @@ public function testOffsetUnsetOnEndKeyPath() /** * Asserts that the container assigns NULL to the midpoint * {@see SeparatorAwareTrait::setWithSeparator() of the keypath} to "remove". - * - * @coversNothing - * @return void */ - public function testOffsetUnsetOnMidKeyPath() + #[\PHPUnit\Framework\Attributes\CoversNothing] + public function testOffsetUnsetOnMidKeyPath(): void { $cfg = $this->cfg; diff --git a/packages/config/tests/Charcoal/Config/Config/ConfigTest.php b/packages/config/tests/Charcoal/Config/Config/ConfigTest.php index d76d7d5ae..8ac73eb39 100644 --- a/packages/config/tests/Charcoal/Config/Config/ConfigTest.php +++ b/packages/config/tests/Charcoal/Config/Config/ConfigTest.php @@ -26,9 +26,13 @@ * - ConfigSeparatorAwareTest * - ConfigFileAwareTest * - FileLoader/* - * - * @coversDefaultClass \Charcoal\Config\AbstractConfig */ +#[\PHPUnit\Framework\Attributes\CoversClass(\Charcoal\Config\AbstractConfig::class)] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractConfig::class, 'getIterator()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractConfig::class, '__construct')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractConfig::class, 'merge')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractConfig::class, 'setData')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractConfig::class, 'defaults')] class ConfigTest extends AbstractConfigTestCase { use AssertionsTrait; @@ -40,8 +44,6 @@ class ConfigTest extends AbstractConfigTestCase /** * Create a concrete MacroConfig instance. - * - * @return void */ protected function setUp(): void { @@ -50,33 +52,23 @@ protected function setUp(): void /** * Asserts that the object implements PSR-11. - * - * @coversNothing - * @return void */ - public function testPsr11() + #[\PHPUnit\Framework\Attributes\CoversNothing] + public function testPsr11(): void { $this->assertInstanceOf(ContainerInterface::class, $this->cfg); } /** * Asserts that the object implements IteratorAggregate. - * - * @covers ::getIterator() - * @return void */ - public function testIteratorAggregate() + public function testIteratorAggregate(): void { $this->assertInstanceOf(IteratorAggregate::class, $this->cfg); $this->assertInstanceOf(ArrayIterator::class, $this->cfg->getIterator()); } - /** - * @covers ::__construct - * @covers ::merge - * @return void - */ - public function testConstructWithArray() + public function testConstructWithArray(): void { $cfg = $this->mockConfig([ 'name' => 'Charcoal' @@ -84,23 +76,13 @@ public function testConstructWithArray() $this->assertEquals('Charcoal', $cfg['name']); } - /** - * @covers ::__construct - * @covers ::merge - * @return void - */ - public function testConstructWithConfigInstance() + public function testConstructWithConfigInstance(): void { $cfg = $this->mockConfig($this->cfg); $this->assertEquals('garply', $cfg['baz']); } - /** - * @covers ::__construct - * @covers ::merge - * @return void - */ - public function testConstructWithTraversableInstance() + public function testConstructWithTraversableInstance(): void { $iter = new ArrayIterator([ 'name' => 'Charcoal' @@ -109,35 +91,23 @@ public function testConstructWithTraversableInstance() $this->assertEquals('Charcoal', $cfg['name']); } - /** - * - * @covers ::__construct - * @covers ::merge - * @return void - */ - public function testConstructWithInvalidData() + public function testConstructWithInvalidData(): void { $this->expectExceptionMessage('Data must be a config file, an associative array, or an object implementing Traversable'); $this->expectException(InvalidArgumentException::class); $std = new StdClass; - $cfg = $this->mockConfig($std); + $this->mockConfig($std); } // Test Defaults // ========================================================================= - /** * Asserts that, when defined, a Config will apply the class' default data. - * - * @covers ::__construct - * @covers ::setData - * @covers ::defaults - * @return void */ - public function testConstructWithDefaults() + public function testConstructWithDefaults(): void { /** @var array $defaults {@see \Charcoal\Tests\Config\Mock\MacroConfig::defaults()} */ $defaults = [ @@ -173,11 +143,8 @@ public function testConstructWithDefaults() /** * Asserts that, by default, a Config has no default data. - * - * @covers ::defaults - * @return void */ - public function testEmptyDefaults() + public function testEmptyDefaults(): void { $cfg = $this->mockConfig(); $this->assertEmpty($cfg->defaults()); diff --git a/packages/config/tests/Charcoal/Config/Entity/AbstractEntityTestCase.php b/packages/config/tests/Charcoal/Config/Entity/AbstractEntityTestCase.php index 85a2b9a78..0bbc42971 100644 --- a/packages/config/tests/Charcoal/Config/Entity/AbstractEntityTestCase.php +++ b/packages/config/tests/Charcoal/Config/Entity/AbstractEntityTestCase.php @@ -18,7 +18,7 @@ abstract class AbstractEntityTestCase extends AbstractTestCase * @param array $data Data to assign to the object. * @return MacroEntity */ - public function createEntity(array $data = null) + public function createEntity(?array $data = null) { return new MacroEntity($data); } @@ -29,7 +29,7 @@ public function createEntity(array $data = null) * @param array $data Data to assign to the object. * @return AbstractEntity */ - public function mockEntity(array $data = null) + public function mockEntity(?array $data = null) { $obj = $this->getMockForAbstractClass(AbstractEntity::class); diff --git a/packages/config/tests/Charcoal/Config/Entity/EntityArrayAccessTest.php b/packages/config/tests/Charcoal/Config/Entity/EntityArrayAccessTest.php index 9873e25b0..2269bdf40 100644 --- a/packages/config/tests/Charcoal/Config/Entity/EntityArrayAccessTest.php +++ b/packages/config/tests/Charcoal/Config/Entity/EntityArrayAccessTest.php @@ -12,9 +12,17 @@ /** * Test ArrayAccess implementation in AbstractEntity - * - * @coversDefaultClass \Charcoal\Config\AbstractEntity */ +#[\PHPUnit\Framework\Attributes\CoversClass(\Charcoal\Config\AbstractEntity::class)] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractEntity::class, 'offsetExists()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractEntity::class, 'offsetGet()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractEntity::class, 'offsetSet()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractEntity::class, 'offsetUnset()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Tests\Config\Mock\MacroEntity::class, 'foo()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Tests\Config\Mock\MacroEntity::class, 'setFoo()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractEntity::class, 'has()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractEntity::class, 'get()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractEntity::class, 'set()')] class EntityArrayAccessTest extends AbstractEntityTestCase { use ArrayAccessTestTrait; @@ -26,8 +34,6 @@ class EntityArrayAccessTest extends AbstractEntityTestCase /** * Create a concrete MacroEntity instance. - * - * @return void */ protected function setUp(): void { @@ -41,9 +47,9 @@ protected function setUp(): void /** * Asserts that the object implements ArrayAccess. * - * @coversNothing * @return MacroEntity */ + #[\PHPUnit\Framework\Attributes\CoversNothing] public function testArrayAccess() { $this->assertInstanceOf(ArrayAccess::class, $this->obj); @@ -54,17 +60,12 @@ public function testArrayAccess() // Test ArrayAccess on non-private properties // ========================================================================= - - /** - * @covers ::offsetExists() - * @return void - */ - public function testOffsetExists() + public function testOffsetExists(): void { $obj = $this->obj; // MacroEntity::$name - $this->assertObjectHasAttribute('name', $obj); + $this->assertTrue(property_exists($obj, 'name')); $this->assertTrue(isset($obj['name'])); // MacroEntity::foo() @@ -74,11 +75,7 @@ public function testOffsetExists() $this->assertTrue(isset($obj['erd'])); } - /** - * @covers ::offsetGet() - * @return void - */ - public function testOffsetGet() + public function testOffsetGet(): void { $obj = $this->obj; @@ -92,29 +89,21 @@ public function testOffsetGet() $this->assertEquals(true, $obj['erd']); } - /** - * @covers ::offsetSet() - * @return void - */ - public function testOffsetSet() + public function testOffsetSet(): void { $obj = $this->obj; $obj['baz'] = 'waldo'; - $this->assertObjectHasAttribute('baz', $obj); + $this->assertTrue(property_exists($obj, 'baz')); $this->assertEquals('waldo', $obj['baz']); } - /** - * @covers ::offsetUnset() - * @return void - */ - public function testOffsetUnset() + public function testOffsetUnset(): void { $obj = $this->obj; unset($obj['name']); - $this->assertObjectHasAttribute('name', $obj); + $this->assertTrue(property_exists($obj, 'name')); $this->assertNull($obj['name']); } @@ -122,38 +111,22 @@ public function testOffsetUnset() // Test ArrayAccess on encapsulated properties // ========================================================================= - - /** - * @covers \Charcoal\Tests\Config\Mock\MacroEntity::foo() - * @covers ::offsetExists() - * @return void - */ - public function testOffsetExistsOnEncapsulatedMethod() + public function testOffsetExistsOnEncapsulatedMethod(): void { $obj = $this->obj; - $this->assertObjectHasAttribute('foo', $obj); + $this->assertTrue(property_exists($obj, 'foo')); $this->assertTrue(isset($obj['foo'])); } - /** - * @covers \Charcoal\Tests\Config\Mock\MacroEntity::foo() - * @covers ::offsetGet() - * @return void - */ - public function testOffsetGetOnEncapsulatedMethod() + public function testOffsetGetOnEncapsulatedMethod(): void { $obj = $this->obj; $this->assertEquals('foo is 20', $obj['foo']); } - /** - * @covers \Charcoal\Tests\Config\Mock\MacroEntity::setFoo() - * @covers ::offsetSet() - * @return void - */ - public function testOffsetSetOnEncapsulatedMethod() + public function testOffsetSetOnEncapsulatedMethod(): void { $obj = $this->obj; @@ -161,17 +134,12 @@ public function testOffsetSetOnEncapsulatedMethod() $this->assertEquals('foo is 42', $obj['foo']); } - /** - * @covers \Charcoal\Tests\Config\Mock\MacroEntity::setFoo() - * @covers ::offsetUnset() - * @return void - */ - public function testOffsetUnsetOnEncapsulatedMethod() + public function testOffsetUnsetOnEncapsulatedMethod(): void { $obj = $this->obj; unset($obj['foo']); - $this->assertObjectHasAttribute('foo', $obj); + $this->assertTrue(property_exists($obj, 'foo')); $this->assertEquals('foo is 10', $obj['foo']); } @@ -179,44 +147,31 @@ public function testOffsetUnsetOnEncapsulatedMethod() // Test ArrayAccess via aliases // ========================================================================= - - /** - * @covers ::has() - * @return void - */ - public function testHas() + public function testHas(): void { $obj = $this->obj; - $this->assertObjectHasAttribute('name', $obj); + $this->assertTrue(property_exists($obj, 'name')); $this->assertTrue($obj->has('name')); unset($obj['name']); $this->assertFalse($obj->has('name')); } - /** - * @covers ::get() - * @return void - */ - public function testGet() + public function testGet(): void { $obj = $this->obj; $this->assertEquals('Charcoal', $obj->get('name')); } - /** - * @covers ::set() - * @return void - */ - public function testSet() + public function testSet(): void { $obj = $this->obj; $that = $obj->set('baz', 'waldo'); $this->assertEquals($obj, $that); - $this->assertObjectHasAttribute('baz', $obj); + $this->assertTrue(property_exists($obj, 'baz')); $this->assertEquals('waldo', $obj->get('baz')); } } diff --git a/packages/config/tests/Charcoal/Config/Entity/EntityTest.php b/packages/config/tests/Charcoal/Config/Entity/EntityTest.php index 4f381c79d..5fdfeabe2 100644 --- a/packages/config/tests/Charcoal/Config/Entity/EntityTest.php +++ b/packages/config/tests/Charcoal/Config/Entity/EntityTest.php @@ -10,9 +10,17 @@ /** * Test AbstractEntity - * - * @coversDefaultClass \Charcoal\Config\AbstractEntity */ +#[\PHPUnit\Framework\Attributes\CoversClass(\Charcoal\Config\AbstractEntity::class)] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractEntity::class, 'keys()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractEntity::class, 'setData()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractEntity::class, 'data()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractEntity::class, 'offsetSet()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractEntity::class, 'offsetGet()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractEntity::class, 'camelize()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractEntity::class, 'jsonSerialize()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractEntity::class, 'serialize()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\AbstractEntity::class, 'unserialize()')] class EntityTest extends AbstractEntityTestCase { use AssertionsTrait; @@ -24,8 +32,6 @@ class EntityTest extends AbstractEntityTestCase /** * Create a concrete MacroEntity instance. - * - * @return void */ protected function setUp(): void { @@ -39,11 +45,8 @@ protected function setUp(): void * - Keys are empty by default * - Keys are added automatically when setting a value via {@see ArrayAccess::offsetSet()} * - Keys are removed automatically when unsetting a value via {@see ArrayAccess::offsetUnset()} - * - * @covers ::keys() - * @return void */ - public function testKeys() + public function testKeys(): void { $obj = $this->obj; @@ -64,15 +67,13 @@ public function testKeys() // Test Data Methods // ========================================================================= - /** * Retrieve data for {@see AbstractEntity::setData()}. * * @used-by self::testSetData() * @used-by self::testGetDataSubset() - * @return array */ - public function getSetData() + public function getSetData(): array { return [ 'name' => 'Charcoal', @@ -96,12 +97,8 @@ public function getSetData() * - When assigning data, the entity will ignore the key "data" * to prevent recursion calls. * - The key-value pair "foo" will be passed to {@see MacroEntity::setFoo()} - * - * @covers ::setData() - * @covers ::data() - * @return void */ - public function testSetData() + public function testSetData(): void { $obj = $this->obj; @@ -129,11 +126,8 @@ public function testSetData() * - The entity will ignore "data" to prevent recursion calls * - The entity will accept "name", "type", "foo", "baz" * - The entity will pass "foo" to {@see MacroEntity::setFoo()} - * - * @covers ::data() - * @return void */ - public function testGetDataSubset() + public function testGetDataSubset(): void { $obj = $this->obj; @@ -150,14 +144,8 @@ public function testGetDataSubset() /** * Test {@see AbstractEntity::setData()} via {@see \ArrayAccess::offsetSet()}. - * - * @covers ::offsetSet() - * @covers ::offsetGet() - * @covers ::setData() - * @covers ::data() - * @return void */ - public function testSetDataViaArrayAccess() + public function testSetDataViaArrayAccess(): void { $obj = $this->obj; @@ -177,7 +165,6 @@ public function testSetDataViaArrayAccess() // Test Internals // ========================================================================= - /** * Test camelization of entity keys. * @@ -185,16 +172,13 @@ public function testSetDataViaArrayAccess() * - Keys are interchangeable between "snake_case" and "camelCase" * - Keys are converted to "camelCase" for method calls or property assignments * - Keys are memorized as "camelCase" - * - * @covers ::camelize() - * @return void */ - public function testCamelize() + public function testCamelize(): void { $obj = $this->obj; $obj->set('foo_bar', 'waldo'); - $this->assertObjectHasAttribute('fooBar', $obj); + $this->assertTrue(property_exists($obj, 'fooBar')); $this->assertEquals('waldo', $obj['fooBar']); $this->assertEquals('waldo', $obj['foo___bar']); $this->assertArrayContains([ 'fooBar' ], $obj->keys()); @@ -206,11 +190,8 @@ public function testCamelize() * Assertions: * 1. Serialization from default state * 2. Serialization from mutated state - * - * @covers ::jsonSerialize() - * @return void */ - public function testJsonSerializable() + public function testJsonSerializable(): void { $obj = $this->obj; @@ -237,18 +218,14 @@ public function testJsonSerializable() * Assertions: * 1. Serialization from default state * 2. Serialization from mutated state - * - * @covers ::serialize() - * @covers ::unserialize() - * @return void */ - public function testSerializable() + public function testSerializable(): void { $obj = $this->obj; /** 1. Serialization from default state */ $that = unserialize(serialize($obj)); - $this->assertInstanceOf(get_class($obj), $that); + $this->assertInstanceOf($obj::class, $that); $this->assertEquals($obj, $that); $this->assertEmpty($that->data()); @@ -258,7 +235,7 @@ public function testSerializable() ]; $obj->setData($mutation); $that = unserialize(serialize($obj)); - $this->assertInstanceOf(get_class($obj), $that); + $this->assertInstanceOf($obj::class, $that); $this->assertEquals($obj->data(), $that->data()); $this->assertEquals('Charcoal', $that['name']); } diff --git a/packages/config/tests/Charcoal/Config/Fixture/fail/exception.php b/packages/config/tests/Charcoal/Config/Fixture/fail/exception.php index c3a551b6d..fcba7eb8a 100644 --- a/packages/config/tests/Charcoal/Config/Fixture/fail/exception.php +++ b/packages/config/tests/Charcoal/Config/Fixture/fail/exception.php @@ -1,3 +1,5 @@ 'localhost', 'port' => 11211, diff --git a/packages/config/tests/Charcoal/Config/Mixin/ArrayAccessTestTrait.php b/packages/config/tests/Charcoal/Config/Mixin/ArrayAccessTestTrait.php index c52a67c77..34f39fdd9 100644 --- a/packages/config/tests/Charcoal/Config/Mixin/ArrayAccessTestTrait.php +++ b/packages/config/tests/Charcoal/Config/Mixin/ArrayAccessTestTrait.php @@ -15,9 +15,9 @@ trait ArrayAccessTestTrait /** * Asserts that the object implements ArrayAccess. * - * @coversNothing * @return ArrayAccess The ArrayAccess implementation to test. */ + #[\PHPUnit\Framework\Attributes\CoversNothing] abstract public function testArrayAccess(); /** @@ -48,15 +48,13 @@ abstract public function testOffsetUnset(); // Test Nonexistent Key // ========================================================================= - /** * @covers ::offsetGet() * @depends testArrayAccess * * @param ArrayAccess $obj The ArrayAccess implementation to test. - * @return void */ - public function testOffsetGetReturnsNullOnNonexistentKey(ArrayAccess $obj) + public function testOffsetGetReturnsNullOnNonexistentKey(ArrayAccess $obj): void { $this->assertNull($obj['xyz']); } @@ -66,9 +64,8 @@ public function testOffsetGetReturnsNullOnNonexistentKey(ArrayAccess $obj) * @depends testArrayAccess * * @param ArrayAccess $obj The ArrayAccess implementation to test. - * @return void */ - public function testOffsetExistsReturnsFalseOnNonexistentKey(ArrayAccess $obj) + public function testOffsetExistsReturnsFalseOnNonexistentKey(ArrayAccess $obj): void { $this->assertFalse(isset($obj['xyz'])); } @@ -77,15 +74,13 @@ public function testOffsetExistsReturnsFalseOnNonexistentKey(ArrayAccess $obj) // Test Zero-Length Key // ========================================================================= - /** * @covers ::offsetGet() * @depends testArrayAccess * * @param ArrayAccess $obj The ArrayAccess implementation to test. - * @return void */ - public function testOffsetGetReturnsNullOnZeroLengthKey(ArrayAccess $obj) + public function testOffsetGetReturnsNullOnZeroLengthKey(ArrayAccess $obj): void { $this->assertNull($obj['']); } @@ -95,9 +90,8 @@ public function testOffsetGetReturnsNullOnZeroLengthKey(ArrayAccess $obj) * @depends testArrayAccess * * @param ArrayAccess $obj The ArrayAccess implementation to test. - * @return void */ - public function testOffsetExistsReturnsFalseOnZeroLengthKey(ArrayAccess $obj) + public function testOffsetExistsReturnsFalseOnZeroLengthKey(ArrayAccess $obj): void { $this->assertFalse(isset($obj[''])); } @@ -107,9 +101,8 @@ public function testOffsetExistsReturnsFalseOnZeroLengthKey(ArrayAccess $obj) * @depends testArrayAccess * * @param ArrayAccess $obj The ArrayAccess implementation to test. - * @return void */ - public function testOffsetSetIgnoredOnZeroLengthKey(ArrayAccess $obj) + public function testOffsetSetIgnoredOnZeroLengthKey(ArrayAccess $obj): void { $obj[''] = 'waldo'; $this->assertNull($obj['']); @@ -120,9 +113,8 @@ public function testOffsetSetIgnoredOnZeroLengthKey(ArrayAccess $obj) * @depends testArrayAccess * * @param ArrayAccess $obj The ArrayAccess implementation to test. - * @return void */ - public function testOffsetUnsetIgnoredOnZeroLengthKey(ArrayAccess $obj) + public function testOffsetUnsetIgnoredOnZeroLengthKey(ArrayAccess $obj): void { unset($obj['']); $this->assertNull($obj['']); @@ -132,15 +124,13 @@ public function testOffsetUnsetIgnoredOnZeroLengthKey(ArrayAccess $obj) // Test Snake-Case Delimiter Key // ========================================================================= - /** * @covers ::offsetGet() * @depends testArrayAccess * * @param ArrayAccess $obj The ArrayAccess implementation to test. - * @return void */ - public function testOffsetGetReturnsNullOnUnderscoreKey(ArrayAccess $obj) + public function testOffsetGetReturnsNullOnUnderscoreKey(ArrayAccess $obj): void { $this->assertNull($obj['_']); } @@ -150,9 +140,8 @@ public function testOffsetGetReturnsNullOnUnderscoreKey(ArrayAccess $obj) * @depends testArrayAccess * * @param ArrayAccess $obj The ArrayAccess implementation to test. - * @return void */ - public function testOffsetExistsReturnsFalseOnUnderscoreKey(ArrayAccess $obj) + public function testOffsetExistsReturnsFalseOnUnderscoreKey(ArrayAccess $obj): void { $this->assertFalse(isset($obj['_'])); } @@ -162,9 +151,8 @@ public function testOffsetExistsReturnsFalseOnUnderscoreKey(ArrayAccess $obj) * @depends testArrayAccess * * @param ArrayAccess $obj The ArrayAccess implementation to test. - * @return void */ - public function testOffsetSetIgnoredOnUnderscoreKey(ArrayAccess $obj) + public function testOffsetSetIgnoredOnUnderscoreKey(ArrayAccess $obj): void { $obj['_'] = 'waldo'; $this->assertNull($obj['_']); @@ -175,9 +163,8 @@ public function testOffsetSetIgnoredOnUnderscoreKey(ArrayAccess $obj) * @depends testArrayAccess * * @param ArrayAccess $obj The ArrayAccess implementation to test. - * @return void */ - public function testOffsetUnsetIgnoredOnUnderscoreKey(ArrayAccess $obj) + public function testOffsetUnsetIgnoredOnUnderscoreKey(ArrayAccess $obj): void { unset($obj['']); $this->assertNull($obj['_']); @@ -186,7 +173,6 @@ public function testOffsetUnsetIgnoredOnUnderscoreKey(ArrayAccess $obj) // Test Numeric Key // ========================================================================= - /** * Asserts that a numeric key throws an exception, when retrieving a value. * @@ -194,9 +180,8 @@ public function testOffsetUnsetIgnoredOnUnderscoreKey(ArrayAccess $obj) * @depends testArrayAccess * * @param ArrayAccess $obj The ArrayAccess implementation to test. - * @return void */ - public function testOffsetGetThrowsExceptionOnNumericKey(ArrayAccess $obj) + public function testOffsetGetThrowsExceptionOnNumericKey(ArrayAccess $obj): void { $this->expectException(InvalidArgumentException::class); $obj[0]; @@ -209,9 +194,8 @@ public function testOffsetGetThrowsExceptionOnNumericKey(ArrayAccess $obj) * @depends testArrayAccess * * @param ArrayAccess $obj The ArrayAccess implementation to test. - * @return void */ - public function testOffsetSetThrowsExceptionOnNumericKey(ArrayAccess $obj) + public function testOffsetSetThrowsExceptionOnNumericKey(ArrayAccess $obj): void { $this->expectException(InvalidArgumentException::class); $obj[0] = 'waldo'; @@ -224,12 +208,11 @@ public function testOffsetSetThrowsExceptionOnNumericKey(ArrayAccess $obj) * @depends testArrayAccess * * @param ArrayAccess $obj The ArrayAccess implementation to test. - * @return void */ - public function testOffsetExistsThrowsExceptionOnNumericKey(ArrayAccess $obj) + public function testOffsetExistsThrowsExceptionOnNumericKey(ArrayAccess $obj): void { $this->expectException(InvalidArgumentException::class); - isset($obj[0]); + $obj[0]; } /** @@ -239,9 +222,8 @@ public function testOffsetExistsThrowsExceptionOnNumericKey(ArrayAccess $obj) * @depends testArrayAccess * * @param ArrayAccess $obj The ArrayAccess implementation to test. - * @return void */ - public function testOffsetUnsetThrowsExceptionOnNumericKey(ArrayAccess $obj) + public function testOffsetUnsetThrowsExceptionOnNumericKey(ArrayAccess $obj): void { $this->expectException(InvalidArgumentException::class); unset($obj[0]); diff --git a/packages/config/tests/Charcoal/Config/Mixin/ConfigurableTest.php b/packages/config/tests/Charcoal/Config/Mixin/ConfigurableTest.php index 66abc36fd..50d571bf1 100644 --- a/packages/config/tests/Charcoal/Config/Mixin/ConfigurableTest.php +++ b/packages/config/tests/Charcoal/Config/Mixin/ConfigurableTest.php @@ -15,9 +15,11 @@ /** * Test ConfigurableTrait - * - * @coversDefaultClass \Charcoal\Config\ConfigurableTrait */ +#[\PHPUnit\Framework\Attributes\CoversTrait(\Charcoal\Config\ConfigurableTrait::class)] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\ConfigurableTrait::class, 'createConfig()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\ConfigurableTrait::class, 'setConfig()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\ConfigurableTrait::class, 'config()')] class ConfigurableTest extends AbstractTestCase { use AssertionsTrait; @@ -40,8 +42,6 @@ class ConfigurableTest extends AbstractTestCase /** * Create a ConfigurableObject instance. - * - * @return void */ protected function setUp(): void { @@ -58,10 +58,8 @@ protected function setUp(): void /** * Create a ConfigurableObject instance. - * - * @return ConfigurableObject */ - public function createObject() + public function createObject(): \Charcoal\Tests\Config\Mock\ConfigurableObject { return new ConfigurableObject(); } @@ -71,20 +69,17 @@ public function createObject() * * @param mixed $data Data to pre-populate the object. * @param array $delegates Delegates to pre-populate the object. - * @return GenericConfig */ - public function createConfig($data = null, array $delegates = null) + public function createConfig($data = null, ?array $delegates = null): \Charcoal\Config\GenericConfig { return new GenericConfig($data, $delegates); } /** * Asserts that the object implements ConfigurableInterface. - * - * @coversNothing - * @return void */ - public function testConfigurableInterface() + #[\PHPUnit\Framework\Attributes\CoversNothing] + public function testConfigurableInterface(): void { $this->assertInstanceOf(ConfigurableInterface::class, $this->obj); $this->assertInstanceOf(ConfigInterface::class, $this->obj->createConfig()); @@ -93,13 +88,7 @@ public function testConfigurableInterface() // Test SetConfig // ========================================================================= - - /** - * @covers ::createConfig() - * @covers ::setConfig() - * @return void - */ - public function testSetConfigWithString() + public function testSetConfigWithString(): void { $path = $this->getPathToFixture('pass/valid.json'); $that = $this->obj->setConfig($path); @@ -111,8 +100,6 @@ public function testSetConfigWithString() } /** - * @covers ::createConfig() - * @covers ::setConfig() * @return ConfigurableInterface */ public function testSetConfigWithArray() @@ -126,12 +113,7 @@ public function testSetConfigWithArray() return $this->obj; } - /** - * @covers ::createConfig() - * @covers ::setConfig() - * @return void - */ - public function testSetConfigWithConfigInstance() + public function testSetConfigWithConfigInstance(): void { $this->obj->setConfig($this->cfg); @@ -140,11 +122,7 @@ public function testSetConfigWithConfigInstance() $this->assertArraySubsets($this->data, $cfg->data()); } - /** - * @covers ::setConfig() - * @return void - */ - public function testSetConfigWithInvalidData() + public function testSetConfigWithInvalidData(): void { $this->expectExceptionMessage('Configset must be an associative array, a file path, or an instance of Charcoal\Config\ConfigInterface'); $this->expectException(InvalidArgumentException::class); @@ -157,107 +135,79 @@ public function testSetConfigWithInvalidData() // Test GetConfig // ========================================================================= - /** * Asserts that the object will create a new Config * if one has not been assigned to object. - * - * @covers ::createConfig() - * @covers ::config() - * @return void */ - public function testGetConfigCreatesConfig() + public function testGetConfigCreatesConfig(): void { $cfg = $this->obj->config(); $this->assertInstanceOf(GenericConfig::class, $cfg); } /** - * @covers ::config() - * @depends testSetConfigWithArray - * * @param ConfigurableInterface $obj The ConfigurableInterface implementation to test. - * @return void */ - public function testGetConfigReturnsConfigOnNullKey(ConfigurableInterface $obj) + #[\PHPUnit\Framework\Attributes\Depends('testSetConfigWithArray')] + public function testGetConfigReturnsConfigOnNullKey(ConfigurableInterface $obj): void { - $cfg = $obj->config(null); + $cfg = $obj->config(); $this->assertInstanceOf(GenericConfig::class, $cfg); } /** - * @covers ::config() - * @depends testSetConfigWithArray - * * @param ConfigurableInterface $obj The ConfigurableInterface implementation to test. - * @return void */ - public function testGetConfigReturnsValueOnKey(ConfigurableInterface $obj) + #[\PHPUnit\Framework\Attributes\Depends('testSetConfigWithArray')] + public function testGetConfigReturnsValueOnKey(ConfigurableInterface $obj): void { $this->assertEquals($this->data['name'], $obj->config('name')); } /** - * @covers ::config() - * @depends testSetConfigWithArray - * * @param ConfigurableInterface $obj The ConfigurableInterface implementation to test. - * @return void */ - public function testGetConfigReturnsNullOnNonexistentKey(ConfigurableInterface $obj) + #[\PHPUnit\Framework\Attributes\Depends('testSetConfigWithArray')] + public function testGetConfigReturnsNullOnNonexistentKey(ConfigurableInterface $obj): void { $this->assertNull($obj->config('charset')); } /** - * @covers ::config() - * @depends testSetConfigWithArray - * * @param ConfigurableInterface $obj The ConfigurableInterface implementation to test. - * @return void */ - public function testGetConfigReturnsDefaultValueOnNonexistentKey(ConfigurableInterface $obj) + #[\PHPUnit\Framework\Attributes\Depends('testSetConfigWithArray')] + public function testGetConfigReturnsDefaultValueOnNonexistentKey(ConfigurableInterface $obj): void { $val = $obj->config('charset', 'utf8mb4'); $this->assertEquals('utf8mb4', $val); } /** - * @covers ::config() - * @depends testSetConfigWithArray - * * @param ConfigurableInterface $obj The ConfigurableInterface implementation to test. - * @return void */ - public function testGetConfigReturnsFallbackClosureOnNonexistentKey(ConfigurableInterface $obj) + #[\PHPUnit\Framework\Attributes\Depends('testSetConfigWithArray')] + public function testGetConfigReturnsFallbackClosureOnNonexistentKey(ConfigurableInterface $obj): void { - $val = $obj->config('charset', function () { - return 'utf8mb4'; - }); + $val = $obj->config('charset', fn(): string => 'utf8mb4'); $this->assertEquals('utf8mb4', $val); } /** - * @covers ::config() - * @depends testSetConfigWithArray - * * @param ConfigurableInterface $obj The ConfigurableInterface implementation to test. - * @return void */ - public function testGetConfigReturnsFallbackMethodOnNonexistentKey(ConfigurableInterface $obj) + #[\PHPUnit\Framework\Attributes\Depends('testSetConfigWithArray')] + public function testGetConfigReturnsFallbackMethodOnNonexistentKey(ConfigurableInterface $obj): void { $val = $obj->config('charset', [ $this, 'getName' ]); $this->assertEquals('testGetConfigReturnsFallbackMethodOnNonexistentKey', $val); } /** - * @covers ::config() - * @depends testSetConfigWithArray - * * @param ConfigurableInterface $obj The ConfigurableInterface implementation to test. - * @return void */ - public function testGetConfigReturnsFallbackFunctionOnNonexistentKey(ConfigurableInterface $obj) + #[\PHPUnit\Framework\Attributes\Depends('testSetConfigWithArray')] + public function testGetConfigReturnsFallbackFunctionOnNonexistentKey(ConfigurableInterface $obj): void { $val = $obj->config('charset', 'getcwd'); $this->assertEquals('getcwd', $val); diff --git a/packages/config/tests/Charcoal/Config/Mixin/DelegatesAwareTest.php b/packages/config/tests/Charcoal/Config/Mixin/DelegatesAwareTest.php index 47ea0704a..af17c6939 100644 --- a/packages/config/tests/Charcoal/Config/Mixin/DelegatesAwareTest.php +++ b/packages/config/tests/Charcoal/Config/Mixin/DelegatesAwareTest.php @@ -11,9 +11,13 @@ /** * Test DelegatesAwareTrait - * - * @coversDefaultClass \Charcoal\Config\DelegatesAwareTrait */ +#[\PHPUnit\Framework\Attributes\CoversTrait(\Charcoal\Config\DelegatesAwareTrait::class)] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\DelegatesAwareTrait::class, 'setDelegates()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\DelegatesAwareTrait::class, 'addDelegate()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\DelegatesAwareTrait::class, 'prependDelegate()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\DelegatesAwareTrait::class, 'hasInDelegates()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\DelegatesAwareTrait::class, 'getInDelegates()')] class DelegatesAwareTest extends AbstractTestCase { /** @@ -28,8 +32,6 @@ class DelegatesAwareTest extends AbstractTestCase /** * Create a DelegateEntity instance. - * - * @return void */ protected function setUp(): void { @@ -72,20 +74,17 @@ protected function setUp(): void * Create a DelegateEntity instance. * * @param array $data Data to pre-populate the object. - * @return DelegateEntity */ - public function createObject(array $data = null) + public function createObject(?array $data = null): \Charcoal\Tests\Config\Mock\DelegateEntity { return new DelegateEntity($data); } /** * Asserts that the object implements DelegatesAwareInterface. - * - * @coversNothing - * @return void */ - public function testDelegatesAwareInterface() + #[\PHPUnit\Framework\Attributes\CoversNothing] + public function testDelegatesAwareInterface(): void { $this->assertInstanceOf(DelegatesAwareInterface::class, $this->obj); } @@ -94,25 +93,16 @@ public function testDelegatesAwareInterface() // Test Delegate Collecting // ========================================================================= - /** * Asserts that the separator is disabled by default. - * - * @coversNothing - * @return void */ - public function testDefaultDelegatesCollection() + #[\PHPUnit\Framework\Attributes\CoversNothing] + public function testDefaultDelegatesCollection(): void { $this->assertEmpty($this->obj->delegates()); } - /** - * @covers ::setDelegates() - * @covers ::addDelegate() - * @covers ::prependDelegate() - * @return void - */ - public function testSetDelegates() + public function testSetDelegates(): void { $obj = $this->obj; @@ -134,10 +124,10 @@ public function testSetDelegates() } /** - * @coversNothing - * @doesNotPerformAssertions * @return DelegateEntity */ + #[\PHPUnit\Framework\Attributes\CoversNothing] + #[\PHPUnit\Framework\Attributes\DoesNotPerformAssertions] public function testSetNestedDelegates() { $this->delegates[4]->addDelegate($this->delegates[2]); @@ -155,28 +145,22 @@ public function testSetNestedDelegates() // Test HasInDelegates // ========================================================================= - /** - * @covers ::hasInDelegates() - * @depends testSetNestedDelegates * * @see self::$delegates[1]['bubble'] * @param DelegatesAwareInterface $obj The DelegatesAwareInterface implementation to test. - * @return void */ - public function testHasInDelegatesReturnsTrueOnDelegatedKey(DelegatesAwareInterface $obj) + #[\PHPUnit\Framework\Attributes\Depends('testSetNestedDelegates')] + public function testHasInDelegatesReturnsTrueOnDelegatedKey(DelegatesAwareInterface $obj): void { $this->assertTrue($obj->hasInDelegates('bubble')); } /** - * @covers ::hasInDelegates() - * @depends testSetNestedDelegates - * * @param DelegatesAwareInterface $obj The DelegatesAwareInterface implementation to test. - * @return void */ - public function testHasInDelegatesReturnsFalseOnNonexistentKey(DelegatesAwareInterface $obj) + #[\PHPUnit\Framework\Attributes\Depends('testSetNestedDelegates')] + public function testHasInDelegatesReturnsFalseOnNonexistentKey(DelegatesAwareInterface $obj): void { $this->assertFalse($obj->hasInDelegates('use_error_handler')); } @@ -186,16 +170,13 @@ public function testHasInDelegatesReturnsFalseOnNonexistentKey(DelegatesAwareInt // Test GetInDelegates // ========================================================================= - /** - * @covers ::getInDelegates() - * @depends testSetNestedDelegates * * @see self::$delegates[2]['level'] * @param DelegatesAwareInterface $obj The DelegatesAwareInterface implementation to test. - * @return void */ - public function testGetInDelegatesReturnsValueOnDelegatedKey(DelegatesAwareInterface $obj) + #[\PHPUnit\Framework\Attributes\Depends('testSetNestedDelegates')] + public function testGetInDelegatesReturnsValueOnDelegatedKey(DelegatesAwareInterface $obj): void { $this->assertEquals( $this->delegates[2]['level'], @@ -204,13 +185,10 @@ public function testGetInDelegatesReturnsValueOnDelegatedKey(DelegatesAwareInter } /** - * @covers ::getInDelegates() - * @depends testSetNestedDelegates - * * @param DelegatesAwareInterface $obj The DelegatesAwareInterface implementation to test. - * @return void */ - public function testGetInDelegatesReturnsNullOnNonexistentKey(DelegatesAwareInterface $obj) + #[\PHPUnit\Framework\Attributes\Depends('testSetNestedDelegates')] + public function testGetInDelegatesReturnsNullOnNonexistentKey(DelegatesAwareInterface $obj): void { $this->assertNull($obj->getInDelegates('use_error_handler')); } diff --git a/packages/config/tests/Charcoal/Config/Mixin/FileAwareTest.php b/packages/config/tests/Charcoal/Config/Mixin/FileAwareTest.php index a943300fa..196bd2e02 100644 --- a/packages/config/tests/Charcoal/Config/Mixin/FileAwareTest.php +++ b/packages/config/tests/Charcoal/Config/Mixin/FileAwareTest.php @@ -10,58 +10,44 @@ /** * Test FileAwareTrait - * - * @coversDefaultClass \Charcoal\Config\FileAwareTrait */ +#[\PHPUnit\Framework\Attributes\CoversTrait(\Charcoal\Config\FileAwareTrait::class)] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\FileAwareTrait::class, 'loadFile()')] class FileAwareTest extends AbstractFileLoaderTestCase { /** * Asserts that the object implements FileAwareInterface. - * - * @coversNothing - * @return void */ - public function testFileAwareInterface() + #[\PHPUnit\Framework\Attributes\CoversNothing] + public function testFileAwareInterface(): void { $this->assertInstanceOf(FileAwareInterface::class, $this->obj); } - /** - * @covers ::loadFile() - * @return void - */ - public function testLoadWithUnsupportedFormat() + public function testLoadWithUnsupportedFormat(): void { $this->expectExceptionMessageMatches('/^Unsupported file format for ".+?"; must be one of ".+?"$/'); $this->expectException(InvalidArgumentException::class); $path = $this->getPathToFixture('fail/unsupported.txt'); - $data = $this->obj->loadFile($path); + $this->obj->loadFile($path); } - /** - * @covers ::loadFile() - * @return void - */ - public function testLoadWithInvalidPath() + public function testLoadWithInvalidPath(): void { $this->expectExceptionMessageMatches('/^File ".+?" does not exist$/'); $this->expectException(InvalidArgumentException::class); $path = $this->getPathToFixture('fail/missing.ini'); - $data = $this->obj->loadFile($path); + $this->obj->loadFile($path); } - /** - * @covers ::loadFile() - * @return void - */ - public function testLoadWithInvalidType() + public function testLoadWithInvalidType(): void { $this->expectExceptionMessage('File must be a string'); $this->expectException(InvalidArgumentException::class); $path = null; - $data = $this->obj->loadFile($path); + $this->obj->loadFile($path); } } diff --git a/packages/config/tests/Charcoal/Config/Mixin/FileLoader/AbstractFileLoaderTestCase.php b/packages/config/tests/Charcoal/Config/Mixin/FileLoader/AbstractFileLoaderTestCase.php index 57953c525..6ac87a944 100644 --- a/packages/config/tests/Charcoal/Config/Mixin/FileLoader/AbstractFileLoaderTestCase.php +++ b/packages/config/tests/Charcoal/Config/Mixin/FileLoader/AbstractFileLoaderTestCase.php @@ -21,8 +21,6 @@ abstract class AbstractFileLoaderTestCase extends AbstractTestCase /** * Create a FileLoader instance. - * - * @return void */ protected function setUp(): void { diff --git a/packages/config/tests/Charcoal/Config/Mixin/FileLoader/IniFileLoaderTest.php b/packages/config/tests/Charcoal/Config/Mixin/FileLoader/IniFileLoaderTest.php index 37406e840..f189a37d8 100644 --- a/packages/config/tests/Charcoal/Config/Mixin/FileLoader/IniFileLoaderTest.php +++ b/packages/config/tests/Charcoal/Config/Mixin/FileLoader/IniFileLoaderTest.php @@ -1,5 +1,7 @@ getPathToFixture('pass/valid1.ini'); $data = $this->obj->loadFile($path); @@ -42,10 +41,8 @@ public function testLoadFile() * Asserts that the File Loader does NOT support key-paths in INI config files. * * @see \Charcoal\Tests\Config\Config\ConfigFileAwareTest::testLoadIniFileWithDelimitedData - * @covers ::loadIniFile() - * @return void */ - public function testLoadFileWithDelimitedData() + public function testLoadFileWithDelimitedData(): void { $path = $this->getPathToFixture('pass/valid2.ini'); $data = $this->obj->loadFile($path); @@ -64,11 +61,8 @@ public function testLoadFileWithDelimitedData() /** * Asserts that an empty file is silently ignored. - * - * @covers ::loadIniFile() - * @return void */ - public function testLoadEmptyFile() + public function testLoadEmptyFile(): void { $path = $this->getPathToFixture('pass/empty.ini'); $data = $this->obj->loadFile($path); @@ -78,28 +72,22 @@ public function testLoadEmptyFile() /** * Asserts that a broken file is NOT ignored. - * - * @covers ::loadIniFile() - * @return void */ - public function testLoadMalformedFile() + public function testLoadMalformedFile(): void { $this->expectExceptionMessageMatches('/^INI file ".+?" is empty or invalid$/'); $this->expectException(UnexpectedValueException::class); // phpcs:disable Generic.PHP.NoSilencedErrors.Discouraged $path = $this->getPathToFixture('fail/malformed.ini'); - $data = @$this->obj->loadFile($path); + @$this->obj->loadFile($path); // phpcs:enable } /** * Asserts that an unparsable file is silently ignored. - * - * @covers ::loadIniFile() - * @return void */ - public function testLoadUnparsableFile() + public function testLoadUnparsableFile(): void { // phpcs:disable Generic.PHP.NoSilencedErrors.Discouraged $path = $this->getPathToFixture('pass/unparsable.ini'); diff --git a/packages/config/tests/Charcoal/Config/Mixin/FileLoader/JsonFileLoaderTest.php b/packages/config/tests/Charcoal/Config/Mixin/FileLoader/JsonFileLoaderTest.php index 3443b4699..2376d8f71 100644 --- a/packages/config/tests/Charcoal/Config/Mixin/FileLoader/JsonFileLoaderTest.php +++ b/packages/config/tests/Charcoal/Config/Mixin/FileLoader/JsonFileLoaderTest.php @@ -1,5 +1,7 @@ getPathToFixture('pass/valid.json'); $data = $this->obj->loadFile($path); @@ -40,11 +39,8 @@ public function testLoadFile() /** * Asserts that an empty file is silently ignored. - * - * @covers ::loadJsonFile() - * @return void */ - public function testLoadEmptyFile() + public function testLoadEmptyFile(): void { $path = $this->getPathToFixture('pass/empty.json'); $data = $this->obj->loadFile($path); @@ -54,18 +50,15 @@ public function testLoadEmptyFile() /** * Asserts that a broken file is NOT ignored. - * - * @covers ::loadJsonFile() - * @return void */ - public function testLoadMalformedFile() + public function testLoadMalformedFile(): void { $this->expectExceptionMessageMatches('/^JSON file ".+?" could not be parsed: .+$/'); $this->expectException(UnexpectedValueException::class); // phpcs:disable Generic.PHP.NoSilencedErrors.Discouraged $path = $this->getPathToFixture('fail/malformed.json'); - $data = @$this->obj->loadFile($path); + @$this->obj->loadFile($path); // phpcs:enable } } diff --git a/packages/config/tests/Charcoal/Config/Mixin/FileLoader/PhpFileLoaderTest.php b/packages/config/tests/Charcoal/Config/Mixin/FileLoader/PhpFileLoaderTest.php index 0dd110db7..7c1f4272c 100644 --- a/packages/config/tests/Charcoal/Config/Mixin/FileLoader/PhpFileLoaderTest.php +++ b/packages/config/tests/Charcoal/Config/Mixin/FileLoader/PhpFileLoaderTest.php @@ -1,5 +1,7 @@ getPathToFixture('pass/valid1.php'); $data = $this->obj->loadFile($path); @@ -40,11 +39,8 @@ public function testLoadFile() /** * Asserts that the scope of PHP config files is bound to the File Loader. - * - * @covers ::loadPhpFile() - * @return void */ - public function testLoadFileThatMutatesContext() + public function testLoadFileThatMutatesContext(): void { $path = $this->getPathToFixture('pass/valid3.php'); $data = $this->obj->loadFile($path); @@ -55,11 +51,8 @@ public function testLoadFileThatMutatesContext() /** * Asserts that an empty file is silently ignored. - * - * @covers ::loadPhpFile() - * @return void */ - public function testLoadEmptyFile() + public function testLoadEmptyFile(): void { $path = $this->getPathToFixture('pass/empty.php'); $data = $this->obj->loadFile($path); @@ -69,34 +62,28 @@ public function testLoadEmptyFile() /** * Asserts that a broken file is NOT ignored. - * - * @requires PHP >= 7.0 - * @covers ::loadPhpFile() - * @return void */ - public function testLoadMalformedFileInPhp7() + #[\PHPUnit\Framework\Attributes\RequiresPhp('>= 7.0')] + public function testLoadMalformedFileInPhp7(): void { $this->expectExceptionMessageMatches('/^PHP file ".+?" could not be parsed: .+$/'); $this->expectException(UnexpectedValueException::class); // phpcs:disable Generic.PHP.NoSilencedErrors.Discouraged $path = $this->getPathToFixture('fail/malformed.php'); - $data = $this->obj->loadFile($path); + $this->obj->loadFile($path); // phpcs:enable } /** * Asserts that an exception thrown within the file is caught. - * - * @covers ::loadPhpFile() - * @return void */ - public function testLoadExceptionalFile() + public function testLoadExceptionalFile(): void { $this->expectExceptionMessageMatches('/^PHP file ".+?" could not be parsed: Thrown Exception$/'); $this->expectException(UnexpectedValueException::class); $path = $this->getPathToFixture('fail/exception.php'); - $data = $this->obj->loadFile($path); + $this->obj->loadFile($path); } } diff --git a/packages/config/tests/Charcoal/Config/Mixin/FileLoader/YamlFileLoaderTest.php b/packages/config/tests/Charcoal/Config/Mixin/FileLoader/YamlFileLoaderTest.php index 9c7de1d6c..5c0911a75 100644 --- a/packages/config/tests/Charcoal/Config/Mixin/FileLoader/YamlFileLoaderTest.php +++ b/packages/config/tests/Charcoal/Config/Mixin/FileLoader/YamlFileLoaderTest.php @@ -12,19 +12,16 @@ /** * Test {@see FileAwareTrait::loadYamlFile() YAML File Loading} - * - * @coversDefaultClass \Charcoal\Config\FileAwareTrait */ +#[\PHPUnit\Framework\Attributes\CoversTrait(\Charcoal\Config\FileAwareTrait::class)] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\FileAwareTrait::class, 'loadYamlFile()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\FileAwareTrait::class, 'loadFile()')] class YamlFileLoaderTest extends AbstractFileLoaderTestCase { /** * Asserts that the File Loader supports '.yml' YAML config files. - * - * @covers ::loadYamlFile() - * @covers ::loadFile() - * @return void */ - public function testLoadFileWithYmlExtension() + public function testLoadFileWithYmlExtension(): void { $path = $this->getPathToFixture('pass/valid1.yml'); $data = $this->obj->loadFile($path); @@ -43,12 +40,8 @@ public function testLoadFileWithYmlExtension() /** * Asserts that the File Loader supports '.yaml' YAML config files. - * - * @covers ::loadYamlFile() - * @covers ::loadFile() - * @return void */ - public function testLoadFileWithYamlExtension() + public function testLoadFileWithYamlExtension(): void { $path = $this->getPathToFixture('pass/valid2.yaml'); $data = $this->obj->loadFile($path); @@ -67,15 +60,12 @@ public function testLoadFileWithYamlExtension() /** * Asserts that the File Loader throws an exception if the YAML Parser is unavailable. - * - * @runInSeparateProcess - * @preserveGlobalState disabled - * @covers ::loadYamlFile() - * @return void */ - public function testLoadFileWithNoYamlParser() + #[\PHPUnit\Framework\Attributes\PreserveGlobalState(false)] + #[\PHPUnit\Framework\Attributes\RunInSeparateProcess] + public function testLoadFileWithNoYamlParser(): void { - if (class_exists('Symfony\Component\Yaml\Parser', false)) { + if (class_exists(\Symfony\Component\Yaml\Parser::class, false)) { $this->markTestSkipped( 'The Symfony YAML component was loaded before the test could run' ); @@ -88,16 +78,13 @@ public function testLoadFileWithNoYamlParser() $this->disableSymfonyYamlComponent(); $path = $this->getPathToFixture('pass/valid1.yml'); - $data = $this->obj->loadFile($path); + $this->obj->loadFile($path); } /** * Asserts that an empty file is silently ignored. - * - * @covers ::loadYamlFile() - * @return void */ - public function testLoadEmptyFile() + public function testLoadEmptyFile(): void { $path = $this->getPathToFixture('pass/empty.yml'); $data = $this->obj->loadFile($path); @@ -107,25 +94,20 @@ public function testLoadEmptyFile() /** * Asserts that a broken file is NOT ignored. - * - * @covers ::loadYamlFile() - * @return void */ - public function testLoadMalformedFile() + public function testLoadMalformedFile(): void { $this->expectExceptionMessageMatches('/^YAML file ".+?" could not be parsed: .+$/'); $this->expectException(UnexpectedValueException::class); $path = $this->getPathToFixture('pass/malformed.yml'); - $data = $this->obj->loadFile($path); + $this->obj->loadFile($path); } /** * Remove the "symfony/yaml" package from Composer's search paths. - * - * @return void */ - public function disableSymfonyYamlComponent() + public function disableSymfonyYamlComponent(): void { // phpcs:disable Squiz.PHP.GlobalKeyword.NotAllowed global $autoloader; @@ -133,18 +115,16 @@ public function disableSymfonyYamlComponent() // If PSR-0/4 autoloading was optimized $classMap = $autoloader->getClassMap(); - if (isset($classMap['Symfony\\Component\\Yaml\\Parser'])) { + if (isset($classMap[\Symfony\Component\Yaml\Parser::class])) { $refClassMap = new ReflectionProperty($autoloader, 'classMap'); - $refClassMap->setAccessible(true); - unset($classMap['Symfony\\Component\\Yaml\\Parser']); + unset($classMap[\Symfony\Component\Yaml\Parser::class]); $refClassMap->setValue($autoloader, $classMap); } $prefixesPsr4 = $autoloader->getPrefixesPsr4(); if (isset($prefixesPsr4['Symfony\\Component\\Yaml\\'])) { $refPrefixesPsr4 = new ReflectionProperty($autoloader, 'prefixDirsPsr4'); - $refPrefixesPsr4->setAccessible(true); unset($prefixesPsr4['Symfony\\Component\\Yaml\\']); $refPrefixesPsr4->setValue($autoloader, $prefixesPsr4); @@ -153,10 +133,8 @@ public function disableSymfonyYamlComponent() /** * Add the "symfony/yaml" package from Composer's search paths. - * - * @return void */ - public function enableSymfonyYamlComponent() + public function enableSymfonyYamlComponent(): void { // phpcs:disable Squiz.PHP.GlobalKeyword.NotAllowed global $autoloader; @@ -164,27 +142,25 @@ public function enableSymfonyYamlComponent() // If PSR-0/4 autoloading was optimized $classMap = $autoloader->getClassMap(); - if (!isset($classMap['Symfony\\Component\\Yaml\\Parser'])) { + if (!isset($classMap[\Symfony\Component\Yaml\Parser::class])) { $refClassMap = new ReflectionProperty($autoloader, 'classMap'); - $refClassMap->setAccessible(true); $refClassLoader = $refClassMap->getDeclaringClass(); $classLoaderPath = $refClassLoader->getFileName(); - $vendorDir = dirname(dirname($classLoaderPath)); - $prefixesPsr4['Symfony\\Component\\Yaml\\Parser'] = [ $vendorDir.'/symfony/yaml/Parser.php' ]; + $vendorDir = dirname($classLoaderPath, 2); + $prefixesPsr4[\Symfony\Component\Yaml\Parser::class] = [ $vendorDir.'/symfony/yaml/Parser.php' ]; $refClassMap->setValue($autoloader, $prefixesPsr4); } $prefixesPsr4 = $autoloader->getPrefixesPsr4(); if (!isset($prefixesPsr4['Symfony\\Component\\Yaml\\'])) { $refPrefixesPsr4 = new ReflectionProperty($autoloader, 'prefixDirsPsr4'); - $refPrefixesPsr4->setAccessible(true); $refClassLoader = $refPrefixesPsr4->getDeclaringClass(); $classLoaderPath = $refClassLoader->getFileName(); - $vendorDir = dirname(dirname($classLoaderPath)); + $vendorDir = dirname($classLoaderPath, 2); $prefixesPsr4['Symfony\\Component\\Yaml\\'] = [ $vendorDir.'/symfony/yaml' ]; $refPrefixesPsr4->setValue($autoloader, $prefixesPsr4); } diff --git a/packages/config/tests/Charcoal/Config/Mixin/SeparatorAwareTest.php b/packages/config/tests/Charcoal/Config/Mixin/SeparatorAwareTest.php index 5bfce4750..75e343fe7 100644 --- a/packages/config/tests/Charcoal/Config/Mixin/SeparatorAwareTest.php +++ b/packages/config/tests/Charcoal/Config/Mixin/SeparatorAwareTest.php @@ -12,9 +12,13 @@ /** * Test SeparatorAwareTrait - * - * @coversDefaultClass \Charcoal\Config\SeparatorAwareTrait */ +#[\PHPUnit\Framework\Attributes\CoversTrait(\Charcoal\Config\SeparatorAwareTrait::class)] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\SeparatorAwareTrait::class, 'separator()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\SeparatorAwareTrait::class, 'setSeparator()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\SeparatorAwareTrait::class, 'hasWithSeparator()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\SeparatorAwareTrait::class, 'getWithSeparator()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Config\SeparatorAwareTrait::class, 'setWithSeparator()')] class SeparatorAwareTest extends AbstractTestCase { use AssertionsTrait; @@ -31,8 +35,6 @@ class SeparatorAwareTest extends AbstractTestCase /** * Create a TreeEntity instance. - * - * @return void */ protected function setUp(): void { @@ -66,20 +68,17 @@ protected function setUp(): void * Create a TreeEntity instance. * * @param array $data Data to pre-populate the object. - * @return TreeEntity */ - public function createObject(array $data = null) + public function createObject(?array $data = null): \Charcoal\Tests\Config\Mock\TreeEntity { return new TreeEntity($data); } /** * Asserts that the object implements SeparatorAwareInterface. - * - * @coversNothing - * @return void */ - public function testSeparatorAwareInterface() + #[\PHPUnit\Framework\Attributes\CoversNothing] + public function testSeparatorAwareInterface(): void { $this->assertInstanceOf(SeparatorAwareInterface::class, $this->obj); } @@ -88,21 +87,15 @@ public function testSeparatorAwareInterface() // Test Seperator Token // ========================================================================= - /** * Asserts that the separator is disabled by default. - * - * @covers ::separator() - * @return void */ - public function testDefaultSeparatorIsEmptyString() + public function testDefaultSeparatorIsEmptyString(): void { $this->assertEmpty($this->obj->separator()); } /** - * @covers ::setSeparator() - * @covers ::separator() * @return TreeEntity */ public function testSetSeparator() @@ -116,12 +109,7 @@ public function testSetSeparator() return $obj; } - /** - * @covers ::setSeparator() - * @covers ::separator() - * @return void - */ - public function testMutatedSeparator() + public function testMutatedSeparator(): void { $obj = $this->obj; @@ -132,12 +120,7 @@ public function testMutatedSeparator() ); } - /** - * @covers ::setSeparator() - * @covers ::separator() - * @return void - */ - public function testEmptySeparator() + public function testEmptySeparator(): void { $obj = $this->obj; @@ -145,11 +128,7 @@ public function testEmptySeparator() $this->assertEquals('', $obj->separator()); } - /** - * @covers ::setSeparator() - * @return void - */ - public function testSetSeparatorWithInvalidType() + public function testSetSeparatorWithInvalidType(): void { $this->expectExceptionMessage('Separator must be a string'); $this->expectException(InvalidArgumentException::class); @@ -157,11 +136,7 @@ public function testSetSeparatorWithInvalidType() $this->obj->setSeparator(1); } - /** - * @covers ::setSeparator() - * @return void - */ - public function testSetSeparatorWithInvalidToken() + public function testSetSeparatorWithInvalidToken(): void { $this->expectExceptionMessage('Separator must be one-character, or empty'); $this->expectException(InvalidArgumentException::class); @@ -173,87 +148,65 @@ public function testSetSeparatorWithInvalidToken() // Test HasWithSeparator // ========================================================================= - /** - * @covers ::hasWithSeparator() - * @depends testSetSeparator - * * @param SeparatorAwareInterface $obj The SeparatorAwareInterface implementation to test. - * @return void */ - public function testObjReturnsTrueOnHasEndKeyPath(SeparatorAwareInterface $obj) + #[\PHPUnit\Framework\Attributes\Depends('testSetSeparator')] + public function testObjReturnsTrueOnHasEndKeyPath(SeparatorAwareInterface $obj): void { $this->assertTrue($obj->hasWithSeparator('connections.default.driver')); } /** - * @covers ::hasWithSeparator() - * @depends testSetSeparator - * * @param SeparatorAwareInterface $obj The SeparatorAwareInterface implementation to test. - * @return void */ - public function testObjReturnsTrueOnHasMidKeyPath(SeparatorAwareInterface $obj) + #[\PHPUnit\Framework\Attributes\Depends('testSetSeparator')] + public function testObjReturnsTrueOnHasMidKeyPath(SeparatorAwareInterface $obj): void { $this->assertTrue($obj->hasWithSeparator('connections.default')); } /** - * @covers ::hasWithSeparator() - * @depends testSetSeparator - * * @param SeparatorAwareInterface $obj The SeparatorAwareInterface implementation to test. - * @return void */ - public function testObjReturnsTrueOnHasBaseKeyPath(SeparatorAwareInterface $obj) + #[\PHPUnit\Framework\Attributes\Depends('testSetSeparator')] + public function testObjReturnsTrueOnHasBaseKeyPath(SeparatorAwareInterface $obj): void { $this->assertTrue($obj->hasWithSeparator('connections')); } /** - * @covers ::hasWithSeparator() - * @depends testSetSeparator - * * @param SeparatorAwareInterface $obj The SeparatorAwareInterface implementation to test. - * @return void */ - public function testObjReturnsFalseOnHasEndKeyPathToNullValue(SeparatorAwareInterface $obj) + #[\PHPUnit\Framework\Attributes\Depends('testSetSeparator')] + public function testObjReturnsFalseOnHasEndKeyPathToNullValue(SeparatorAwareInterface $obj): void { $this->assertFalse($obj->hasWithSeparator('connections.customer.unix_socket')); } /** - * @covers ::hasWithSeparator() - * @depends testSetSeparator - * * @param SeparatorAwareInterface $obj The SeparatorAwareInterface implementation to test. - * @return void */ - public function testObjReturnsFalseOnHasNonexistentEndKeyPath(SeparatorAwareInterface $obj) + #[\PHPUnit\Framework\Attributes\Depends('testSetSeparator')] + public function testObjReturnsFalseOnHasNonexistentEndKeyPath(SeparatorAwareInterface $obj): void { $this->assertFalse($obj->hasWithSeparator('connections.default.server_version')); } /** - * @covers ::hasWithSeparator() - * @depends testSetSeparator - * * @param SeparatorAwareInterface $obj The SeparatorAwareInterface implementation to test. - * @return void */ - public function testObjReturnsFalseOnHasNonexistentMidKeyPath(SeparatorAwareInterface $obj) + #[\PHPUnit\Framework\Attributes\Depends('testSetSeparator')] + public function testObjReturnsFalseOnHasNonexistentMidKeyPath(SeparatorAwareInterface $obj): void { $this->assertFalse($obj->hasWithSeparator('connections.analytics.host')); } /** - * @covers ::hasWithSeparator() - * @depends testSetSeparator - * * @param SeparatorAwareInterface $obj The SeparatorAwareInterface implementation to test. - * @return void */ - public function testObjReturnsFalseOnHasNonexistentBaseKeyPath(SeparatorAwareInterface $obj) + #[\PHPUnit\Framework\Attributes\Depends('testSetSeparator')] + public function testObjReturnsFalseOnHasNonexistentBaseKeyPath(SeparatorAwareInterface $obj): void { $this->assertFalse($obj->hasWithSeparator('logging')); } @@ -261,20 +214,14 @@ public function testObjReturnsFalseOnHasNonexistentBaseKeyPath(SeparatorAwareInt /** * @used-by self::testHasWithSeparatorWithoutDelimiterInPhp7() * @used-by self::testHasWithSeparatorWithoutDelimiterInPhp5() - * - * @covers ::hasWithSeparator() - * @return void */ - public function delegatedTestHasWithSeparatorWithoutDelimiter() + public function delegatedTestHasWithSeparatorWithoutDelimiter(): void { $this->obj->hasWithSeparator('connections.default.host'); } - /** - * @requires PHP >= 7.0 - * @return void - */ - public function testHasWithSeparatorWithoutDelimiterInPhp7() + #[\PHPUnit\Framework\Attributes\RequiresPhp('>= 7.0')] + public function testHasWithSeparatorWithoutDelimiterInPhp7(): void { $this->expectError(); @@ -285,15 +232,11 @@ public function testHasWithSeparatorWithoutDelimiterInPhp7() // Test GetWithSeparator // ========================================================================= - /** - * @covers ::getWithSeparator() - * @depends testSetSeparator - * * @param SeparatorAwareInterface $obj The SeparatorAwareInterface implementation to test. - * @return void */ - public function testObjReturnsValueOnGetEndKeyPath(SeparatorAwareInterface $obj) + #[\PHPUnit\Framework\Attributes\Depends('testSetSeparator')] + public function testObjReturnsValueOnGetEndKeyPath(SeparatorAwareInterface $obj): void { $this->assertEquals( $this->connections['default']['driver'], @@ -302,13 +245,10 @@ public function testObjReturnsValueOnGetEndKeyPath(SeparatorAwareInterface $obj) } /** - * @covers ::getWithSeparator() - * @depends testSetSeparator - * * @param SeparatorAwareInterface $obj The SeparatorAwareInterface implementation to test. - * @return void */ - public function testObjReturnsValueOnGetMidKeyPath(SeparatorAwareInterface $obj) + #[\PHPUnit\Framework\Attributes\Depends('testSetSeparator')] + public function testObjReturnsValueOnGetMidKeyPath(SeparatorAwareInterface $obj): void { $this->assertEquals( $this->connections['default'], @@ -317,13 +257,10 @@ public function testObjReturnsValueOnGetMidKeyPath(SeparatorAwareInterface $obj) } /** - * @covers ::getWithSeparator() - * @depends testSetSeparator - * * @param SeparatorAwareInterface $obj The SeparatorAwareInterface implementation to test. - * @return void */ - public function testObjReturnsValueOnGetBaseKeyPath(SeparatorAwareInterface $obj) + #[\PHPUnit\Framework\Attributes\Depends('testSetSeparator')] + public function testObjReturnsValueOnGetBaseKeyPath(SeparatorAwareInterface $obj): void { $this->assertEquals( $this->connections, @@ -332,49 +269,37 @@ public function testObjReturnsValueOnGetBaseKeyPath(SeparatorAwareInterface $obj } /** - * @covers ::getWithSeparator() - * @depends testSetSeparator - * * @param SeparatorAwareInterface $obj The SeparatorAwareInterface implementation to test. - * @return void */ - public function testObjReturnsNullOnGetEndKeyPathToNullValue(SeparatorAwareInterface $obj) + #[\PHPUnit\Framework\Attributes\Depends('testSetSeparator')] + public function testObjReturnsNullOnGetEndKeyPathToNullValue(SeparatorAwareInterface $obj): void { $this->assertNull($obj->getWithSeparator('connections.customer.unix_socket')); } /** - * @covers ::getWithSeparator() - * @depends testSetSeparator - * * @param SeparatorAwareInterface $obj The SeparatorAwareInterface implementation to test. - * @return void */ - public function testObjReturnsNullOnGetNonexistentEndKeyPath(SeparatorAwareInterface $obj) + #[\PHPUnit\Framework\Attributes\Depends('testSetSeparator')] + public function testObjReturnsNullOnGetNonexistentEndKeyPath(SeparatorAwareInterface $obj): void { $this->assertNull($obj->getWithSeparator('connections.default.server_version')); } /** - * @covers ::getWithSeparator() - * @depends testSetSeparator - * * @param SeparatorAwareInterface $obj The SeparatorAwareInterface implementation to test. - * @return void */ - public function testObjReturnsNullOnGetNonexistentMidKeyPath(SeparatorAwareInterface $obj) + #[\PHPUnit\Framework\Attributes\Depends('testSetSeparator')] + public function testObjReturnsNullOnGetNonexistentMidKeyPath(SeparatorAwareInterface $obj): void { $this->assertNull($obj->getWithSeparator('connections.analytics.host')); } /** - * @covers ::getWithSeparator() - * @depends testSetSeparator - * * @param SeparatorAwareInterface $obj The SeparatorAwareInterface implementation to test. - * @return void */ - public function testObjReturnsNullOnGetNonexistentBaseKeyPath(SeparatorAwareInterface $obj) + #[\PHPUnit\Framework\Attributes\Depends('testSetSeparator')] + public function testObjReturnsNullOnGetNonexistentBaseKeyPath(SeparatorAwareInterface $obj): void { $this->assertNull($obj->getWithSeparator('logging')); } @@ -382,20 +307,14 @@ public function testObjReturnsNullOnGetNonexistentBaseKeyPath(SeparatorAwareInte /** * @used-by self::testGetWithSeparatorWithoutDelimiterInPhp7() * @used-by self::testGetWithSeparatorWithoutDelimiterInPhp5() - * - * @covers ::getWithSeparator() - * @return void */ - public function delegatedTestGetWithSeparatorWithoutDelimiter() + public function delegatedTestGetWithSeparatorWithoutDelimiter(): void { $this->obj->getWithSeparator('connections.default.host'); } - /** - * @requires PHP >= 7.0 - * @return void - */ - public function testGetWithSeparatorWithoutDelimiterInPhp7() + #[\PHPUnit\Framework\Attributes\RequiresPhp('>= 7.0')] + public function testGetWithSeparatorWithoutDelimiterInPhp7(): void { $this->expectError(); @@ -405,15 +324,11 @@ public function testGetWithSeparatorWithoutDelimiterInPhp7() // Test SetWithSeparator // ========================================================================= - /** - * @covers ::setWithSeparator() - * @depends testSetSeparator - * * @param SeparatorAwareInterface $obj The SeparatorAwareInterface implementation to test. - * @return void */ - public function testObjReplacesValueRecursivelyOnSetKeyPath(SeparatorAwareInterface $obj) + #[\PHPUnit\Framework\Attributes\Depends('testSetSeparator')] + public function testObjReplacesValueRecursivelyOnSetKeyPath(SeparatorAwareInterface $obj): void { $obj->setWithSeparator('keywords', [ 'php', 'framework', 'charcoal', 'config' ]); $obj->setWithSeparator('keywords', [ 1 => 'library', 4 => 'component' ]); @@ -430,78 +345,60 @@ public function testObjReplacesValueRecursivelyOnSetKeyPath(SeparatorAwareInterf } /** - * @covers ::setWithSeparator() - * @depends testSetSeparator - * * @param SeparatorAwareInterface $obj The SeparatorAwareInterface implementation to test. - * @return void */ - public function testObjReplacesValueOnSetEndKeyPath(SeparatorAwareInterface $obj) + #[\PHPUnit\Framework\Attributes\Depends('testSetSeparator')] + public function testObjReplacesValueOnSetEndKeyPath(SeparatorAwareInterface $obj): void { $obj->setWithSeparator('connections.default.driver', 'pdo_sqlite'); $this->assertEquals('pdo_sqlite', $obj->get('connections.default.driver')); } /** - * @covers ::setWithSeparator() - * @depends testSetSeparator - * * @param SeparatorAwareInterface $obj The SeparatorAwareInterface implementation to test. - * @return void */ - public function testObjReplacesValueOnSetMidKeyPath(SeparatorAwareInterface $obj) + #[\PHPUnit\Framework\Attributes\Depends('testSetSeparator')] + public function testObjReplacesValueOnSetMidKeyPath(SeparatorAwareInterface $obj): void { $obj->setWithSeparator('connections.default', [ 'dbname' => 'otherdatabase' ]); $this->assertEquals('otherdatabase', $obj->get('connections.default.dbname')); } /** - * @covers ::setWithSeparator() - * @depends testSetSeparator - * * @param SeparatorAwareInterface $obj The SeparatorAwareInterface implementation to test. - * @return void */ - public function testObjReplacesValueOnSetBaseKeyPath(SeparatorAwareInterface $obj) + #[\PHPUnit\Framework\Attributes\Depends('testSetSeparator')] + public function testObjReplacesValueOnSetBaseKeyPath(SeparatorAwareInterface $obj): void { $obj->setWithSeparator('connections', [ 'default' => [ 'host' => 'web.otherplace.tld' ] ]); $this->assertEquals('web.otherplace.tld', $obj->get('connections.default.host')); } /** - * @covers ::setWithSeparator() - * @depends testSetSeparator - * * @param SeparatorAwareInterface $obj The SeparatorAwareInterface implementation to test. - * @return void */ - public function testObjAddsValueOnSetNonexistentEndKeyPath(SeparatorAwareInterface $obj) + #[\PHPUnit\Framework\Attributes\Depends('testSetSeparator')] + public function testObjAddsValueOnSetNonexistentEndKeyPath(SeparatorAwareInterface $obj): void { $obj->setWithSeparator('connections.default.server_version', '5.7'); $this->assertEquals('5.7', $obj->get('connections.default.server_version')); } /** - * @covers ::setWithSeparator() - * @depends testSetSeparator - * * @param SeparatorAwareInterface $obj The SeparatorAwareInterface implementation to test. - * @return void */ - public function testObjAddsValueOnSetNonexistentMidKeyPath(SeparatorAwareInterface $obj) + #[\PHPUnit\Framework\Attributes\Depends('testSetSeparator')] + public function testObjAddsValueOnSetNonexistentMidKeyPath(SeparatorAwareInterface $obj): void { $obj->setWithSeparator('connections.analytics', [ 'driver' => 'pdo_pgsql' ]); $this->assertEquals('pdo_pgsql', $obj->get('connections.analytics.driver')); } /** - * @covers ::setWithSeparator() - * @depends testSetSeparator - * * @param SeparatorAwareInterface $obj The SeparatorAwareInterface implementation to test. - * @return void */ - public function testObjAddsValueOnSetNonexistentBaseKeyPath(SeparatorAwareInterface $obj) + #[\PHPUnit\Framework\Attributes\Depends('testSetSeparator')] + public function testObjAddsValueOnSetNonexistentBaseKeyPath(SeparatorAwareInterface $obj): void { $obj->setWithSeparator('logging', [ 'level' => 'debug' ]); $this->assertTrue($obj->has('logging.level')); @@ -514,20 +411,14 @@ public function testObjAddsValueOnSetNonexistentBaseKeyPath(SeparatorAwareInterf /** * @used-by self::testSetWithSeparatorWithoutDelimiterInPhp7() * @used-by self::testSetWithSeparatorWithoutDelimiterInPhp5() - * - * @covers ::setWithSeparator() - * @return void */ - public function delegatedTestSetWithSeparatorWithoutDelimiter() + public function delegatedTestSetWithSeparatorWithoutDelimiter(): void { $this->obj->setWithSeparator('connections.default.server_version', '5.7'); } - /** - * @requires PHP >= 7.0 - * @return void - */ - public function testSetWithSeparatorWithoutDelimiterInPhp7() + #[\PHPUnit\Framework\Attributes\RequiresPhp('>= 7.0')] + public function testSetWithSeparatorWithoutDelimiterInPhp7(): void { $this->expectError(); diff --git a/packages/config/tests/Charcoal/Config/Mock/ConfigurableObject.php b/packages/config/tests/Charcoal/Config/Mock/ConfigurableObject.php index 876aa3583..9d0d53df4 100644 --- a/packages/config/tests/Charcoal/Config/Mock/ConfigurableObject.php +++ b/packages/config/tests/Charcoal/Config/Mock/ConfigurableObject.php @@ -1,5 +1,7 @@ {$key}(); + } elseif (isset($this->{$key})) { + return $this->{$key}; } else { - if (isset($this->{$key})) { - return $this->{$key}; - } else { - return $this->getInDelegates($key); - } + return $this->getInDelegates($key); } } } diff --git a/packages/config/tests/Charcoal/Config/Mock/Entity.php b/packages/config/tests/Charcoal/Config/Mock/Entity.php index 7b84adfb9..d783597f8 100644 --- a/packages/config/tests/Charcoal/Config/Mock/Entity.php +++ b/packages/config/tests/Charcoal/Config/Mock/Entity.php @@ -1,5 +1,7 @@ setData($data); } } diff --git a/packages/config/tests/Charcoal/Config/Mock/FileLoader.php b/packages/config/tests/Charcoal/Config/Mock/FileLoader.php index 2da06b397..d6c07c419 100644 --- a/packages/config/tests/Charcoal/Config/Mock/FileLoader.php +++ b/packages/config/tests/Charcoal/Config/Mock/FileLoader.php @@ -1,5 +1,7 @@ -3, diff --git a/packages/config/tests/Charcoal/Config/Mock/MacroEntity.php b/packages/config/tests/Charcoal/Config/Mock/MacroEntity.php index e5b55cd60..24a5d29f8 100644 --- a/packages/config/tests/Charcoal/Config/Mock/MacroEntity.php +++ b/packages/config/tests/Charcoal/Config/Mock/MacroEntity.php @@ -1,5 +1,7 @@ foo; } diff --git a/packages/config/tests/Charcoal/Config/Mock/TreeEntity.php b/packages/config/tests/Charcoal/Config/Mock/TreeEntity.php index 7761a7bc6..b49aa22bf 100644 --- a/packages/config/tests/Charcoal/Config/Mock/TreeEntity.php +++ b/packages/config/tests/Charcoal/Config/Mock/TreeEntity.php @@ -26,7 +26,8 @@ class TreeEntity extends Entity implements SeparatorAwareInterface * @throws InvalidArgumentException If the $key is not a string or is a numeric value. * @return boolean TRUE if $key exists and has a value other than NULL, FALSE otherwise. */ - public function offsetExists($key) + #[\Override] + public function offsetExists($key): bool { if (is_numeric($key)) { throw new InvalidArgumentException( @@ -63,12 +64,7 @@ public function offsetExists($key) return ($this->{$key}() !== null); } // -- END DEPRECATED - - if (isset($this->{$key})) { - return true; - } - - return false; + return isset($this->{$key}); } /** @@ -78,7 +74,8 @@ public function offsetExists($key) * @throws InvalidArgumentException If the $key is not a string or is a numeric value. * @return mixed Value of the requested $key on success, NULL if the $key is not set. */ - public function offsetGet($key) + #[\Override] + public function offsetGet($key): mixed { if (is_numeric($key)) { throw new InvalidArgumentException( @@ -114,13 +111,8 @@ public function offsetGet($key) if ($this->mutatorCache[$key]) { return $this->{$key}(); } - // -- END DEPRECATED - - if (isset($this->{$key})) { - return $this->{$key}; - } - return null; + return $this->{$key} ?? null; } /** @@ -129,9 +121,9 @@ public function offsetGet($key) * @param string $key The data key to assign $value to. * @param mixed $value The data value to assign to $key. * @throws InvalidArgumentException If the $key is not a string or is a numeric value. - * @return void */ - public function offsetSet($key, $value) + #[\Override] + public function offsetSet($key, $value): void { if (is_numeric($key)) { throw new InvalidArgumentException( diff --git a/packages/config/tests/Charcoal/FixturesTrait.php b/packages/config/tests/Charcoal/FixturesTrait.php index d296ee5cf..7168625b3 100644 --- a/packages/config/tests/Charcoal/FixturesTrait.php +++ b/packages/config/tests/Charcoal/FixturesTrait.php @@ -13,7 +13,7 @@ trait FixturesTrait * @param string $file The file path relative to the Fixture directory. * @return string The file path to the fixture relative to the base directory. */ - public function getPathToFixture($file) + public function getPathToFixture($file): string { return __DIR__.'/../../tests/Charcoal/Config/Fixture/'.ltrim($file, '/'); } diff --git a/packages/config/tests/bootstrap.php b/packages/config/tests/bootstrap.php index a79f4b20d..3269fb5b7 100644 --- a/packages/config/tests/bootstrap.php +++ b/packages/config/tests/bootstrap.php @@ -1,5 +1,7 @@ factory = $factory; @@ -126,7 +125,7 @@ protected function factory() { if ($this->factory === null) { throw new RuntimeException( - sprintf('Model Factory is not defined for "%s"', get_class($this)) + sprintf('Model Factory is not defined for "%s"', static::class) ); } @@ -140,8 +139,7 @@ protected function factory() */ public function createModel() { - $obj = $this->factory()->create($this->modelClass()); - return $obj; + return $this->factory()->create($this->modelClass()); } /** @@ -152,17 +150,15 @@ public function createModel() */ protected function createModelFromData(array $data) { - $obj = $this->factory()->create($this->dynamicModelClass($data)); - return $obj; + return $this->factory()->create($this->dynamicModelClass($data)); } /** * Set the loader settings. * * @param array $data Data to assign to the loader. - * @return self */ - public function setData(array $data) + public function setData(array $data): static { foreach ($data as $key => $val) { $setter = $this->setter($key); @@ -195,9 +191,8 @@ public function source() * Set the source to load objects from. * * @param SourceInterface $source A data source. - * @return self */ - public function setSource(SourceInterface $source) + public function setSource(SourceInterface $source): static { $source->reset(); @@ -208,10 +203,8 @@ public function setSource(SourceInterface $source) /** * Reset everything but the model. - * - * @return self */ - public function reset() + public function reset(): static { if ($this->source) { $this->source()->reset(); @@ -240,12 +233,10 @@ public function model() /** * Determine if the loader has an object model. - * - * @return boolean */ - public function hasModel() + public function hasModel(): bool { - return !!$this->model; + return (bool) $this->model; } /** @@ -253,9 +244,8 @@ public function hasModel() * * @param string|ModelInterface $model An object model. * @throws InvalidArgumentException If the given argument is not a model. - * @return self */ - public function setModel($model) + public function setModel($model): static { if (is_string($model)) { $model = $this->factory()->get($model); @@ -279,12 +269,10 @@ public function setModel($model) /** * Retrieve the model class. - * - * @return string */ - public function modelClass() + public function modelClass(): string { - return get_class($this->model()); + return $this->model()::class; } /** @@ -313,20 +301,17 @@ public function dynamicTypeField() /** * Determine if the model has a dynamic object type. - * - * @return boolean */ - public function hasDynamicTypeField() + public function hasDynamicTypeField(): bool { - return !!$this->dynamicTypeField; + return (bool) $this->dynamicTypeField; } /** * @param string $field The field to use for dynamic object type. * @throws InvalidArgumentException If the field is not a string. - * @return self */ - public function setDynamicTypeField($field) + public function setDynamicTypeField($field): static { if (!is_string($field)) { throw new InvalidArgumentException( @@ -353,9 +338,8 @@ public function properties() * Alias of {@see SourceInterface::setProperties()} * * @param array $properties An array of property identifiers. - * @return self */ - public function setProperties(array $properties) + public function setProperties(array $properties): static { $this->source()->setProperties($properties); @@ -366,9 +350,8 @@ public function setProperties(array $properties) * Alias of {@see SourceInterface::addProperty()} * * @param string $property A property identifier. - * @return self */ - public function addProperty($property) + public function addProperty($property): static { $this->source()->addProperty($property); @@ -380,9 +363,8 @@ public function addProperty($property) * * @param array $keywords An array of keywords and properties. * Expected format: `[ "search query", [ "field names…" ] ]`. - * @return self */ - public function setKeywords(array $keywords) + public function setKeywords(array $keywords): static { foreach ($keywords as $query) { $keyword = $query[0]; @@ -398,9 +380,8 @@ public function setKeywords(array $keywords) * * @param string $keyword A value to match among $properties. * @param array $properties One or more of properties to search amongst. - * @return self */ - public function addKeyword($keyword, array $properties = null) + public function addKeyword(string $keyword, ?array $properties = null): static { if ($properties === null) { $properties = []; @@ -443,9 +424,8 @@ public function hasFilters() * Alias of {@see SourceInterface::setFilters()} * * @param array $filters An array of filters. - * @return self */ - public function setFilters(array $filters) + public function setFilters(array $filters): static { $this->source()->setFilters($filters); return $this; @@ -455,9 +435,8 @@ public function setFilters(array $filters) * Alias of {@see SourceInterface::addFilters()} * * @param array $filters An array of filters. - * @return self */ - public function addFilters(array $filters) + public function addFilters(array $filters): static { foreach ($filters as $f) { $this->addFilter($f); @@ -474,9 +453,8 @@ public function addFilters(array $filters) * @param mixed $value Optional value for the property to compare against. * Only used if the first argument is a string. * @param array $options Optional extra settings to apply on the filter. - * @return self */ - public function addFilter($param, $value = null, array $options = null) + public function addFilter($param, $value = null, ?array $options = null): static { $this->source()->addFilter($param, $value, $options); return $this; @@ -506,9 +484,8 @@ public function hasOrders() * Alias of {@see SourceInterface::setOrders()} * * @param array $orders An array of orders. - * @return self */ - public function setOrders(array $orders) + public function setOrders(array $orders): static { $this->source()->setOrders($orders); return $this; @@ -518,9 +495,8 @@ public function setOrders(array $orders) * Alias of {@see SourceInterface::addOrders()} * * @param array $orders An array of orders. - * @return self */ - public function addOrders(array $orders) + public function addOrders(array $orders): static { foreach ($orders as $o) { $this->addOrder($o); @@ -537,9 +513,8 @@ public function addOrders(array $orders) * @param string $mode Optional sorting mode. * Defaults to ascending if a property is provided. * @param array $options Optional extra settings to apply on the order. - * @return self */ - public function addOrder($param, $mode = 'asc', array $options = null) + public function addOrder($param, $mode = 'asc', ?array $options = null): static { $this->source()->addOrder($param, $mode, $options); return $this; @@ -559,9 +534,8 @@ public function pagination() * Alias of {@see SourceInterface::setPagination()} * * @param mixed $param An associative array of pagination settings. - * @return self */ - public function setPagination($param) + public function setPagination($param): static { $this->source()->setPagination($param); @@ -582,9 +556,8 @@ public function page() * Alias of {@see PaginationInterface::pagination()} * * @param integer $page A page number. - * @return self */ - public function setPage($page) + public function setPage($page): static { $this->pagination()->setPage($page); @@ -605,9 +578,8 @@ public function numPerPage() * Alias of {@see PaginationInterface::setNumPerPage()} * * @param integer $num The number of items to display per page. - * @return self */ - public function setNumPerPage($num) + public function setNumPerPage($num): static { $this->pagination()->setNumPerPage($num); @@ -618,9 +590,8 @@ public function setNumPerPage($num) * Set the callback routine applied to every object added to the collection. * * @param callable $callback The callback routine. - * @return self */ - public function setCallback(callable $callback) + public function setCallback(callable $callback): static { $this->callback = $callback; @@ -647,7 +618,7 @@ public function callback() * @throws Exception If the database connection fails. * @return ModelInterface[]|ArrayAccess */ - public function load($ident = null, callable $callback = null, callable $before = null) + public function load($ident = null, ?callable $callback = null, ?callable $before = null): \ArrayAccess|array { // Unused. unset($ident); @@ -661,9 +632,8 @@ public function load($ident = null, callable $callback = null, callable $before * Get the total number of items for this collection query. * * @throws RuntimeException If the database connection fails. - * @return integer */ - public function loadCount() + public function loadCount(): int { $query = $this->source()->sqlLoadCount(); @@ -707,7 +677,7 @@ public function loadCount() * @throws InvalidArgumentException If the SQL string/set is invalid. * @return ModelInterface[]|ArrayAccess */ - public function loadFromQuery($query, callable $callback = null, callable $before = null) + public function loadFromQuery($query, ?callable $callback = null, ?callable $before = null): \ArrayAccess|array { $db = $this->source()->db(); @@ -723,14 +693,14 @@ public function loadFromQuery($query, callable $callback = null, callable $befor $sth = $db->prepare($query); $sth->execute(); } elseif (is_array($query)) { - list($query, $binds, $types) = array_pad($query, 3, []); + [$query, $binds, $types] = array_pad($query, 3, []); $sth = $this->source()->dbQuery($query, $binds, $types); } else { throw new InvalidArgumentException(sprintf( 'The SQL query must be a string or an array: ' . '[ string $query, array $binds, array $dataTypes ]; ' . 'received %s', - is_object($query) ? get_class($query) : $query + is_object($query) ? $query::class : $query )); } @@ -751,7 +721,7 @@ public function loadFromQuery($query, callable $callback = null, callable $befor * @param callable|null $after Process each entity after applying raw data. * @return ModelInterface[]|ArrayAccess */ - protected function processCollection($results, callable $before = null, callable $after = null) + protected function processCollection($results, ?callable $before = null, ?callable $after = null): \ArrayAccess|array { $collection = $this->createCollection(); foreach ($results as $objData) { @@ -773,7 +743,7 @@ protected function processCollection($results, callable $before = null, callable * @param callable|null $after Process each entity after applying raw data. * @return ModelInterface|ArrayAccess|null */ - protected function processModel($objData, callable $before = null, callable $after = null) + protected function processModel(array $objData, ?callable $before = null, ?callable $after = null) { $obj = $this->createModelFromData($objData); @@ -794,9 +764,8 @@ protected function processModel($objData, callable $before = null, callable $aft * Create a collection class or array. * * @throws RuntimeException If the collection class is invalid. - * @return array|ArrayAccess */ - public function createCollection() + public function createCollection(): array|\ArrayAccess { $collectClass = $this->collectionClass(); if ($collectClass === 'array') { @@ -817,9 +786,7 @@ public function createCollection() )); } - $collection = new $collectClass(); - - return $collection; + return new $collectClass(); } /** @@ -827,9 +794,8 @@ public function createCollection() * * @param string $className The class name of the collection. * @throws InvalidArgumentException If the class name is not a string. - * @return self */ - public function setCollectionClass($className) + public function setCollectionClass($className): static { if (!is_string($className)) { throw new InvalidArgumentException( @@ -858,7 +824,7 @@ public function collectionClass() * @param string $key The key to get the getter from. * @return string The getter method name, for a given key. */ - protected function getter($key) + protected function getter($key): string { $getter = $key; return $this->camelize($getter); @@ -870,7 +836,7 @@ protected function getter($key) * @param string $key The key to get the setter from. * @return string The setter method name, for a given key. */ - protected function setter($key) + protected function setter(string $key): string { $setter = 'set_' . $key; return $this->camelize($setter); @@ -882,8 +848,8 @@ protected function setter($key) * @param string $str The snake_case string to camelize. * @return string The camelcase'd string. */ - protected function camelize($str) + protected function camelize($str): string { - return lcfirst(implode('', array_map('ucfirst', explode('_', $str)))); + return lcfirst(implode('', array_map(ucfirst(...), explode('_', $str)))); } } diff --git a/packages/core/src/Charcoal/Loader/CollectionLoaderAwareTrait.php b/packages/core/src/Charcoal/Loader/CollectionLoaderAwareTrait.php index 770f9d5fd..02d7b6ac4 100644 --- a/packages/core/src/Charcoal/Loader/CollectionLoaderAwareTrait.php +++ b/packages/core/src/Charcoal/Loader/CollectionLoaderAwareTrait.php @@ -42,7 +42,7 @@ public function collectionLoader() if (!isset($this->collectionLoader)) { throw new RuntimeException(sprintf( 'Collection Loader is not defined for [%s]', - get_class($this) + $this::class )); } diff --git a/packages/core/src/Charcoal/Loader/CollectionLoaderFactoryTrait.php b/packages/core/src/Charcoal/Loader/CollectionLoaderFactoryTrait.php index f256bd609..0643e1d6d 100644 --- a/packages/core/src/Charcoal/Loader/CollectionLoaderFactoryTrait.php +++ b/packages/core/src/Charcoal/Loader/CollectionLoaderFactoryTrait.php @@ -42,7 +42,7 @@ public function collectionLoaderFactory() if (!isset($this->collectionLoaderFactory)) { throw new RuntimeException(sprintf( 'Collection Loader Factory is not defined for [%s]', - get_class($this) + $this::class )); } @@ -56,7 +56,7 @@ public function collectionLoaderFactory() * @param callable|null $callback Optional. Called at creation. * @return CollectionLoader */ - public function createCollectionLoaderWith(array $args = null, callable $callback = null) + public function createCollectionLoaderWith(?array $args = null, ?callable $callback = null) { $factory = $this->collectionLoaderFactory(); diff --git a/packages/core/src/Charcoal/Loader/LazyCollectionLoader.php b/packages/core/src/Charcoal/Loader/LazyCollectionLoader.php index caa54cba7..f6696db75 100644 --- a/packages/core/src/Charcoal/Loader/LazyCollectionLoader.php +++ b/packages/core/src/Charcoal/Loader/LazyCollectionLoader.php @@ -1,5 +1,7 @@ processModel($objData, $before, $after); diff --git a/packages/core/src/Charcoal/Model/AbstractModel.php b/packages/core/src/Charcoal/Model/AbstractModel.php index 15c4e88c4..1570b0c0c 100644 --- a/packages/core/src/Charcoal/Model/AbstractModel.php +++ b/packages/core/src/Charcoal/Model/AbstractModel.php @@ -65,7 +65,7 @@ abstract class AbstractModel extends AbstractEntity implements /** * @param array $data Dependencies. */ - public function __construct(array $data = null) + public function __construct(?array $data = null) { // LoggerAwareInterface dependencies $this->setLogger($data['logger']); @@ -107,7 +107,8 @@ public function __construct(array $data = null) * for retrieving a subset of data. * @return array */ - public function data(array $properties = null) + #[\Override] + public function data(?array $properties = null) { $data = []; $properties = $this->properties($properties); @@ -128,6 +129,7 @@ public function data(array $properties = null) * @return self * @see AbstractEntity::setData() */ + #[\Override] public function setData(array $data) { $data = $this->setIdFromData($data); @@ -183,11 +185,7 @@ public function mergeData(array $data) $property = $this->p($propIdent); if ($property['l10n'] && is_array($val)) { $currentValue = json_decode(json_encode($this[$propIdent]), true); - if (is_array($currentValue)) { - $this[$propIdent] = array_merge($currentValue, $val); - } else { - $this[$propIdent] = $val; - } + $this[$propIdent] = is_array($currentValue) ? array_merge($currentValue, $val) : $val; } else { $this[$propIdent] = $val; } @@ -238,20 +236,18 @@ public function setFlatData(array $flatData) public function setPropertyDataFromFlatData(array $flatData) { $flatData = $this->setIdFromData($flatData); - - $propData = []; $properties = $this->properties(); foreach ($properties as $propertyIdent => $property) { $fieldValues = []; $fieldNames = $property->fieldNames(); foreach ($fieldNames as $fieldName) { - if (array_key_exists($fieldName, $flatData)) { + if (array_key_exists((string) $fieldName, $flatData)) { $fieldValues[$fieldName] = $flatData[$fieldName]; unset($flatData[$fieldName]); } } - if ($fieldValues) { + if ($fieldValues !== []) { $this[$propertyIdent] = $property->parseFromFlatData($fieldValues); } } @@ -268,7 +264,7 @@ public function setPropertyDataFromFlatData(array $flatData) * for retrieving a subset of data. * @return array */ - public function flatData(array $properties = null) + public function flatData(?array $properties = null) { $flatData = []; $properties = $this->properties($properties); @@ -299,7 +295,7 @@ public function propertyValue($propertyIdent) * @param array $properties Optional array of properties to save. If null, use all object's properties. * @return boolean */ - public function saveProperties(array $properties = null) + public function saveProperties(?array $properties = null) { if ($properties === null) { $properties = array_keys($this->metadata()->properties()); @@ -335,7 +331,7 @@ public function saveProperties(array $properties = null) * @throws PDOException If the PDO query fails. * @return string The matching language. */ - public function loadFromL10n($key, $value, array $langs) + public function loadFromL10n(string $key, $value, array $langs) { $binds = [ 'ident' => $value, @@ -366,9 +362,9 @@ public function loadFromL10n($key, $value, array $langs) if ($sth === false) { throw new PDOException(sprintf( 'Could not load model [%s] for localized column "%s" [%s]', - get_class($this), + static::class, $fieldName, - (is_object($value) ? get_class($value) : (is_string($value) ? $value : gettype($value))) + (is_object($value) ? $value::class : (is_string($value) ? $value : gettype($value))) )); } @@ -376,9 +372,9 @@ public function loadFromL10n($key, $value, array $langs) if (!$data || !isset($data['_lang'])) { throw new PDOException(sprintf( 'Unable to retrieve model [%s] data for localized column "%s" [%s]', - get_class($this), + static::class, $fieldName, - (is_object($value) ? get_class($value) : (is_string($value) ? $value : gettype($value))) + (is_object($value) ? $value::class : (is_string($value) ? $value : gettype($value))) )); } @@ -446,7 +442,7 @@ protected function preSave() * @see StorableTrait::preUpdate() * @return boolean */ - protected function preUpdate(array $properties = null) + protected function preUpdate(?array $properties = null) { return $this->saveProperties($properties); } @@ -488,11 +484,11 @@ protected function createSource() if (!$sourceConfig) { throw new UnexpectedValueException(sprintf( 'Can not create source for model [%s]: Invalid metadata (can not load source\'s configuration)', - get_class($this) + static::class )); } - $type = isset($sourceConfig['type']) ? $sourceConfig['type'] : self::DEFAULT_SOURCE_TYPE; + $type = $sourceConfig['type'] ?? self::DEFAULT_SOURCE_TYPE; $source = $this->sourceFactory()->create($type); $source->setModel($this); @@ -508,8 +504,7 @@ protected function createSource() */ protected function createValidator() { - $validator = new ModelValidator($this); - return $validator; + return new ModelValidator($this); } /** @@ -533,9 +528,8 @@ protected function setDependencies(Container $container) */ public static function objType() { - $class = get_called_class(); + $class = static::class; $ident = preg_replace('/([a-z])([A-Z])/', '$1-$2', $class); - $ident = strtolower(str_replace('\\', '/', $ident)); - return $ident; + return strtolower(str_replace('\\', '/', $ident)); } } diff --git a/packages/core/src/Charcoal/Model/Collection.php b/packages/core/src/Charcoal/Model/Collection.php index f05a22173..654228097 100644 --- a/packages/core/src/Charcoal/Model/Collection.php +++ b/packages/core/src/Charcoal/Model/Collection.php @@ -40,7 +40,6 @@ class Collection implements CollectionInterface * Create a new collection. * * @param array|Traversable|null $objs Array of objects to pre-populate this collection. - * @return void */ public function __construct($objs = null) { @@ -54,7 +53,7 @@ public function __construct($objs = null) * * @return object|null Returns the first object, or NULL if the collection is empty. */ - public function first() + public function first(): ?object { if (empty($this->objects)) { return null; @@ -68,7 +67,7 @@ public function first() * * @return object|null Returns the last object, or NULL if the collection is empty. */ - public function last() + public function last(): ?object { if (empty($this->objects)) { return null; @@ -79,15 +78,13 @@ public function last() // Satisfies CollectionInterface // ============================================================================================= - /** * Merge the collection with the given objects. * * @param array|Traversable $objs Array of objects to append to this collection. * @throws InvalidArgumentException If the given array contains an unacceptable value. - * @return self */ - public function merge($objs) + public function merge($objs): static { $objs = $this->asArray($objs); @@ -96,7 +93,7 @@ public function merge($objs) throw new InvalidArgumentException( sprintf( 'Must be an array of models, contains %s', - (is_object($obj) ? get_class($obj) : gettype($obj)) + (get_debug_type($obj)) ) ); } @@ -113,15 +110,14 @@ public function merge($objs) * * @param object $obj An acceptable object. * @throws InvalidArgumentException If the given object is not acceptable. - * @return self */ - public function add($obj) + public function add($obj): static { if (!$this->isAcceptable($obj)) { throw new InvalidArgumentException( sprintf( 'Must be a model, received %s', - (is_object($obj) ? get_class($obj) : gettype($obj)) + (get_debug_type($obj)) ) ); } @@ -155,15 +151,14 @@ public function get($key) * Determine if an object exists in the collection by key. * * @param mixed $key The primary key to lookup. - * @return boolean */ - public function has($key) + public function has($key): bool { if ($this->isAcceptable($key)) { $key = $this->modelKey($key); } - return array_key_exists($key, $this->objects); + return array_key_exists((string) $key, $this->objects); } /** @@ -171,9 +166,8 @@ public function has($key) * * @param mixed $key The object primary key to remove. * @throws InvalidArgumentException If the given key is not acceptable. - * @return self */ - public function remove($key) + public function remove($key): static { if ($this->isAcceptable($key)) { $key = $this->modelKey($key); @@ -186,10 +180,8 @@ public function remove($key) /** * Remove all objects from collection. - * - * @return self */ - public function clear() + public function clear(): static { $this->objects = []; @@ -211,7 +203,7 @@ public function all() * * @return object[] A sequential array of objects. */ - public function values() + public function values(): array { return array_values($this->objects); } @@ -221,7 +213,7 @@ public function values() * * @return array A sequential array of keys. */ - public function keys() + public function keys(): array { return array_keys($this->objects); } @@ -275,9 +267,8 @@ public function offsetGet($offset) * @param mixed $offset The object primary key or array offset. * @param mixed $value The object. * @throws LogicException Attempts to assign an offset. - * @return void */ - public function offsetSet($offset, $value) + public function offsetSet($offset, $value): void { if ($offset === null) { $this->add($value); @@ -293,9 +284,8 @@ public function offsetSet($offset, $value) * * @see \ArrayAccess * @param mixed $offset The object primary key or array offset. - * @return void */ - public function offsetUnset($offset) + public function offsetUnset($offset): void { if (is_int($offset)) { $offset = $this->resolveOffset($offset); @@ -319,10 +309,8 @@ public function offsetUnset($offset) */ protected function resolveOffset($offset) { - if (is_int($offset)) { - if ($offset < 0) { - $offset = ($this->count() - abs($offset)); - } + if (is_int($offset) && $offset < 0) { + $offset = ($this->count() - abs($offset)); } return $offset; @@ -360,24 +348,22 @@ public function getIterator() * Retrieve a cached iterator. * * @param integer $flags Bitmask of flags. - * @return \CachingIterator */ - public function getCachingIterator($flags = CachingIterator::CALL_TOSTRING) + public function getCachingIterator($flags = CachingIterator::CALL_TOSTRING): \CachingIterator { return new CachingIterator($this->getIterator(), $flags); } // Satisfies backwards-compatibility // ============================================================================================= - /** * Retrieve the array offset from the given key. * - * @deprecated * @param mixed $key The primary key to retrieve the offset from. * @return integer Returns an array offset. */ - public function pos($key) + #[\Deprecated] + public function pos($key): int|false { trigger_error('Collection::pos() is deprecated', E_USER_DEPRECATED); @@ -387,11 +373,11 @@ public function pos($key) /** * Alias of {@see self::values()} * - * @deprecated * @todo Trigger deprecation error. * @return object[] */ - public function objects() + #[\Deprecated] + public function objects(): array { return $this->values(); } @@ -399,26 +385,24 @@ public function objects() /** * Alias of {@see self::all()}. * - * @deprecated * @todo Trigger deprecation error. * @return object[] */ + #[\Deprecated] public function map() { return $this->all(); } // ============================================================================================= - /** * Determine if the given value is acceptable for the collection. * * Note: Practical for specialized collections extending the base collection. * * @param mixed $value The value being vetted. - * @return boolean */ - public function isAcceptable($value) + public function isAcceptable($value): bool { return ($value instanceof ModelInterface); } @@ -438,7 +422,7 @@ protected function modelKey($obj) throw new InvalidArgumentException( sprintf( 'Must be a model, received %s', - (is_object($obj) ? get_class($obj) : gettype($obj)) + (get_debug_type($obj)) ) ); } @@ -448,10 +432,8 @@ protected function modelKey($obj) /** * Determine if the collection is empty or not. - * - * @return boolean */ - public function isEmpty() + public function isEmpty(): bool { return empty($this->objects); } @@ -460,10 +442,8 @@ public function isEmpty() * Get a base collection instance from this collection. * * Note: Practical for extended classes. - * - * @return Collection */ - public function toBase() + public function toBase(): self { return new self($this); } diff --git a/packages/core/src/Charcoal/Model/CollectionInterface.php b/packages/core/src/Charcoal/Model/CollectionInterface.php index 245394c30..19b538c4f 100644 --- a/packages/core/src/Charcoal/Model/CollectionInterface.php +++ b/packages/core/src/Charcoal/Model/CollectionInterface.php @@ -1,5 +1,7 @@ metadataLoader) { throw new RuntimeException( - sprintf('Metadata loader was not set for "%s"', get_class($this)) + sprintf('Metadata loader was not set for "%s"', $this::class) ); } return $this->metadataLoader; @@ -160,13 +160,10 @@ protected function metadataLoader() * Generate a metadata identifier from this object's class name (FQN). * * Converts the short class name and converts it from camelCase to kebab-case. - * - * @return string */ - protected function generateMetadataIdent() + protected function generateMetadataIdent(): string { $ident = preg_replace('/([a-z])([A-Z])/', '$1-$2', static::class); - $ident = strtolower(str_replace('\\', '/', $ident)); - return $ident; + return strtolower(str_replace('\\', '/', $ident)); } } diff --git a/packages/core/src/Charcoal/Model/MetadataInterface.php b/packages/core/src/Charcoal/Model/MetadataInterface.php index 9f60dde0c..6e2c973f9 100644 --- a/packages/core/src/Charcoal/Model/MetadataInterface.php +++ b/packages/core/src/Charcoal/Model/MetadataInterface.php @@ -1,5 +1,7 @@ metadataLoader)) { throw new RuntimeException(sprintf( 'Metadata Loader is not defined for [%s]', - get_class($this) + $this::class )); } @@ -58,9 +58,8 @@ public function metadataLoader() protected function loadMetadata($metadataIdent) { $metadataLoader = $this->metadataLoader(); - $metadata = $metadataLoader->load($metadataIdent, $this->createMetadata()); - return $metadata; + return $metadataLoader->load($metadataIdent, $this->createMetadata()); } /** diff --git a/packages/core/src/Charcoal/Model/Model.php b/packages/core/src/Charcoal/Model/Model.php index 59dc94c17..02aa694be 100644 --- a/packages/core/src/Charcoal/Model/Model.php +++ b/packages/core/src/Charcoal/Model/Model.php @@ -1,12 +1,13 @@ */ - namespace Charcoal\Model; // From 'charcoal-core' diff --git a/packages/core/src/Charcoal/Model/ModelFactoryTrait.php b/packages/core/src/Charcoal/Model/ModelFactoryTrait.php index 78251ce21..7d0b6ef2f 100644 --- a/packages/core/src/Charcoal/Model/ModelFactoryTrait.php +++ b/packages/core/src/Charcoal/Model/ModelFactoryTrait.php @@ -43,7 +43,7 @@ public function modelFactory() if (!isset($this->modelFactory)) { throw new RuntimeException(sprintf( 'Model Factory is not defined for [%s]', - get_class($this) + $this::class )); } diff --git a/packages/core/src/Charcoal/Model/ModelInterface.php b/packages/core/src/Charcoal/Model/ModelInterface.php index 01c133a7f..f4b60f818 100644 --- a/packages/core/src/Charcoal/Model/ModelInterface.php +++ b/packages/core/src/Charcoal/Model/ModelInterface.php @@ -1,5 +1,7 @@ modelLoaderBuilder)) { throw new RuntimeException(sprintf( 'Model Factory is not defined for [%s]', - get_class($this) + $this::class )); } @@ -72,7 +72,7 @@ protected function modelLoader($objType, $objKey = null) if (!is_string($objType)) { throw new InvalidArgumentException(sprintf( 'The object type must be a string, received %s', - is_object($objType) ? get_class($objType) : gettype($objType) + get_debug_type($objType) )); } @@ -82,7 +82,7 @@ protected function modelLoader($objType, $objKey = null) } elseif (!is_string($key)) { throw new InvalidArgumentException(sprintf( 'The object property key must be a string, received %s', - is_object($key) ? get_class($key) : gettype($key) + get_debug_type($key) )); } diff --git a/packages/core/src/Charcoal/Model/ModelMetadata.php b/packages/core/src/Charcoal/Model/ModelMetadata.php index 696a22f1a..64e72e2af 100644 --- a/packages/core/src/Charcoal/Model/ModelMetadata.php +++ b/packages/core/src/Charcoal/Model/ModelMetadata.php @@ -13,10 +13,8 @@ class ModelMetadata extends AbstractMetadata { /** * The metadata identifier. - * - * @var string */ - private $ident; + private ?string $ident = null; /** * The model's sources. @@ -27,26 +25,23 @@ class ModelMetadata extends AbstractMetadata /** * The model's default source. - * - * @var string */ - private $defaultSource; + private ?string $defaultSource = null; /** * Set the metadata identifier. * * @param string $ident The metadata identifier. * @throws InvalidArgumentException If identifier is not a string. - * @return self */ - public function setIdent($ident) + public function setIdent($ident): static { if (!is_string($ident)) { throw new InvalidArgumentException( sprintf( '[%s] Identifier must be a string; received %s', - get_called_class(), - (is_object($ident) ? get_class($ident) : gettype($ident)) + static::class, + (get_debug_type($ident)) ) ); } @@ -61,16 +56,15 @@ public function setIdent($ident) * * @return string */ - public function ident() + public function ident(): ?string { return $this->ident; } /** * @param array $sources The available sources for this model. - * @return self */ - public function setSources(array $sources) + public function setSources(array $sources): static { foreach ($sources as $sourceIdent => $source) { $this->addSource($sourceIdent, $source); @@ -89,9 +83,8 @@ public function sources() /** * @param string $sourceIdent The source identifier. * @param mixed $source The source data. - * @return self */ - public function addSource($sourceIdent, $source) + public function addSource($sourceIdent, $source): static { $this->sources[$sourceIdent] = $source; return $this; @@ -113,9 +106,8 @@ public function source($sourceIdent) /** * @param string $defaultSource The default source identifier. * @throws InvalidArgumentException If the argument is not a string. - * @return self */ - public function setDefaultSource($defaultSource) + public function setDefaultSource($defaultSource): static { if (!is_string($defaultSource)) { throw new InvalidArgumentException( @@ -129,7 +121,7 @@ public function setDefaultSource($defaultSource) /** * @return string */ - public function defaultSource() + public function defaultSource(): ?string { return $this->defaultSource; } diff --git a/packages/core/src/Charcoal/Model/Service/MetadataConfig.php b/packages/core/src/Charcoal/Model/Service/MetadataConfig.php index 446dc61bf..bc552a316 100644 --- a/packages/core/src/Charcoal/Model/Service/MetadataConfig.php +++ b/packages/core/src/Charcoal/Model/Service/MetadataConfig.php @@ -15,17 +15,13 @@ class MetadataConfig extends AbstractConfig { /** * Metadata search paths. - * - * @var array */ - private $paths = []; + private array $paths = []; /** * The PSR-6 caching service or cache identifier(s) to use. - * - * @var mixed */ - private $cache = true; + private array|bool|null|object $cache = true; /** * Retrieve the default values. @@ -34,7 +30,8 @@ class MetadataConfig extends AbstractConfig * @return mixed An associative array if $key is NULL. * If $key is specified, the value of that data key if it exists, NULL on failure. */ - public function defaults($key = null) + #[\Override] + public function defaults($key = null): array|true|null { $data = [ 'paths' => [], @@ -42,7 +39,7 @@ public function defaults($key = null) ]; if ($key) { - return isset($data[$key]) ? $data[$key] : null; + return $data[$key] ?? null; } return $data; @@ -53,9 +50,9 @@ public function defaults($key = null) * * @see \Charcoal\Config\AbstractConfig::merge() * @param array|Traversable $data The data to merge. - * @return self */ - public function merge($data) + #[\Override] + public function merge($data): static { foreach ($data as $key => $val) { if ($key === 'paths') { @@ -70,19 +67,15 @@ public function merge($data) return $this; } - /** - * @return array - */ - public function paths() + public function paths(): array { return $this->paths; } /** * @param string[] $paths One or more search paths. - * @return self */ - public function setPaths(array $paths) + public function setPaths(array $paths): static { $this->paths = []; $this->addPaths($paths); @@ -91,9 +84,8 @@ public function setPaths(array $paths) /** * @param string[] $paths One or more search paths. - * @return self */ - public function addPaths(array $paths) + public function addPaths(array $paths): static { foreach ($paths as $path) { $this->addPath($path); @@ -104,9 +96,8 @@ public function addPaths(array $paths) /** * @param string $path A directory path. * @throws InvalidArgumentException If the path is not a string. - * @return self */ - public function addPath($path) + public function addPath($path): static { if (!is_string($path)) { throw new InvalidArgumentException( @@ -117,12 +108,9 @@ public function addPath($path) return $this; } - /** - * @return mixed - */ - public function cache() + public function cache(): bool|object|array { - return isset($this->cache) ? $this->cache : false; + return $this->cache ?? false; } /** @@ -137,9 +125,8 @@ public function cache() * - a {@see \Psr\Cache\CacheItemPoolInterface PSR-6 caching service}, * that instance will be used by the {@see \Charcoal\Model\Service\MetadataLoader}. * @throws InvalidArgumentException If the cache option is invalid. - * @return self */ - public function setCache($cache) + public function setCache($cache): static { if ($cache === null) { $this->cache = $this->defaults('cache'); diff --git a/packages/core/src/Charcoal/Model/Service/MetadataLoader.php b/packages/core/src/Charcoal/Model/Service/MetadataLoader.php index e424665ff..ced975da8 100644 --- a/packages/core/src/Charcoal/Model/Service/MetadataLoader.php +++ b/packages/core/src/Charcoal/Model/Service/MetadataLoader.php @@ -29,10 +29,8 @@ final class MetadataLoader implements LoggerAwareInterface /** * The PSR-6 caching service. - * - * @var CacheItemPoolInterface */ - private $cachePool; + private \Psr\Cache\CacheItemPoolInterface $cachePool; /** * The cache of metadata instances, indexed by metadata identifier. @@ -43,10 +41,8 @@ final class MetadataLoader implements LoggerAwareInterface /** * The cache of class/interface lineages. - * - * @var array */ - private static $lineageCache = []; + private static array $lineageCache = []; /** * The cache of snake-cased words. @@ -57,24 +53,18 @@ final class MetadataLoader implements LoggerAwareInterface /** * The cache of camel-cased words. - * - * @var array */ - private static $camelCache = []; + private static array $camelCache = []; /** * The base path to prepend to any relative paths to search in. - * - * @var string */ - private $basePath = ''; + private string $basePath = ''; /** * The paths to search in. - * - * @var array */ - private $paths = []; + private array $paths = []; /** * Return new MetadataLoader object. @@ -89,9 +79,8 @@ final class MetadataLoader implements LoggerAwareInterface * - `base_path` * * @param array $data The loader's dependencies. - * @return void */ - public function __construct(array $data = null) + public function __construct(?array $data = null) { $this->setLogger($data['logger']); $this->setCachePool($data['cache']); @@ -121,16 +110,16 @@ public function __construct(array $data = null) * as an array or an instance of {@see MetadataInterface}. * See $metadata for more details. */ - public function load($ident, $metadata = [], array $idents = null) + public function load($ident, $metadata = [], ?array $idents = null) { if (!is_string($ident)) { throw new InvalidArgumentException(sprintf( 'Metadata identifier must be a string, received %s', - is_object($ident) ? get_class($ident) : gettype($ident) + get_debug_type($ident) )); } - if (strpos($ident, '\\') !== false) { + if (str_contains($ident, '\\')) { $ident = $this->metaKeyFromClassName($ident); } @@ -139,12 +128,12 @@ public function load($ident, $metadata = [], array $idents = null) throw new InvalidArgumentException(sprintf( 'Metadata object must be a class name or instance of %s, received %s', MetadataInterface::class, - is_object($metadata) ? get_class($metadata) : gettype($metadata) + get_debug_type($metadata) )); } - if (isset(static::$metadataCache[$ident])) { - $cachedMetadata = static::$metadataCache[$ident]; + if (isset(self::$metadataCache[$ident])) { + $cachedMetadata = self::$metadataCache[$ident]; if (is_object($targetMetadata)) { return $targetMetadata->merge($cachedMetadata); @@ -165,7 +154,7 @@ public function load($ident, $metadata = [], array $idents = null) $targetMetadata = new $metadataType(); $targetMetadata->setData($data); - static::$metadataCache[$ident] = $targetMetadata; + self::$metadataCache[$ident] = $targetMetadata; return $targetMetadata; } @@ -175,9 +164,8 @@ public function load($ident, $metadata = [], array $idents = null) * * @param string $ident The metadata identifier to load. * @throws InvalidArgumentException If the identifier is not a string. - * @return array */ - public function loadMetadataByKey($ident) + public function loadMetadataByKey($ident): array { if (!is_string($ident)) { throw new InvalidArgumentException( @@ -201,9 +189,8 @@ public function loadMetadataByKey($ident) * Fetch the metadata for the given identifiers. * * @param array $idents One or more metadata identifiers to load. - * @return array */ - public function loadMetadataByKeys(array $idents) + public function loadMetadataByKeys(array $idents): array { $metadata = []; foreach ($idents as $metaKey) { @@ -222,14 +209,14 @@ public function loadMetadataByKeys(array $idents) * @param string $ident The FQCN (in snake-case) to load the hierarchy from. * @return array */ - private function hierarchy($ident) + private function hierarchy(string $ident) { if (!is_string($ident)) { return []; } - if (isset(static::$lineageCache[$ident])) { - return static::$lineageCache[$ident]; + if (isset(self::$lineageCache[$ident])) { + return self::$lineageCache[$ident]; } $classname = $this->classNameFromMetaKey($ident); @@ -254,8 +241,8 @@ private function classLineage($class, $ident = null) $ident = $this->metaKeyFromClassName($class); } - if (isset(static::$lineageCache[$ident])) { - return static::$lineageCache[$ident]; + if (isset(self::$lineageCache[$ident])) { + return self::$lineageCache[$ident]; } $class = $this->classNameFromMetaKey($ident); @@ -280,7 +267,7 @@ private function classLineage($class, $ident = null) $hierarchy = array_keys($hierarchy); - static::$lineageCache[$ident] = $hierarchy; + self::$lineageCache[$ident] = $hierarchy; return $hierarchy; } @@ -293,7 +280,7 @@ private function classLineage($class, $ident = null) * and these metadata identifiers are loaded instead. * @return array The data associated with the metadata identifier. */ - private function loadMetadataFromCache($ident, array $idents = null) + private function loadMetadataFromCache($ident, ?array $idents = null) { $cacheKey = $this->cacheKeyFromMetaKey($ident); $cacheItem = $this->cachePool()->getItem($cacheKey); @@ -310,12 +297,7 @@ private function loadMetadataFromCache($ident, array $idents = null) return $metadata; } else { - if (empty($idents)) { - $metadata = $this->loadMetadataByKey($ident); - } else { - $metadata = $this->loadMetadataByKeys($idents); - } - + $metadata = $idents === null || $idents === [] ? $this->loadMetadataByKey($ident) : $this->loadMetadataByKeys($idents); $cacheItem->set($metadata); $this->cachePool()->save($cacheItem); } @@ -346,14 +328,14 @@ private function loadMetadataFromSource($ident) * @throws UnexpectedValueException If the file cannot be loaded. * @return array|null An associative array on success, NULL on failure. */ - private function loadFile($path) + private function loadFile(string $path) { if (file_exists($path)) { return $this->loadJsonFile($path); } $dirs = $this->paths(); - if (empty($dirs)) { + if ($dirs === []) { return null; } @@ -366,7 +348,7 @@ private function loadFile($path) } } - if (empty($data)) { + if ($data === []) { return null; } @@ -380,7 +362,7 @@ private function loadFile($path) * @throws UnexpectedValueException If the file can not correctly be parsed into an array. * @return array An associative array on success. */ - private function loadJsonFile($path) + private function loadJsonFile(string $path) { $data = json_decode(file_get_contents($path), true); if (json_last_error() !== JSON_ERROR_NONE) { @@ -403,9 +385,8 @@ private function loadJsonFile($path) * Generate a store key. * * @param string|string[] $ident The metadata identifier(s) to convert. - * @return string */ - public function serializeMetaKey($ident) + public function serializeMetaKey($ident): string { if (is_array($ident)) { sort($ident); @@ -423,8 +404,7 @@ public function serializeMetaKey($ident) */ public function cacheKeyFromMetaKey($ident) { - $cacheKey = 'metadata/' . str_replace('/', '.', $ident); - return $cacheKey; + return 'metadata/' . str_replace('/', '.', $ident); } /** @@ -436,9 +416,8 @@ public function cacheKeyFromMetaKey($ident) private function filePathFromMetaKey($ident) { $filename = str_replace('\\', '.', $ident); - $filename .= '.json'; - return $filename; + return $filename . '.json'; } /** @@ -451,15 +430,15 @@ private function classNameFromMetaKey($ident) { $key = $ident; - if (isset(static::$camelCache[$key])) { - return static::$camelCache[$key]; + if (isset(self::$camelCache[$key])) { + return self::$camelCache[$key]; } // Change "foo-bar" to "fooBar" $parts = explode('-', $ident); array_walk( $parts, - function (&$i) { + function (&$i): void { $i = ucfirst($i); } ); @@ -471,14 +450,14 @@ function (&$i) { array_walk( $parts, - function (&$i) { + function (&$i): void { $i = ucfirst($i); } ); $classname = trim(implode('\\', $parts), '\\'); - static::$camelCache[$key] = $classname; - static::$snakeCache[$classname] = $key; + self::$camelCache[$key] = $classname; + self::$snakeCache[$classname] = $key; return $classname; } @@ -493,16 +472,16 @@ private function metaKeyFromClassName($class) { $key = trim($class, '\\'); - if (isset(static::$snakeCache[$key])) { - return static::$snakeCache[$key]; + if (isset(self::$snakeCache[$key])) { + return self::$snakeCache[$key]; } - $ident = strtolower(preg_replace('/([a-z])([A-Z])/', '$1-$2', $class)); + $ident = strtolower((string) preg_replace('/([a-z])([A-Z])/', '$1-$2', $class)); $ident = str_replace('\\', '/', strtolower($ident)); $ident = ltrim($ident, '/'); - static::$snakeCache[$key] = $ident; - static::$camelCache[$ident] = $key; + self::$snakeCache[$key] = $ident; + self::$camelCache[$ident] = $key; return $ident; } @@ -515,9 +494,8 @@ private function metaKeyFromClassName($class) * @param mixed $metadata The metadata type or container to validate. * @param string|null $type If provided, then it is filled with the resolved metadata type. * @param mixed|null $bag If provided, then it is filled with the resolved metadata container. - * @return boolean */ - private function validateMetadataContainer($metadata, &$type = null, &$bag = null) + private function validateMetadataContainer($metadata, &$type = null, &$bag = null): bool { // If variables are provided, clear existing values. $type = null; @@ -531,7 +509,7 @@ private function validateMetadataContainer($metadata, &$type = null, &$bag = nul if (is_a($metadata, MetadataInterface::class, true)) { if (is_object($metadata)) { - $type = get_class($metadata); + $type = $metadata::class; $bag = $metadata; return true; } @@ -549,9 +527,8 @@ private function validateMetadataContainer($metadata, &$type = null, &$bag = nul * * @param string $basePath The base path to use. * @throws InvalidArgumentException If the base path parameter is not a string. - * @return void */ - private function setBasePath($basePath) + private function setBasePath($basePath): void { if (!is_string($basePath)) { throw new InvalidArgumentException( @@ -565,10 +542,8 @@ private function setBasePath($basePath) /** * Retrieve the base path for relative search paths. - * - * @return string */ - private function basePath() + private function basePath(): string { return $this->basePath; } @@ -577,9 +552,8 @@ private function basePath() * Assign many search paths. * * @param string[] $paths One or more search paths. - * @return void */ - private function setPaths(array $paths) + private function setPaths(array $paths): void { $this->paths = []; $this->addPaths($paths); @@ -590,7 +564,7 @@ private function setPaths(array $paths) * * @return string[] */ - private function paths() + private function paths(): array { return $this->paths; } @@ -599,9 +573,8 @@ private function paths() * Append many search paths. * * @param string[] $paths One or more search paths. - * @return self */ - private function addPaths(array $paths) + private function addPaths(array $paths): self { foreach ($paths as $path) { $this->addPath($path); @@ -614,9 +587,8 @@ private function addPaths(array $paths) * Append a search path. * * @param string $path A directory path. - * @return self */ - private function addPath($path) + private function addPath($path): self { $path = $this->resolvePath($path); @@ -632,9 +604,8 @@ private function addPath($path) * * @param string $path The path to resolve. * @throws InvalidArgumentException If the path is invalid. - * @return string */ - private function resolvePath($path) + private function resolvePath($path): string { if (!is_string($path)) { throw new InvalidArgumentException( @@ -645,7 +616,7 @@ private function resolvePath($path) $basePath = $this->basePath(); $path = trim($path, '/\\'); - if ($basePath && strpos($path, $basePath) === false) { + if ($basePath && !str_contains($path, $basePath)) { $path = $basePath . DIRECTORY_SEPARATOR . $path; } @@ -656,9 +627,8 @@ private function resolvePath($path) * Validate a resolved path. * * @param string $path The path to validate. - * @return string */ - private function validatePath($path) + private function validatePath(string $path): bool { return is_dir($path); } @@ -667,19 +637,16 @@ private function validatePath($path) * Set the cache service. * * @param CacheItemPoolInterface $cache A PSR-6 compliant cache pool instance. - * @return void */ - private function setCachePool(CacheItemPoolInterface $cache) + private function setCachePool(CacheItemPoolInterface $cache): void { $this->cachePool = $cache; } /** * Retrieve the cache service. - * - * @return CacheItemPoolInterface */ - private function cachePool() + private function cachePool(): \Psr\Cache\CacheItemPoolInterface { return $this->cachePool; } diff --git a/packages/core/src/Charcoal/Model/Service/ModelBuilder.php b/packages/core/src/Charcoal/Model/Service/ModelBuilder.php index d23fc9afc..f60342f2c 100644 --- a/packages/core/src/Charcoal/Model/Service/ModelBuilder.php +++ b/packages/core/src/Charcoal/Model/Service/ModelBuilder.php @@ -14,22 +14,13 @@ */ final class ModelBuilder { - public const DEFAULT_SOURCE_TYPE = 'database'; + public const string DEFAULT_SOURCE_TYPE = 'database'; - /** - * @var FactoryInterface - */ - private $factory; + private \Charcoal\Factory\FactoryInterface $factory; - /** - * @var MetadataLoader - */ - private $metadataLoader; + private \Charcoal\Model\Service\MetadataLoader $metadataLoader; - /** - * @var FactoryInterface - */ - private $sourceFactory; + private \Charcoal\Factory\FactoryInterface $sourceFactory; /** * @param array $data Constructor dependencies. @@ -84,27 +75,24 @@ public function __invoke($objType, $metadataIdent = null, $sourceIdent = null) /** * @param FactoryInterface $factory The factory to use to create models. - * @return void */ - private function setFactory(FactoryInterface $factory) + private function setFactory(FactoryInterface $factory): void { $this->factory = $factory; } /** * @param MetadataLoader $loader The loader instance, used to load metadata. - * @return void */ - private function setMetadataLoader(MetadataLoader $loader) + private function setMetadataLoader(MetadataLoader $loader): void { $this->metadataLoader = $loader; } /** * @param FactoryInterface $factory The factory to use to create models. - * @return void */ - private function setSourceFactory(FactoryInterface $factory) + private function setSourceFactory(FactoryInterface $factory): void { $this->sourceFactory = $factory; } @@ -118,7 +106,7 @@ private function setSourceFactory(FactoryInterface $factory) */ private function createMetadata($objType, $metadataIdent = null) { - $metadataIdent = ($metadataIdent !== null) ? $metadataIdent : $objType; + $metadataIdent ??= $objType; return $this->metadataLoader->load($metadataIdent, ModelMetadata::class); } @@ -139,11 +127,11 @@ private function createSource(ModelMetadata $metadata, $sourceIdent = null) if (!$sourceConfig) { throw new UnexpectedValueException( - sprintf('Can not create %s source: "%s" is not defined in metadata.', get_class($this), $sourceIdent) + sprintf('Can not create %s source: "%s" is not defined in metadata.', self::class, $sourceIdent) ); } - $sourceType = isset($sourceConfig['type']) ? $sourceConfig['type'] : self::DEFAULT_SOURCE_TYPE; + $sourceType = $sourceConfig['type'] ?? self::DEFAULT_SOURCE_TYPE; $source = $this->sourceFactory->create($sourceType); $source->setData($sourceConfig); diff --git a/packages/core/src/Charcoal/Model/Service/ModelLoader.php b/packages/core/src/Charcoal/Model/Service/ModelLoader.php index 3e776aa24..0be75c95f 100644 --- a/packages/core/src/Charcoal/Model/Service/ModelLoader.php +++ b/packages/core/src/Charcoal/Model/Service/ModelLoader.php @@ -42,17 +42,13 @@ final class ModelLoader implements ArrayAccess /** * The model factory. - * - * @var FactoryInterface */ - private $factory; + private \Charcoal\Factory\FactoryInterface $factory; /** * The PSR-6 caching service. - * - * @var CacheItemPoolInterface */ - private $cachePool; + private \Psr\Cache\CacheItemPoolInterface $cachePool; /** * Construct a Model Loader with the dependencies @@ -90,7 +86,7 @@ public function __construct(array $data) * @param mixed $args Unused; Method arguments. * @return ModelInterface */ - public function __call($ident, $args = null) + public function __call(string $ident, ?array $args = null) { unset($args); @@ -103,7 +99,7 @@ public function __call($ident, $args = null) * @param string|integer $ident The object identifier to load. * @return ModelInterface */ - public function __get($ident) + public function __get(string $ident): mixed { return $this->load($ident); } @@ -115,7 +111,7 @@ public function __get($ident) * @param string $ident The object identifier to lookup. * @return boolean */ - public function __isset($ident) + public function __isset(string $ident) { return true; } @@ -127,7 +123,7 @@ public function __isset($ident) * @throws LogicException This method should never be called. * @return void */ - public function __unset($ident) + public function __unset(string $ident) { throw new LogicException( 'Can not unset value on a loader' @@ -169,9 +165,8 @@ public function offsetGet($ident) * @param string|integer $ident The $object identifier. * @param mixed $obj The object to add. * @throws LogicException This method should never be called. - * @return void */ - public function offsetSet($ident, $obj) + public function offsetSet($ident, $obj): void { throw new LogicException( 'Can not set value on a loader' @@ -184,9 +179,8 @@ public function offsetSet($ident, $obj) * @see ArrayAccess::offsetUnset() * @param string|integer $ident The object identifier to remove. * @throws LogicException This method should never be called. - * @return void */ - public function offsetUnset($ident) + public function offsetUnset($ident): void { throw new LogicException( 'Can not unset value on a loader' @@ -215,14 +209,11 @@ public function load($ident, $useCache = true, $reloadObj = false) $cacheKey = $this->cacheKey($ident); $cacheItem = $this->cachePool->getItem($cacheKey); - if (!$reloadObj) { - if ($cacheItem->isHit()) { - $data = $cacheItem->get(); - $obj = $this->factory->create($this->objType); - $obj->setData($data); - - return $obj; - } + if (!$reloadObj && $cacheItem->isHit()) { + $data = $cacheItem->get(); + $obj = $this->factory->create($this->objType); + $obj->setData($data); + return $obj; } $obj = $this->loadFromSource($ident); @@ -264,9 +255,7 @@ private function cacheKey($ident) $this->setObjKey($model->key()); } - $cacheKey = 'object/' . str_replace('/', '.', $this->objType . '.' . $this->objKey . '.' . $ident); - - return $cacheKey; + return 'object/' . str_replace('/', '.', $this->objType . '.' . $this->objKey . '.' . $ident); } /** @@ -276,9 +265,8 @@ private function cacheKey($ident) * * @param string $objType The object type to load with this loader. * @throws InvalidArgumentException If the object type is not a string. - * @return self */ - private function setObjType($objType) + private function setObjType($objType): self { if (!is_string($objType)) { throw new InvalidArgumentException( @@ -287,7 +275,7 @@ private function setObjType($objType) } $objType = preg_replace('/([a-z])([A-Z])/', '$1-$2', $objType); - $objType = strtolower(str_replace('\\', '/', trim($objType, '\\/'))); + $objType = strtolower(str_replace('\\', '/', trim((string) $objType, '\\/'))); $this->objType = $objType; return $this; @@ -298,9 +286,8 @@ private function setObjType($objType) * * @param string $objKey The object key to use for laoding. * @throws InvalidArgumentException If the object key is not a string. - * @return self */ - private function setObjKey($objKey) + private function setObjKey($objKey): self { if (empty($objKey) && !is_numeric($objKey)) { $this->objKey = null; @@ -321,9 +308,8 @@ private function setObjKey($objKey) * Set the model factory. * * @param FactoryInterface $factory The factory to create models. - * @return self */ - private function setFactory(FactoryInterface $factory) + private function setFactory(FactoryInterface $factory): self { $this->factory = $factory; return $this; @@ -333,9 +319,8 @@ private function setFactory(FactoryInterface $factory) * Set the cache pool handler. * * @param CacheItemPoolInterface $cachePool A PSR-6 compatible cache pool. - * @return self */ - private function setCachePool(CacheItemPoolInterface $cachePool) + private function setCachePool(CacheItemPoolInterface $cachePool): self { $this->cachePool = $cachePool; return $this; diff --git a/packages/core/src/Charcoal/Model/Service/ModelLoaderBuilder.php b/packages/core/src/Charcoal/Model/Service/ModelLoaderBuilder.php index b1c66116c..d4f34fd84 100644 --- a/packages/core/src/Charcoal/Model/Service/ModelLoaderBuilder.php +++ b/packages/core/src/Charcoal/Model/Service/ModelLoaderBuilder.php @@ -16,15 +16,9 @@ */ final class ModelLoaderBuilder { - /** - * @var FactoryInterface - */ - private $factory; + private \Charcoal\Factory\FactoryInterface $factory; - /** - * @var CacheItemPoolInterface - */ - private $cachePool; + private \Psr\Cache\CacheItemPoolInterface $cachePool; /** * @param array $data Builder dependencies. @@ -38,9 +32,8 @@ public function __construct(array $data) /** * @param string $objType The object type of the ModelLoader. * @param string $objKey Optional object key, to set on the ModelLoader. - * @return ModelLoader */ - public function build($objType, $objKey = null) + public function build($objType, $objKey = null): \Charcoal\Model\Service\ModelLoader { return new ModelLoader([ 'factory' => $this->factory, @@ -55,27 +48,24 @@ public function build($objType, $objKey = null) * * @param string $objType The object type of the ModelLoader. * @param string $objKey Optional object key, to set on the ModelLoader. - * @return ModelLoader */ - public function __invoke($objType, $objKey = null) + public function __invoke($objType, $objKey = null): \Charcoal\Model\Service\ModelLoader { return $this->build($objType, $objKey); } /** * @param FactoryInterface $factory The factory to use to create models. - * @return void */ - private function setFactory(FactoryInterface $factory) + private function setFactory(FactoryInterface $factory): void { $this->factory = $factory; } /** * @param CacheItemPoolInterface $cachePool The PSR-6 compliant cache pool. - * @return void */ - private function setCachePool(CacheItemPoolInterface $cachePool) + private function setCachePool(CacheItemPoolInterface $cachePool): void { $this->cachePool = $cachePool; } diff --git a/packages/core/src/Charcoal/Model/ServiceProvider/ModelServiceProvider.php b/packages/core/src/Charcoal/Model/ServiceProvider/ModelServiceProvider.php index 3212f075b..6236b5647 100644 --- a/packages/core/src/Charcoal/Model/ServiceProvider/ModelServiceProvider.php +++ b/packages/core/src/Charcoal/Model/ServiceProvider/ModelServiceProvider.php @@ -48,9 +48,8 @@ class ModelServiceProvider implements ServiceProviderInterface { /** * @param Container $container A Pimple DI container. - * @return void */ - public function register(Container $container) + public function register(Container $container): void { $this->registerModelDependencies($container); $this->registerMetadataDependencies($container); @@ -68,35 +67,29 @@ protected function registerBuilderDependencies(Container $container) * @param Container $container A Pimple DI container. * @return \Charcoal\Factory\FactoryInterface */ - $container['model/factory'] = function (Container $container) { - return new Factory([ - 'base_class' => ModelInterface::class, - 'arguments' => [ $container['model/dependencies'] ] - ]); - }; + $container['model/factory'] = (fn(Container $container): \Charcoal\Factory\GenericFactory => new Factory([ + 'base_class' => ModelInterface::class, + 'arguments' => [ $container['model/dependencies'] ] + ])); /** * @param Container $container A Pimple DI container. * @return ModelBuilder */ - $container['model/builder'] = function (Container $container) { - return new ModelBuilder([ - 'factory' => $container['model/factory'], - 'metadata_loader' => $container['metadata/loader'], - 'source_factory' => $container['source/factory'] - ]); - }; + $container['model/builder'] = (fn(Container $container): \Charcoal\Model\Service\ModelBuilder => new ModelBuilder([ + 'factory' => $container['model/factory'], + 'metadata_loader' => $container['metadata/loader'], + 'source_factory' => $container['source/factory'] + ])); /** * @param Container $container A Pimple DI container. * @return ModelLoaderBuilder */ - $container['model/loader/builder'] = function (Container $container) { - return new ModelLoaderBuilder([ - 'factory' => $container['model/factory'], - 'cache' => $container['cache'] - ]); - }; + $container['model/loader/builder'] = (fn(Container $container): \Charcoal\Model\Service\ModelLoaderBuilder => new ModelLoaderBuilder([ + 'factory' => $container['model/factory'], + 'cache' => $container['cache'] + ])); } /** @@ -112,9 +105,7 @@ protected function registerCollectionDependencies(Container $container) * @param Container $container A Pimple DI container. * @return \ArrayAccess|\Traversable */ - $container['model/collection'] = $container->factory(function (Container $container) { - return new $container['model/collection/class'](); - }); + $container['model/collection'] = $container->factory(fn(Container $container): object => new $container['model/collection/class']()); /** * @param Container $container A Pimple DI container. @@ -129,16 +120,14 @@ protected function registerCollectionDependencies(Container $container) * @param Container $container A Pimple DI container. * @return \Charcoal\Factory\FactoryInterface */ - $container['model/collection/loader/factory'] = function (Container $container) { - return new Factory([ - 'default_class' => CollectionLoader::class, - 'arguments' => [[ - 'logger' => $container['logger'], - 'factory' => $container['model/factory'], - 'collection' => $container['model/collection/class'] - ]] - ]); - }; + $container['model/collection/loader/factory'] = (fn(Container $container): \Charcoal\Factory\GenericFactory => new Factory([ + 'default_class' => CollectionLoader::class, + 'arguments' => [[ + 'logger' => $container['logger'], + 'factory' => $container['model/factory'], + 'collection' => $container['model/collection/class'] + ]] + ])); } /** @@ -153,16 +142,14 @@ protected function registerModelDependencies(Container $container) * @param Container $container A Pimple DI container. * @return array The model dependencies array. */ - $container['model/dependencies'] = function (Container $container) { - return [ - 'container' => $container, - 'logger' => $container['logger'], - 'view' => $container['view'], - 'property_factory' => $container['property/factory'], - 'metadata_loader' => $container['metadata/loader'], - 'source_factory' => $container['source/factory'] - ]; - }; + $container['model/dependencies'] = (fn(Container $container): array => [ + 'container' => $container, + 'logger' => $container['logger'], + 'view' => $container['view'], + 'property_factory' => $container['property/factory'], + 'metadata_loader' => $container['metadata/loader'], + 'source_factory' => $container['source/factory'] + ]); } // The property factory might be already set from elsewhere; defines it if not. @@ -171,22 +158,20 @@ protected function registerModelDependencies(Container $container) * @param Container $container A Pimple DI container. * @return \Charcoal\Factory\FactoryInterface */ - $container['property/factory'] = function (Container $container) { - return new Factory([ - 'base_class' => PropertyInterface::class, - 'default_class' => GenericProperty::class, - 'resolver_options' => [ - 'prefix' => '\\Charcoal\\Property\\', - 'suffix' => 'Property' - ], - 'arguments' => [[ - 'container' => $container, - 'database' => $container['database'], - 'logger' => $container['logger'], - 'translator' => $container['translator'] - ]] - ]); - }; + $container['property/factory'] = (fn(Container $container): \Charcoal\Factory\GenericFactory => new Factory([ + 'base_class' => PropertyInterface::class, + 'default_class' => GenericProperty::class, + 'resolver_options' => [ + 'prefix' => '\\Charcoal\\Property\\', + 'suffix' => 'Property' + ], + 'arguments' => [[ + 'container' => $container, + 'database' => $container['database'], + 'logger' => $container['logger'], + 'translator' => $container['translator'] + ]] + ])); } if (!isset($container['source/factory'])) { @@ -194,19 +179,17 @@ protected function registerModelDependencies(Container $container) * @param Container $container A Pimple DI container. * @return \Charcoal\Factory\FactoryInterface */ - $container['source/factory'] = function (Container $container) { - return new Factory([ - 'map' => [ - 'database' => DatabaseSource::class - ], - 'base_class' => SourceInterface::class, - 'arguments' => [[ - 'logger' => $container['logger'], - 'cache' => $container['cache'], - 'pdo' => $container['database'] - ]] - ]); - }; + $container['source/factory'] = (fn(Container $container): \Charcoal\Factory\GenericFactory => new Factory([ + 'map' => [ + 'database' => DatabaseSource::class + ], + 'base_class' => SourceInterface::class, + 'arguments' => [[ + 'logger' => $container['logger'], + 'cache' => $container['cache'], + 'pdo' => $container['database'] + ]] + ])); } } @@ -223,9 +206,9 @@ protected function registerMetadataDependencies(Container $container) * @param Container $container Pimple DI container. * @return MetadataConfig */ - $container['metadata/config'] = function (Container $container) { - $appConfig = isset($container['config']) ? $container['config'] : []; - $metaConfig = isset($appConfig['metadata']) ? $appConfig['metadata'] : null; + $container['metadata/config'] = function (Container $container): \Charcoal\Model\Service\MetadataConfig { + $appConfig = $container['config'] ?? []; + $metaConfig = $appConfig['metadata'] ?? null; $metaConfig = new MetadataConfig($metaConfig); if (isset($container['module/classes'])) { @@ -234,7 +217,7 @@ protected function registerMetadataDependencies(Container $container) $modules = $container['module/classes']; foreach ($modules as $module) { if (defined(sprintf('%s::APP_CONFIG', $module))) { - $configPath = ltrim($module::APP_CONFIG, '/'); + $configPath = ltrim((string) $module::APP_CONFIG, '/'); $configPath = $basePath . DIRECTORY_SEPARATOR . $configPath; $configData = $metaConfig->loadFile($configPath); @@ -247,7 +230,7 @@ protected function registerMetadataDependencies(Container $container) }; } - if (!empty($extraPaths)) { + if ($extraPaths !== []) { $metaConfig->addPaths($extraPaths); } } @@ -288,7 +271,7 @@ protected function registerMetadataDependencies(Container $container) * @param Container $container A Pimple DI container. * @return MetadataLoader */ - $container['metadata/loader'] = function (Container $container) { + $container['metadata/loader'] = function (Container $container): \Charcoal\Model\Service\MetadataLoader { $appConfig = $container['config']; $metaConfig = $container['metadata/config']; diff --git a/packages/core/src/Charcoal/Source/AbstractExpression.php b/packages/core/src/Charcoal/Source/AbstractExpression.php index fe6b88a61..eff1c1f83 100644 --- a/packages/core/src/Charcoal/Source/AbstractExpression.php +++ b/packages/core/src/Charcoal/Source/AbstractExpression.php @@ -71,7 +71,7 @@ abstract public function data(); */ public function setActive($active) { - $this->active = !!$active; + $this->active = (bool) $active; return $this; } @@ -183,7 +183,7 @@ public static function quoteIdentifier($identifier, $tableName = null) if (!is_string($identifier)) { throw new InvalidArgumentException(sprintf( 'Field Name must be a string, received %s', - is_object($identifier) ? get_class($identifier) : gettype($identifier) + get_debug_type($identifier) )); } @@ -191,7 +191,7 @@ public static function quoteIdentifier($identifier, $tableName = null) if (!is_string($tableName)) { throw new InvalidArgumentException(sprintf( 'Table Name must be a string, received %s', - is_object($tableName) ? get_class($tableName) : gettype($tableName) + get_debug_type($tableName) )); } @@ -201,11 +201,7 @@ public static function quoteIdentifier($identifier, $tableName = null) ); } - if ($identifier === '*') { - $template = '%1$s.*'; - } else { - $template = '%1$s.`%2$s`'; - } + $template = $identifier === '*' ? '%1$s.*' : '%1$s.`%2$s`'; return sprintf($template, $tableName, $identifier); } @@ -257,7 +253,7 @@ public static function isCallable($value) */ public function jsonSerialize() { - return array_udiff_assoc($this->data(), $this->defaultData(), [ $this, 'diffValues' ]); + return array_udiff_assoc($this->data(), $this->defaultData(), $this->diffValues(...)); } /** @@ -276,9 +272,8 @@ public function serialize() * * @see Serializable * @param string $data The serialized data. - * @return void */ - public function unserialize($data) + public function unserialize($data): void { $data = unserialize($data); $this->setData($data); diff --git a/packages/core/src/Charcoal/Source/AbstractSource.php b/packages/core/src/Charcoal/Source/AbstractSource.php index b5e1dab1e..9cfc70c60 100644 --- a/packages/core/src/Charcoal/Source/AbstractSource.php +++ b/packages/core/src/Charcoal/Source/AbstractSource.php @@ -227,7 +227,6 @@ protected function resolvePropertyName($property) * - as 3 parameters: `property`, `value` and `options` * - `addFilter('foo', 42, ['operator' => '<=']);` * - * @deprecated 0.3 To be replaced with FilterCollectionTrait::addFilter() * * @uses self::parseFilterWithModel() * @uses FilterCollectionTrait::processFilter() @@ -240,7 +239,8 @@ protected function resolvePropertyName($property) * @throws InvalidArgumentException If the $param argument is invalid. * @return self */ - public function addFilter($param, $value = null, array $options = null) + #[\Deprecated(message: '0.3 To be replaced with FilterCollectionTrait::addFilter()')] + public function addFilter($param, $value = null, ?array $options = null) { if (is_string($param) && $value !== null) { $expr = $this->createFilter(); @@ -292,7 +292,7 @@ protected function parseFilterWithModel(FilterInterface $filter) } if ($filter instanceof FilterCollectionInterface) { - $filter->traverseFilters(function (FilterInterface $expr) { + $filter->traverseFilters(function (FilterInterface $expr): void { $this->parseFilterWithModel($expr); }); } @@ -308,7 +308,7 @@ protected function parseFilterWithModel(FilterInterface $filter) * @param array $data Optional expression data. * @return FilterInterface A new filter expression object. */ - protected function createFilter(array $data = null) + protected function createFilter(?array $data = null) { $filter = new Filter(); if ($data !== null) { @@ -320,7 +320,6 @@ protected function createFilter(array $data = null) /** * Append a query order on the source. * - * @deprecated 0.3 To be replaced with OrderCollectionTrait::addOrder() * * @uses self::parseOrderWithModel() * @uses OrderCollectionTrait::processOrder() @@ -333,7 +332,8 @@ protected function createFilter(array $data = null) * @throws InvalidArgumentException If the $param argument is invalid. * @return self */ - public function addOrder($param, $mode = 'asc', array $options = null) + #[\Deprecated(message: '0.3 To be replaced with OrderCollectionTrait::addOrder()')] + public function addOrder($param, $mode = 'asc', ?array $options = null) { if (is_string($param) && $mode !== null) { $expr = $this->createOrder(); @@ -380,7 +380,7 @@ protected function parseOrderWithModel(OrderInterface $order) } if ($order instanceof OrderCollectionInterface) { - $order->traverseOrders(function (OrderInterface $expr) { + $order->traverseOrders(function (OrderInterface $expr): void { $this->parseOrderWithModel($expr); }); } @@ -395,7 +395,7 @@ protected function parseOrderWithModel(OrderInterface $order) * @param array $data Optional expression data. * @return OrderInterface */ - protected function createOrder(array $data = null) + protected function createOrder(?array $data = null) { $order = new Order(); if ($data !== null) { @@ -467,7 +467,7 @@ public function pagination() * @param array $data Optional clause data. * @return PaginationInterface */ - protected function createPagination(array $data = null) + protected function createPagination(?array $data = null) { $pagination = new Pagination(); if ($data !== null) { @@ -529,10 +529,9 @@ public function numPerPage() * @param array $data Optional data. * @return SourceConfig */ - public function createConfig(array $data = null) + public function createConfig(?array $data = null) { - $config = new SourceConfig($data); - return $config; + return new SourceConfig($data); } /** @@ -542,7 +541,7 @@ public function createConfig(array $data = null) * @param StorableInterface $item Optional item to load into. * @return StorableInterface */ - abstract public function loadItem($ident, StorableInterface $item = null); + abstract public function loadItem($ident, ?StorableInterface $item = null); /** * Load items for the given model. @@ -550,7 +549,7 @@ abstract public function loadItem($ident, StorableInterface $item = null); * @param StorableInterface|null $item Optional model. * @return StorableInterface[] */ - abstract public function loadItems(StorableInterface $item = null); + abstract public function loadItems(?StorableInterface $item = null); /** * Save an item (create a new row) in storage. @@ -568,7 +567,7 @@ abstract public function saveItem(StorableInterface $item); * @param array $properties The list of properties to update, if not all. * @return boolean TRUE if the item was updated, otherwise FALSE. */ - abstract public function updateItem(StorableInterface $item, array $properties = null); + abstract public function updateItem(StorableInterface $item, ?array $properties = null); /** * Delete an item from storage. @@ -577,7 +576,7 @@ abstract public function updateItem(StorableInterface $item, array $properties = * @throws UnexpectedValueException If the item does not have an ID. * @return boolean TRUE if the item was deleted, otherwise FALSE. */ - abstract public function deleteItem(StorableInterface $item = null); + abstract public function deleteItem(?StorableInterface $item = null); /** * Allow an object to define how the key getter are called. @@ -597,7 +596,7 @@ protected function getter($key) * @param string $key The key to get the setter from. * @return string The setter method name, for a given key. */ - protected function setter($key) + protected function setter(string $key) { $setter = 'set_' . $key; return $this->camelize($setter); @@ -611,7 +610,7 @@ protected function setter($key) */ protected function camelize($str) { - return lcfirst(implode('', array_map('ucfirst', explode('_', $str)))); + return lcfirst(implode('', array_map(ucfirst(...), explode('_', $str)))); } /** @@ -620,7 +619,7 @@ protected function camelize($str) protected function getModelClassForException() { if ($this->hasModel()) { - return get_class($this->model()); + return $this->model()::class; } return 'Unknown Model'; diff --git a/packages/core/src/Charcoal/Source/Database/DatabaseExpression.php b/packages/core/src/Charcoal/Source/Database/DatabaseExpression.php index c58ff74bd..5526333a7 100644 --- a/packages/core/src/Charcoal/Source/Database/DatabaseExpression.php +++ b/packages/core/src/Charcoal/Source/Database/DatabaseExpression.php @@ -1,5 +1,7 @@ operator(), [ '!', 'NOT' ]); } @@ -88,9 +88,8 @@ public function isNegating() * * @param string[] $conditions The list of conditions to compile. * @param string|null $conjunction The condition separator. - * @return string */ - protected function compileConditions(array $conditions, $conjunction = null) + protected function compileConditions(array $conditions, $conjunction = null): string { if (count($conditions) === 1) { return $conditions[0]; @@ -124,9 +123,8 @@ protected function byCondition() * Retrieve the correctly parenthesized and nested WHERE conditions. * * @throws UnexpectedValueException If the custom condition is empty. - * @return string */ - protected function byFilters() + protected function byFilters(): string { if (!$this->hasFilters()) { throw new UnexpectedValueException( @@ -140,7 +138,7 @@ protected function byFilters() $filter = $filter->sql(); } - if ($filter && strlen($filter) > 0) { + if ($filter && (string) $filter !== '') { $conditions[] = $filter; } } @@ -153,12 +151,11 @@ protected function byFilters() * * @todo Values are often not quoted. * @throws UnexpectedValueException If any required property, function, operator, or value is empty. - * @return string */ - protected function byPredicate() + protected function byPredicate(): string { $fields = $this->fieldIdentifiers(); - if (empty($fields)) { + if ($fields === []) { throw new UnexpectedValueException( 'Property is required.' ); @@ -169,11 +166,7 @@ protected function byPredicate() $operator = $this->operator(); $function = $this->func(); foreach ($fields as $fieldName) { - if ($function !== null) { - $target = sprintf('%1$s(%2$s)', $function, $fieldName); - } else { - $target = $fieldName; - } + $target = $function !== null ? sprintf('%1$s(%2$s)', $function, $fieldName) : $fieldName; switch ($operator) { case 'FIND_IN_SET': diff --git a/packages/core/src/Charcoal/Source/Database/DatabaseOrder.php b/packages/core/src/Charcoal/Source/Database/DatabaseOrder.php index c587e600a..e8af9e88f 100644 --- a/packages/core/src/Charcoal/Source/Database/DatabaseOrder.php +++ b/packages/core/src/Charcoal/Source/Database/DatabaseOrder.php @@ -25,14 +25,14 @@ class DatabaseOrder extends Order implements * * @var string */ + #[\Override] protected $table = DatabaseSource::DEFAULT_TABLE_ALIAS; /** * Retrieve the default values for sorting. - * - * @return array */ - public function defaultData() + #[\Override] + public function defaultData(): array { $defaults = parent::defaultData(); $defaults['table'] = DatabaseSource::DEFAULT_TABLE_ALIAS; @@ -77,10 +77,8 @@ public function sql() /** * Retrieve the ORDER BY clause for the {@see self::MODE_RANDOM} mode. - * - * @return string */ - protected function byRandom() + protected function byRandom(): string { return 'RAND()'; } @@ -89,12 +87,11 @@ protected function byRandom() * Generate the ORDER BY clause(s) for the direction mode. * * @throws UnexpectedValueException If any required property is empty. - * @return string */ - protected function byProperty() + protected function byProperty(): string { $fields = $this->fieldIdentifiers(); - if (empty($fields)) { + if ($fields === []) { throw new UnexpectedValueException( 'Property is required.' ); @@ -127,19 +124,18 @@ protected function byCondition() * Retrieve the ORDER BY clause for the {@see self::MODE_VALUES} mode. * * @throws UnexpectedValueException If any required property or values is empty. - * @return string */ - protected function byValues() + protected function byValues(): string { $fields = $this->fieldIdentifiers(); - if (empty($fields)) { + if ($fields === []) { throw new UnexpectedValueException( 'Property is required.' ); } $values = $this->prepareValues($this->values()); - if (empty($values)) { + if ($values === []) { throw new UnexpectedValueException(sprintf( 'Value can not be empty on fields: %s', implode(', ', $fields) @@ -164,7 +160,7 @@ protected function byValues() * @param mixed $values The value to be normalized. * @return array Returns a collection of parsed values. */ - public function prepareValues($values) + public function prepareValues($values): array { if (empty($values)) { return []; @@ -174,9 +170,8 @@ public function prepareValues($values) $values = (array)$values; } - $values = array_filter($values, 'is_scalar'); - $values = array_map('self::quoteValue', $values); + $values = array_filter($values, is_scalar(...)); - return $values; + return array_map(self::quoteValue(...), $values); } } diff --git a/packages/core/src/Charcoal/Source/Database/DatabasePagination.php b/packages/core/src/Charcoal/Source/Database/DatabasePagination.php index 34af33a3f..6b9993a7e 100644 --- a/packages/core/src/Charcoal/Source/Database/DatabasePagination.php +++ b/packages/core/src/Charcoal/Source/Database/DatabasePagination.php @@ -1,5 +1,7 @@ active() && $this->hasLimit()) { $limit = $this->limit(); @@ -31,10 +33,8 @@ public function sql() /** * Determine if the expression has a number per page. - * - * @return boolean */ - public function hasLimit() + public function hasLimit(): bool { return ($this->limit() > 0); } @@ -51,10 +51,8 @@ public function limit() /** * Retrieve the offset from the page number and count. - * - * @return integer */ - public function offset() + public function offset(): int { $page = $this->page(); $limit = $this->numPerPage(); diff --git a/packages/core/src/Charcoal/Source/DatabaseSource.php b/packages/core/src/Charcoal/Source/DatabaseSource.php index 1c27b0ef9..1ebf9968b 100644 --- a/packages/core/src/Charcoal/Source/DatabaseSource.php +++ b/packages/core/src/Charcoal/Source/DatabaseSource.php @@ -41,10 +41,8 @@ class DatabaseSource extends AbstractSource implements /** * The {@see self::$model}'s table name. - * - * @var string */ - private $table; + private ?string $table = null; /** * Create a new database handler. @@ -80,9 +78,8 @@ public function db() * * @param string $table The source table. * @throws InvalidArgumentException If argument is not a string or alphanumeric/underscore. - * @return self */ - public function setTable($table) + public function setTable($table): static { if (!is_string($table)) { throw new InvalidArgumentException(sprintf( @@ -97,7 +94,7 @@ public function setTable($table) * are valid table names; Although SQL can support more, * there's really no reason to. */ - if (!preg_match('/[A-Za-z0-9_]/', $table)) { + if (!preg_match('/\w/', $table)) { throw new InvalidArgumentException(sprintf( '[%s] Database table name "%s" is invalid: must be alphanumeric / underscore', $this->getModelClassForException(), @@ -111,21 +108,18 @@ public function setTable($table) /** * Determine if a table is assigned. - * - * @return boolean */ - public function hasTable() + public function hasTable(): bool { - return !empty($this->table); + return !in_array($this->table, [null, '', '0'], true); } /** * Get the database's current table. * * @throws RuntimeException If the table was not set. - * @return string */ - public function table() + public function table(): string { if ($this->table === null) { throw new RuntimeException(sprintf( @@ -141,7 +135,7 @@ public function table() * * @return boolean TRUE if the table was created, otherwise FALSE. */ - public function createTable() + public function createTable(): bool { if ($this->tableExists() === true) { return true; @@ -178,7 +172,7 @@ public function createTable() /** @todo Add indexes for all defined list constraints (yea... tough job...) */ if ($driver === self::MYSQL_DRIVER_NAME) { $engine = 'InnoDB'; - $query .= ') ENGINE=' . $engine . ' DEFAULT CHARSET=utf8 COMMENT="' . addslashes($metadata['name']) . '";'; + $query .= ') ENGINE=' . $engine . ' DEFAULT CHARSET=utf8 COMMENT="' . addslashes((string) $metadata['name']) . '";'; } else { $query .= ');'; } @@ -196,7 +190,7 @@ public function createTable() * * @return boolean TRUE if the table was altered, otherwise FALSE. */ - public function alterTable() + public function alterTable(): bool { if ($this->tableExists() === false) { return false; @@ -209,7 +203,7 @@ public function alterTable() foreach ($fields as $field) { $ident = $field->ident(); - if (!array_key_exists($ident, $cols)) { + if (!array_key_exists((string) $ident, $cols)) { $fieldSql = $field->sql(); if ($fieldSql) { // The key does not exist at all. @@ -226,30 +220,27 @@ public function alterTable() // The key exists. Validate. $col = $cols[$ident]; $alter = true; - if (strtolower($col['Type']) !== strtolower($field->sqlType())) { + if (strtolower((string) $col['Type']) !== strtolower($field->sqlType())) { $alter = true; } - if ((strtolower($col['Null']) !== 'no') !== $field->allowNull()) { + if ((strtolower((string) $col['Null']) !== 'no') !== $field->allowNull()) { $alter = true; } if ($col['Default'] !== $field->defaultVal()) { $alter = true; } - - if ($alter === true) { - $fieldSql = $field->sql(); - if ($fieldSql) { - $query = 'ALTER TABLE `' . $table . '` CHANGE `' . $ident . '` ' . $fieldSql; - $this->logger->debug($query); - $dbh->query($query); - } else { - $this->logger->warning('Empty column definition.', [ - 'table' => $table, - 'field' => $ident, - ]); - } + $fieldSql = $field->sql(); + if ($fieldSql) { + $query = 'ALTER TABLE `' . $table . '` CHANGE `' . $ident . '` ' . $fieldSql; + $this->logger->debug($query); + $dbh->query($query); + } else { + $this->logger->warning('Empty column definition.', [ + 'table' => $table, + 'field' => $ident, + ]); } } } @@ -267,7 +258,7 @@ public function tableExists() $dbh = $this->db(); $table = $this->table(); - if (isset($dbh->tableExists, $dbh->tableExists[$table])) { + if (property_exists($dbh, 'tableExists') && $dbh->tableExists !== null) { return $dbh->tableExists[$table]; } @@ -282,7 +273,7 @@ public function tableExists() * * @return boolean TRUE if the table exists, otherwise FALSE. */ - protected function performTableExists() + protected function performTableExists(): bool { $dbh = $this->db(); $table = $this->table(); @@ -312,7 +303,7 @@ protected function setTableExists($exists = true) $dbh = $this->db(); $table = $this->table(); - if (!isset($dbh->tableExists)) { + if (!property_exists($dbh, 'tableExists') || $dbh->tableExists === null) { $dbh->tableExists = []; } @@ -345,9 +336,9 @@ public function tableStructure() // Normalize SQLite's result (PRAGMA) with mysql's (SHOW COLUMNS) $struct[$col['name']] = [ 'Type' => $col['type'], - 'Null' => !!$col['notnull'] ? 'NO' : 'YES', + 'Null' => $col['notnull'] ? 'NO' : 'YES', 'Default' => $col['dflt_value'], - 'Key' => !!$col['pk'] ? 'PRI' : '', + 'Key' => $col['pk'] ? 'PRI' : '', 'Extra' => '', ]; } @@ -362,7 +353,7 @@ public function tableStructure() * * @return boolean TRUE if the table has no data, otherwise FALSE. */ - public function tableIsEmpty() + public function tableIsEmpty(): bool { $table = $this->table(); $query = sprintf('SELECT NULL FROM `%s` LIMIT 1', $table); @@ -380,7 +371,7 @@ public function tableIsEmpty() * If NULL, retrieve all (from metadata). * @return PropertyField[] */ - private function getModelFields(ModelInterface $model, $properties = null) + private function getModelFields(ModelInterface $model, ?array $properties = null): array { if ($properties === null) { // No custom properties; use all (from model metadata) @@ -398,7 +389,7 @@ private function getModelFields(ModelInterface $model, $properties = null) } $val = $model->propertyValue($propertyIdent); - foreach ($prop->fields($val) as $fieldIdent => $field) { + foreach ($prop->fields($val) as $field) { $fields[$field->ident()] = $field; } } @@ -413,7 +404,7 @@ private function getModelFields(ModelInterface $model, $properties = null) * @param StorableInterface $item Optional item to load into. * @return StorableInterface */ - public function loadItem($ident, StorableInterface $item = null) + public function loadItem($ident, ?StorableInterface $item = null) { $key = $this->model()->key(); @@ -429,12 +420,12 @@ public function loadItem($ident, StorableInterface $item = null) * @throws \Exception If the query fails. * @return StorableInterface */ - public function loadItemFromKey($key, $ident, StorableInterface $item = null) + public function loadItemFromKey($key, $ident, ?StorableInterface $item = null) { - if ($item !== null) { + if ($item instanceof \Charcoal\Source\StorableInterface) { $this->setModel($item); } else { - $class = get_class($this->model()); + $class = $this->model()::class; $item = new $class(); } @@ -472,12 +463,12 @@ public function loadItemFromKey($key, $ident, StorableInterface $item = null) * @throws PDOException If there is a query error. * @return StorableInterface */ - public function loadItemFromQuery($query, array $binds = [], StorableInterface $item = null) + public function loadItemFromQuery($query, array $binds = [], ?StorableInterface $item = null): object { - if ($item !== null) { + if ($item instanceof \Charcoal\Source\StorableInterface) { $this->setModel($item); } else { - $class = get_class($this->model()); + $class = $this->model()::class; $item = new $class(); } @@ -508,9 +499,9 @@ public function loadItemFromQuery($query, array $binds = [], StorableInterface $ * @param StorableInterface|null $item Optional model. * @return StorableInterface[] */ - public function loadItems(StorableInterface $item = null) + public function loadItems(?StorableInterface $item = null): array { - if ($item !== null) { + if ($item instanceof \Charcoal\Source\StorableInterface) { $this->setModel($item); } @@ -526,9 +517,9 @@ public function loadItems(StorableInterface $item = null) * @param StorableInterface|null $item Model Item. * @return StorableInterface[] */ - public function loadItemsFromQuery($query, array $binds = [], StorableInterface $item = null) + public function loadItemsFromQuery($query, array $binds = [], ?StorableInterface $item = null): array { - if ($item !== null) { + if ($item instanceof \Charcoal\Source\StorableInterface) { $this->setModel($item); } @@ -541,14 +532,14 @@ public function loadItemsFromQuery($query, array $binds = [], StorableInterface $sth = $dbh->prepare($query); // @todo Binds - if (!empty($binds)) { + if ($binds !== []) { unset($binds); } $sth->execute(); $sth->setFetchMode(PDO::FETCH_ASSOC); - $className = get_class($model); + $className = $model::class; while ($objData = $sth->fetch()) { $obj = new $className(); $obj->setFlatData($objData); @@ -572,9 +563,7 @@ public function saveItem(StorableInterface $item) $this->createTable(); } - if ($item !== null) { - $this->setModel($item); - } + $this->setModel($item); $model = $this->model(); $table = $this->table(); $struct = array_keys($this->tableStructure()); @@ -609,12 +598,10 @@ public function saveItem(StorableInterface $item) '[%s] Could not save item', $this->getModelClassForException() )); + } elseif ($model->id()) { + return $model->id(); } else { - if ($model->id()) { - return $model->id(); - } else { - return $this->db()->lastInsertId(); - } + return $this->db()->lastInsertId(); } } @@ -625,11 +612,9 @@ public function saveItem(StorableInterface $item) * @param array $properties The list of properties to update, if not all. * @return boolean TRUE if the item was updated, otherwise FALSE. */ - public function updateItem(StorableInterface $item, array $properties = null) + public function updateItem(StorableInterface $item, ?array $properties = null): bool { - if ($item !== null) { - $this->setModel($item); - } + $this->setModel($item); $model = $this->model(); $table = $this->table(); $struct = array_keys($this->tableStructure()); @@ -651,7 +636,7 @@ public function updateItem(StorableInterface $item, array $properties = null) $this->logger->warning( sprintf('Field "%s" not in table structure', $key), [ - 'model' => get_class($model), + 'model' => $model::class, 'table' => $table, 'field' => $key, ] @@ -659,11 +644,11 @@ public function updateItem(StorableInterface $item, array $properties = null) } } - if (empty($updates)) { + if ($updates === []) { $this->logger->warning( 'Could not update items. No valid fields were set or available in database table.', [ - 'model' => get_class($model), + 'model' => $model::class, 'table' => $table, 'properties' => $properties, 'structure' => $struct @@ -704,9 +689,9 @@ public function updateItem(StorableInterface $item, array $properties = null) * @throws UnexpectedValueException If the item does not have an ID. * @return boolean TRUE if the item was deleted, otherwise FALSE. */ - public function deleteItem(StorableInterface $item = null) + public function deleteItem(?StorableInterface $item = null): bool { - if ($item !== null) { + if ($item instanceof \Charcoal\Source\StorableInterface) { $this->setModel($item); } @@ -798,17 +783,15 @@ public function dbPrepare($query, array $binds = [], array $types = []) return false; } - if (!empty($binds)) { - foreach ($binds as $key => $val) { - if ($binds[$key] === null) { - $types[$key] = PDO::PARAM_NULL; - } elseif (!is_scalar($binds[$key])) { - $binds[$key] = json_encode($binds[$key]); - } - $type = (isset($types[$key]) ? $types[$key] : PDO::PARAM_STR); - $param = ':' . $key; - $sth->bindParam($param, $binds[$key], $type); + foreach (array_keys($binds) as $key) { + if ($binds[$key] === null) { + $types[$key] = PDO::PARAM_NULL; + } elseif (!is_scalar($binds[$key])) { + $binds[$key] = json_encode($binds[$key]); } + $type = ($types[$key] ?? PDO::PARAM_STR); + $param = ':' . $key; + $sth->bindParam($param, $binds[$key], $type); } return $sth; @@ -818,9 +801,8 @@ public function dbPrepare($query, array $binds = [], array $types = []) * Compile the SELECT statement for fetching one or more objects. * * @throws UnexpectedValueException If the source does not have a table defined. - * @return string */ - public function sqlLoad() + public function sqlLoad(): string { if (!$this->hasTable()) { throw new UnexpectedValueException(sprintf( @@ -834,18 +816,15 @@ public function sqlLoad() $filters = $this->sqlFilters(); $orders = $this->sqlOrders(); $limits = $this->sqlPagination(); - - $query = 'SELECT ' . $selects . ' FROM ' . $tables . $filters . $orders . $limits; - return $query; + return 'SELECT ' . $selects . ' FROM ' . $tables . $filters . $orders . $limits; } /** * Compile the SELECT statement for fetching the number of objects. * * @throws UnexpectedValueException If the source does not have a table defined. - * @return string */ - public function sqlLoadCount() + public function sqlLoadCount(): string { if (!$this->hasTable()) { throw new UnexpectedValueException(sprintf( @@ -856,18 +835,15 @@ public function sqlLoadCount() $tables = $this->sqlFrom(); $filters = $this->sqlFilters(); - - $query = 'SELECT COUNT(*) FROM ' . $tables . $filters; - return $query; + return 'SELECT COUNT(*) FROM ' . $tables . $filters; } /** * Compile the SELECT clause. * * @throws UnexpectedValueException If the clause has no selectable fields. - * @return string */ - public function sqlSelect() + public function sqlSelect(): string { $properties = $this->properties(); if (empty($properties)) { @@ -879,25 +855,22 @@ public function sqlSelect() $parts[] = Expression::quoteIdentifier($key, self::DEFAULT_TABLE_ALIAS); } - if (empty($parts)) { + if ($parts === []) { throw new UnexpectedValueException(sprintf( '[%s] Can not get SQL SELECT clause; no valid properties', $this->getModelClassForException() )); } - $clause = implode(', ', $parts); - - return $clause; + return implode(', ', $parts); } /** * Compile the FROM clause. * * @throws UnexpectedValueException If the source does not have a table defined. - * @return string */ - public function sqlFrom() + public function sqlFrom(): string { if (!$this->hasTable()) { throw new UnexpectedValueException(sprintf( @@ -927,7 +900,7 @@ public function sqlFilters() ]); $sql = $criteria->sql(); - if ($sql && strlen($sql) > 0) { + if ($sql && (string) $sql !== '') { $sql = ' WHERE ' . $sql; } @@ -936,10 +909,8 @@ public function sqlFilters() /** * Compile the ORDER BY clause. - * - * @return string */ - public function sqlOrders() + public function sqlOrders(): string { if (!$this->hasOrders()) { return ''; @@ -952,12 +923,12 @@ public function sqlOrders() } $sql = $order->sql(); - if ($sql && strlen($sql) > 0) { + if ($sql && (string) $sql !== '') { $parts[] = $sql; } } - if (empty($parts)) { + if ($parts === []) { return ''; } @@ -966,10 +937,8 @@ public function sqlOrders() /** * Compile the LIMIT clause. - * - * @return string */ - public function sqlPagination() + public function sqlPagination(): string { $pager = $this->pagination(); if (!$pager instanceof DatabasePagination) { @@ -977,7 +946,7 @@ public function sqlPagination() } $sql = $pager->sql(); - if ($sql && strlen($sql) > 0) { + if ($sql && $sql !== '') { $sql = ' ' . $sql; } @@ -988,9 +957,9 @@ public function sqlPagination() * Create a new filter expression. * * @param array $data Optional expression data. - * @return DatabaseFilter */ - protected function createFilter(array $data = null) + #[\Override] + protected function createFilter(?array $data = null): \Charcoal\Source\Database\DatabaseFilter { $filter = new DatabaseFilter(); if ($data !== null) { @@ -1003,9 +972,9 @@ protected function createFilter(array $data = null) * Create a new order expression. * * @param array $data Optional expression data. - * @return DatabaseOrder */ - protected function createOrder(array $data = null) + #[\Override] + protected function createOrder(?array $data = null): \Charcoal\Source\Database\DatabaseOrder { $order = new DatabaseOrder(); if ($data !== null) { @@ -1018,9 +987,9 @@ protected function createOrder(array $data = null) * Create a new pagination clause. * * @param array $data Optional clause data. - * @return DatabasePagination */ - protected function createPagination(array $data = null) + #[\Override] + protected function createPagination(?array $data = null): \Charcoal\Source\Database\DatabasePagination { $pagination = new DatabasePagination(); if ($data !== null) { @@ -1034,11 +1003,10 @@ protected function createPagination(array $data = null) * * @see \Charcoal\Config\ConfigurableTrait * @param array $data Optional data. - * @return DatabaseSourceConfig */ - public function createConfig(array $data = null) + #[\Override] + public function createConfig(?array $data = null): \Charcoal\Source\DatabaseSourceConfig { - $config = new DatabaseSourceConfig($data); - return $config; + return new DatabaseSourceConfig($data); } } diff --git a/packages/core/src/Charcoal/Source/DatabaseSourceConfig.php b/packages/core/src/Charcoal/Source/DatabaseSourceConfig.php index 6cfe1042c..14a5c20ff 100644 --- a/packages/core/src/Charcoal/Source/DatabaseSourceConfig.php +++ b/packages/core/src/Charcoal/Source/DatabaseSourceConfig.php @@ -1,5 +1,7 @@ 'mysql', @@ -61,9 +43,9 @@ public function defaults() * * @param string $type The database type. * @throws InvalidArgumentException If parameter is not a string. - * @return self */ - public function setType($type) + #[\Override] + public function setType($type): static { if (!is_string($type)) { throw new InvalidArgumentException( @@ -79,7 +61,8 @@ public function setType($type) * * @return string */ - public function type() + #[\Override] + public function type(): ?string { return $this->type; } @@ -89,9 +72,8 @@ public function type() * * @param string $hostname The database server hostname. * @throws InvalidArgumentException If hostname is not a string. - * @return self */ - public function setHostname($hostname) + public function setHostname($hostname): static { if (!is_string($hostname)) { throw new InvalidArgumentException( @@ -107,7 +89,7 @@ public function setHostname($hostname) * * @return string */ - public function hostname() + public function hostname(): ?string { return $this->hostname; } @@ -117,9 +99,8 @@ public function hostname() * * @param string $username The username. * @throws InvalidArgumentException If username is not a string. - * @return self */ - public function setUsername($username) + public function setUsername($username): static { if (!is_string($username)) { throw new InvalidArgumentException( @@ -135,7 +116,7 @@ public function setUsername($username) * * @return string */ - public function username() + public function username(): ?string { return $this->username; } @@ -145,9 +126,8 @@ public function username() * * @param string $password The password. * @throws InvalidArgumentException If password is not a string. - * @return self */ - public function setPassword($password) + public function setPassword($password): static { if (!is_string($password)) { throw new InvalidArgumentException( @@ -163,7 +143,7 @@ public function setPassword($password) * * @return string */ - public function password() + public function password(): ?string { return $this->password; } @@ -173,9 +153,8 @@ public function password() * * @param string $database The database name. * @throws InvalidArgumentException If database is not a string. - * @return self */ - public function setDatabase($database) + public function setDatabase($database): static { if (!is_string($database)) { throw new InvalidArgumentException( @@ -191,7 +170,7 @@ public function setDatabase($database) * * @return string */ - public function database() + public function database(): ?string { return $this->database; } @@ -200,11 +179,10 @@ public function database() * Set whether to disable UTF-8 compatibility or not. * * @param boolean $disableUtf8 The disable flag. - * @return self */ - public function setDisableUtf8($disableUtf8) + public function setDisableUtf8($disableUtf8): static { - $this->disableUtf8 = !!$disableUtf8; + $this->disableUtf8 = (bool) $disableUtf8; return $this; } @@ -213,7 +191,7 @@ public function setDisableUtf8($disableUtf8) * * @return boolean */ - public function disableUtf8() + public function disableUtf8(): ?bool { return $this->disableUtf8; } diff --git a/packages/core/src/Charcoal/Source/DatabaseSourceInterface.php b/packages/core/src/Charcoal/Source/DatabaseSourceInterface.php index 182b87f02..987748c9d 100644 --- a/packages/core/src/Charcoal/Source/DatabaseSourceInterface.php +++ b/packages/core/src/Charcoal/Source/DatabaseSourceInterface.php @@ -1,5 +1,7 @@ $data The expression data; * as an associative array. - * @return self */ - public function setData(array $data) + #[\Override] + public function setData(array $data): static { parent::setData($data); @@ -44,7 +44,7 @@ public function setData(array $data) * * @return array An associative array. */ - public function defaultData() + public function defaultData(): array { return [ 'condition' => null, @@ -58,7 +58,7 @@ public function defaultData() * * @return array An associative array. */ - public function data() + public function data(): array { return [ 'condition' => $this->condition(), @@ -72,9 +72,8 @@ public function data() * * @param string|null $condition The custom query expression. * @throws InvalidArgumentException If the parameter is not a valid string expression. - * @return self */ - public function setCondition($condition) + public function setCondition($condition): static { if ($condition === null) { $this->condition = $condition; @@ -98,10 +97,8 @@ public function setCondition($condition) /** * Determine if the expression has a custom condition. - * - * @return boolean */ - public function hasCondition() + public function hasCondition(): bool { return !(empty($this->condition) && !is_numeric($this->condition)); } diff --git a/packages/core/src/Charcoal/Source/ExpressionFieldInterface.php b/packages/core/src/Charcoal/Source/ExpressionFieldInterface.php index 665bf7440..3694c3b3f 100644 --- a/packages/core/src/Charcoal/Source/ExpressionFieldInterface.php +++ b/packages/core/src/Charcoal/Source/ExpressionFieldInterface.php @@ -1,5 +1,7 @@ property); } @@ -130,10 +128,8 @@ public function setTable($table) /** * Determine if a table is assigned. - * - * @return boolean */ - public function hasTable() + public function hasTable(): bool { return !empty($this->table); } @@ -212,7 +208,7 @@ public function fieldName() * * @return string[] */ - public function fieldIdentifiers() + public function fieldIdentifiers(): array { $identifiers = []; $tableName = $this->table(); @@ -243,7 +239,7 @@ public function fieldIdentifier() * @param string $value The string to snakeize. * @return string The snake_case string. */ - protected function snakeize($value) + protected function snakeize($value): string { $key = $value; @@ -251,7 +247,7 @@ protected function snakeize($value) return static::$snakeCache[$key]; } - $value = strtolower(preg_replace('/(? $data The expression data; * as an associative array. - * @return self */ - public function setData(array $data) + #[\Override] + public function setData(array $data): static { parent::setData($data); @@ -174,7 +174,8 @@ public function setData(array $data) * * @return array An associative array. */ - public function defaultData() + #[\Override] + public function defaultData(): array { return [ 'property' => null, @@ -195,7 +196,8 @@ public function defaultData() * * @return array An associative array. */ - public function data() + #[\Override] + public function data(): array { return [ 'property' => $this->property(), @@ -215,9 +217,8 @@ public function data() * Set the value used for comparison. * * @param mixed $value The value on the right side of the comparison. - * @return self */ - public function setValue($value) + public function setValue($value): static { $this->value = $this::parseValue($value); return $this; @@ -238,9 +239,8 @@ public function value() * * @param string $operator The comparison operator. * @throws InvalidArgumentException If the parameter is not a valid operator. - * @return self */ - public function setOperator($operator) + public function setOperator($operator): static { if (!is_string($operator)) { throw new InvalidArgumentException( @@ -262,10 +262,8 @@ public function setOperator($operator) /** * Retrieve the operator used for comparing field and value. - * - * @return string */ - public function operator() + public function operator(): string { return strtoupper($this->operator); } @@ -275,9 +273,8 @@ public function operator() * * @param string $func The function name to invoke on the field. * @throws InvalidArgumentException If the parameter is not a valid function. - * @return self */ - public function setFunc($func) + public function setFunc($func): static { if ($func === null) { $this->func = $func; @@ -317,9 +314,8 @@ public function func() * * @param string $conjunction The separator to use. * @throws InvalidArgumentException If the parameter is not a valid conjunction. - * @return self */ - public function setConjunction($conjunction) + public function setConjunction($conjunction): static { if (!is_string($conjunction)) { throw new InvalidArgumentException( @@ -354,7 +350,7 @@ public function conjunction() * * @return string[] */ - protected function validOperators() + protected function validOperators(): array { return [ '!', 'NOT', @@ -376,7 +372,7 @@ protected function validOperators() * * @return string[] */ - protected function validFunc() + protected function validFunc(): array { return [ 'ABS', @@ -410,7 +406,7 @@ protected function validFunc() * * @return string[] List of separators (case sensitive). */ - protected function validConjunctions() + protected function validConjunctions(): array { return [ 'AND', '&&', @@ -424,9 +420,8 @@ protected function validConjunctions() * * @see FilterCollectionTrait::createFilter() * @param array $data Optional expression data. - * @return self */ - protected function createFilter(array $data = null) + protected function createFilter(?array $data = null): static { $filter = new static(); if ($data !== null) { @@ -448,8 +443,6 @@ public function traverse(callable $callable) /** * Clone this expression and its subtree of expressions. - * - * @return void */ public function __clone() { diff --git a/packages/core/src/Charcoal/Source/FilterCollectionInterface.php b/packages/core/src/Charcoal/Source/FilterCollectionInterface.php index c0f9d330e..c81437713 100644 --- a/packages/core/src/Charcoal/Source/FilterCollectionInterface.php +++ b/packages/core/src/Charcoal/Source/FilterCollectionInterface.php @@ -1,5 +1,7 @@ filters); } @@ -165,5 +163,5 @@ public function traverseFilters(callable $callable) * @param array $data Optional expression data. * @return FilterInterface A new filter expression object. */ - abstract protected function createFilter(array $data = null); + abstract protected function createFilter(?array $data = null); } diff --git a/packages/core/src/Charcoal/Source/FilterInterface.php b/packages/core/src/Charcoal/Source/FilterInterface.php index bc132ddc2..bc618ba87 100644 --- a/packages/core/src/Charcoal/Source/FilterInterface.php +++ b/packages/core/src/Charcoal/Source/FilterInterface.php @@ -1,5 +1,7 @@ model !== null); } diff --git a/packages/core/src/Charcoal/Source/Order.php b/packages/core/src/Charcoal/Source/Order.php index afd1876a6..06771dd19 100644 --- a/packages/core/src/Charcoal/Source/Order.php +++ b/packages/core/src/Charcoal/Source/Order.php @@ -60,9 +60,9 @@ class Order extends Expression implements * * @param array $data The expression data; * as an associative array. - * @return self */ - public function setData(array $data) + #[\Override] + public function setData(array $data): static { parent::setData($data); @@ -114,10 +114,8 @@ public function setData(array $data) } } - if (isset($data['condition']) || isset($data['string'])) { - if (!isset($data['mode'])) { - $this->setMode(self::MODE_CUSTOM); - } + if ((isset($data['condition']) || isset($data['string'])) && !isset($data['mode'])) { + $this->setMode(self::MODE_CUSTOM); } return $this; @@ -128,7 +126,8 @@ public function setData(array $data) * * @return array An associative array. */ - public function defaultData() + #[\Override] + public function defaultData(): array { return [ 'property' => null, @@ -147,7 +146,8 @@ public function defaultData() * * @return array An associative array. */ - public function data() + #[\Override] + public function data(): array { return [ 'property' => $this->property(), @@ -166,9 +166,8 @@ public function data() * * @param string|null $mode The sorting mode. * @throws InvalidArgumentException If the mode is not a string or invalid. - * @return self */ - public function setMode($mode) + public function setMode($mode): static { if ($mode === null) { $this->mode = $mode; @@ -213,9 +212,8 @@ public function mode() * * @param string|null $direction The direction to sort on. * @throws InvalidArgumentException If the direction is not a string. - * @return self */ - public function setDirection($direction) + public function setDirection($direction): static { if ($direction === null) { $this->direction = $direction; @@ -253,9 +251,8 @@ public function direction() * - is a string, the string will be split by ",". * - is an array, the values will be used as is. * - any other data type throws an exception. - * @return self */ - public function setValues($values) + public function setValues($values): static { if ($values === null) { $this->values = $values; @@ -269,11 +266,11 @@ public function setValues($values) ); } - $values = array_map('trim', explode(',', $values)); + $values = array_map(trim(...), explode(',', $values)); } if (is_array($values)) { - if (empty($values)) { + if ($values === []) { throw new InvalidArgumentException( 'Array values can not be empty.' ); @@ -285,16 +282,14 @@ public function setValues($values) throw new InvalidArgumentException(sprintf( 'Order Values must be an array or comma-delimited string, received %s', - is_object($values) ? get_class($values) : gettype($values) + get_debug_type($values) )); } /** * Determine if the Order expression has values. - * - * @return boolean */ - public function hasValues() + public function hasValues(): bool { return !empty($this->values); } @@ -311,10 +306,8 @@ public function values() /** * Retrieve the supported sorting modes. - * - * @return array */ - protected function validModes() + protected function validModes(): array { return [ self::MODE_DESC, diff --git a/packages/core/src/Charcoal/Source/OrderCollectionInterface.php b/packages/core/src/Charcoal/Source/OrderCollectionInterface.php index 9f8abf25c..b737427da 100644 --- a/packages/core/src/Charcoal/Source/OrderCollectionInterface.php +++ b/packages/core/src/Charcoal/Source/OrderCollectionInterface.php @@ -1,5 +1,7 @@ orders); } @@ -165,5 +163,5 @@ public function traverseOrders(callable $callable) * @param array $data Optional expression data. * @return OrderInterface A new order expression object. */ - abstract protected function createOrder(array $data = null); + abstract protected function createOrder(?array $data = null); } diff --git a/packages/core/src/Charcoal/Source/OrderInterface.php b/packages/core/src/Charcoal/Source/OrderInterface.php index d49ab383e..b57ba3ffe 100644 --- a/packages/core/src/Charcoal/Source/OrderInterface.php +++ b/packages/core/src/Charcoal/Source/OrderInterface.php @@ -1,5 +1,7 @@ $data The expression data; * as an associative array. - * @return self */ - public function setData(array $data) + #[\Override] + public function setData(array $data): static { parent::setData($data); @@ -63,7 +63,7 @@ public function setData(array $data) * * @return array An associative array. */ - public function defaultData() + public function defaultData(): array { return [ 'page' => self::DEFAULT_PAGE, @@ -78,7 +78,7 @@ public function defaultData() * * @return array An associative array. */ - public function data() + public function data(): array { return [ 'page' => $this->page(), @@ -94,9 +94,8 @@ public function data() * @param integer $page The current page. * Pages should start at 1. * @throws InvalidArgumentException If the parameter is not numeric or < 0. - * @return self */ - public function setPage($page) + public function setPage($page): static { if (!is_numeric($page)) { throw new InvalidArgumentException( @@ -133,9 +132,8 @@ public function page() * @param integer $count The number of results to return, per page. * Use 0 to request all results. * @throws InvalidArgumentException If the parameter is not numeric or < 0. - * @return self */ - public function setNumPerPage($count) + public function setNumPerPage($count): static { if (!is_numeric($count)) { throw new InvalidArgumentException( @@ -166,10 +164,8 @@ public function numPerPage() /** * Retrieve the pagination's lowest possible index. - * - * @return integer */ - public function first() + public function first(): int { $page = $this->page(); $limit = $this->numPerPage(); @@ -184,7 +180,7 @@ public function first() * * @return integer */ - public function last() + public function last(): float|int|array { $first = $this->first(); $limit = $this->numPerPage(); diff --git a/packages/core/src/Charcoal/Source/PaginationInterface.php b/packages/core/src/Charcoal/Source/PaginationInterface.php index f90fdb9a9..d6dcfb324 100644 --- a/packages/core/src/Charcoal/Source/PaginationInterface.php +++ b/packages/core/src/Charcoal/Source/PaginationInterface.php @@ -1,5 +1,7 @@ '' @@ -29,9 +26,8 @@ public function defaults() /** * @param string $type The type of source. * @throws InvalidArgumentException If parameter is not a string. - * @return self */ - public function setType($type) + public function setType($type): static { if (!is_string($type)) { throw new InvalidArgumentException( @@ -45,7 +41,7 @@ public function setType($type) /** * @return string */ - public function type() + public function type(): ?string { return $this->type; } diff --git a/packages/core/src/Charcoal/Source/SourceInterface.php b/packages/core/src/Charcoal/Source/SourceInterface.php index 5d566e29a..c73e251ab 100644 --- a/packages/core/src/Charcoal/Source/SourceInterface.php +++ b/packages/core/src/Charcoal/Source/SourceInterface.php @@ -1,5 +1,7 @@ preSave(); if ($pre === false) { @@ -229,7 +229,7 @@ final public function save() 'Can not save object "%s:%s"; cancelled by %s::preSave()', $this->objType(), $this->id(), - get_called_class() + static::class )); return false; } @@ -240,7 +240,7 @@ final public function save() 'Can not save object "%s:%s"; repository failed for %s', $this->objType(), $this->id(), - get_called_class() + static::class )); return false; } else { @@ -253,7 +253,7 @@ final public function save() 'Saved object "%s:%s" but %s::postSave() failed', $this->objType(), $this->id(), - get_called_class() + static::class )); return false; } @@ -267,7 +267,7 @@ final public function save() * @param string[] $keys If provided, only update the properties specified. * @return boolean TRUE on success. */ - final public function update(array $keys = null) + final public function update(?array $keys = null): bool { $pre = $this->preUpdate($keys); if ($pre === false) { @@ -275,7 +275,7 @@ final public function update(array $keys = null) 'Can not update object "%s:%s"; cancelled by %s::preUpdate()', $this->objType(), $this->id(), - get_called_class() + static::class )); return false; } @@ -286,7 +286,7 @@ final public function update(array $keys = null) 'Can not update object "%s:%s"; repository failed for %s', $this->objType(), $this->id(), - get_called_class() + static::class )); return false; } @@ -297,7 +297,7 @@ final public function update(array $keys = null) 'Updated object "%s:%s" but %s::postUpdate() failed', $this->objType(), $this->id(), - get_called_class() + static::class )); return false; } @@ -310,7 +310,7 @@ final public function update(array $keys = null) * * @return boolean TRUE on success. */ - final public function delete() + final public function delete(): bool { $pre = $this->preDelete(); if ($pre === false) { @@ -318,7 +318,7 @@ final public function delete() 'Can not delete object "%s:%s"; cancelled by %s::preDelete()', $this->objType(), $this->id(), - get_called_class() + static::class )); return false; } @@ -329,7 +329,7 @@ final public function delete() 'Can not delete object "%s:%s"; repository failed for %s', $this->objType(), $this->id(), - get_called_class() + static::class )); return false; } @@ -340,7 +340,7 @@ final public function delete() 'Deleted object "%s:%s" but %s::postDelete() failed', $this->objType(), $this->id(), - get_called_class() + static::class )); return false; } @@ -370,7 +370,7 @@ protected function sourceFactory() { if (!isset($this->sourceFactory)) { throw new RuntimeException( - sprintf('Source factory is not set for "%s"', get_class($this)) + sprintf('Source factory is not set for "%s"', $this::class) ); } return $this->sourceFactory; @@ -381,7 +381,7 @@ protected function sourceFactory() * * @return boolean TRUE to proceed with creation; FALSE to stop creation. */ - protected function preSave() + protected function preSave(): bool { return true; } @@ -391,7 +391,7 @@ protected function preSave() * * @return boolean TRUE to indicate object was created. */ - protected function postSave() + protected function postSave(): bool { return true; } @@ -402,7 +402,7 @@ protected function postSave() * @param string[] $keys Optional list of properties to update. * @return boolean TRUE to proceed with update; FALSE to stop update. */ - protected function preUpdate(array $keys = null) + protected function preUpdate(?array $keys = null): bool { return true; } @@ -413,7 +413,7 @@ protected function preUpdate(array $keys = null) * @param string[] $keys Optional list of properties to update. * @return boolean TRUE to indicate object was updated. */ - protected function postUpdate(array $keys = null) + protected function postUpdate(?array $keys = null): bool { return true; } @@ -423,7 +423,7 @@ protected function postUpdate(array $keys = null) * * @return boolean TRUE to proceed with deletion; FALSE to stop deletion. */ - protected function preDelete() + protected function preDelete(): bool { return true; } @@ -433,7 +433,7 @@ protected function preDelete() * * @return boolean TRUE to indicate object was deleted. */ - protected function postDelete() + protected function postDelete(): bool { return true; } diff --git a/packages/core/src/Charcoal/Validator/AbstractValidator.php b/packages/core/src/Charcoal/Validator/AbstractValidator.php index be98ede35..6bea5c507 100644 --- a/packages/core/src/Charcoal/Validator/AbstractValidator.php +++ b/packages/core/src/Charcoal/Validator/AbstractValidator.php @@ -15,15 +15,10 @@ */ abstract class AbstractValidator implements ValidatorInterface { - /** - * @var ValidatableInterface - */ - protected $model; - /** * @var ValidatorResult[] $results */ - private $results = []; + private array $results = []; /** * Holds a list of all camelized strings. @@ -35,9 +30,8 @@ abstract class AbstractValidator implements ValidatorInterface /** * @param ValidatableInterface $model The object to validate. */ - public function __construct(ValidatableInterface $model) + public function __construct(protected \Charcoal\Validator\ValidatableInterface $model) { - $this->model = $model; } /** @@ -80,7 +74,7 @@ public function log($level, $msg, $ident = null) { $this->addResult( [ - 'ident' => (($ident !== null) ? $ident : ''), + 'ident' => ($ident ?? ''), 'level' => $level, 'message' => $msg ] @@ -160,7 +154,7 @@ public function merge(ValidatorInterface $v, $prefix = null) { $allResults = $v->results(); - foreach ($allResults as $level => $resultset) { + foreach ($allResults as $resultset) { foreach ($resultset as $result) { if ($prefix !== null) { $result->setIdent($prefix . '.' . $result->ident()); @@ -185,8 +179,8 @@ final protected function camelize($value) return static::$camelCache[$key]; } - if (strpos($value, '_') !== false) { - $value = implode('', array_map('ucfirst', explode('_', $value))); + if (str_contains($value, '_')) { + $value = implode('', array_map(ucfirst(...), explode('_', $value))); } static::$camelCache[$key] = lcfirst($value); diff --git a/packages/core/src/Charcoal/Validator/ValidatableInterface.php b/packages/core/src/Charcoal/Validator/ValidatableInterface.php index 09791b9f2..aa0f525e3 100644 --- a/packages/core/src/Charcoal/Validator/ValidatableInterface.php +++ b/packages/core/src/Charcoal/Validator/ValidatableInterface.php @@ -1,5 +1,7 @@ setValidator($v); } diff --git a/packages/core/src/Charcoal/Validator/ValidatorInterface.php b/packages/core/src/Charcoal/Validator/ValidatorInterface.php index a500e19ce..726400766 100644 --- a/packages/core/src/Charcoal/Validator/ValidatorInterface.php +++ b/packages/core/src/Charcoal/Validator/ValidatorInterface.php @@ -1,5 +1,7 @@ setIdent($data['ident']); @@ -69,9 +58,8 @@ public function setData(array $data) /** * @param string $ident The result identigier. * @throws InvalidArgumentException If parameter is not valid. - * @return ValidatorResult */ - public function setIdent($ident) + public function setIdent($ident): static { if (!is_string($ident)) { throw new InvalidArgumentException( @@ -85,7 +73,7 @@ public function setIdent($ident) /** * @return string */ - public function ident() + public function ident(): ?string { return $this->ident; } @@ -93,9 +81,8 @@ public function ident() /** * @param string $level The validation level ('notice', 'warning' or 'error'). * @throws InvalidArgumentException If parameter is not a valid level. - * @return ValidatorResult */ - public function setLevel($level) + public function setLevel($level): static { if (!is_string($level)) { throw new InvalidArgumentException( @@ -114,7 +101,7 @@ public function setLevel($level) /** * @return string */ - public function level() + public function level(): ?string { return $this->level; } @@ -122,9 +109,8 @@ public function level() /** * @param string $message The validation message. * @throws InvalidArgumentException If parameter is not valid. - * @return ValidatorResult */ - public function setMessage($message) + public function setMessage($message): static { if (!is_string($message)) { throw new InvalidArgumentException( @@ -138,7 +124,7 @@ public function setMessage($message) /** * @return string */ - public function message() + public function message(): ?string { return $this->message; } @@ -146,9 +132,8 @@ public function message() /** * @param string|DateTime $ts The datetime value. * @throws InvalidArgumentException If parameter is not valid "datetime". - * @return ValidatorResult */ - public function setTs($ts) + public function setTs($ts): static { if (is_string($ts)) { $ts = new DateTime($ts); @@ -165,7 +150,7 @@ public function setTs($ts) /** * @return DateTime */ - public function ts() + public function ts(): \DateTimeInterface { return $this->ts; } diff --git a/packages/core/tests/Charcoal/AbstractTestCase.php b/packages/core/tests/Charcoal/AbstractTestCase.php index 59ba12ea0..80f1772c4 100644 --- a/packages/core/tests/Charcoal/AbstractTestCase.php +++ b/packages/core/tests/Charcoal/AbstractTestCase.php @@ -1,5 +1,7 @@ assertCount(count($expected), $haystack, $message); $this->assertEquals($expected, $haystack, $message); @@ -27,9 +26,8 @@ public function assertArrayEquals(array $expected, array $haystack, $message = ' * @param array $expected The expected haystack. * @param array $haystack The actual haystack. * @param string $message The error to report. - * @return void */ - public function assertArrayContains(array $expected, array $haystack, $message = '') + public function assertArrayContains(array $expected, array $haystack, $message = ''): void { foreach ($expected as $item) { $this->assertContains($item, $haystack, $message); @@ -42,9 +40,8 @@ public function assertArrayContains(array $expected, array $haystack, $message = * @param array $expected The expected haystack. * @param array $haystack The actual haystack. * @param string $message The error to report. - * @return void */ - public function assertArrayHasKeys(array $expected, array $haystack, $message = '') + public function assertArrayHasKeys(array $expected, array $haystack, $message = ''): void { foreach ($expected as $item) { $this->assertArrayHasKey($item, $haystack, $message); @@ -58,14 +55,13 @@ public function assertArrayHasKeys(array $expected, array $haystack, $message = * @param array $haystack The actual haystack. * @param boolean $strict Whether to check for object identity. * @param string $message The error to report. - * @return void */ public function assertArraySubsets( array $expected, array $haystack, $strict = false, $message = '' - ) { + ): void { foreach ($expected as $key => $val) { $this->assertArraySubset([ $key => $val ], $haystack, $strict, $message); } diff --git a/packages/core/tests/Charcoal/CoreContainerIntegrationTrait.php b/packages/core/tests/Charcoal/CoreContainerIntegrationTrait.php index 57ce03768..9b6c79600 100644 --- a/packages/core/tests/Charcoal/CoreContainerIntegrationTrait.php +++ b/packages/core/tests/Charcoal/CoreContainerIntegrationTrait.php @@ -50,10 +50,9 @@ protected function getContainerProvider() } /** - * @return void * @see CoreContainerProvider */ - private function setupContainer() + private function setupContainer(): void { $provider = new CoreContainerProvider(); $container = new Container(); diff --git a/packages/core/tests/Charcoal/CoreContainerProvider.php b/packages/core/tests/Charcoal/CoreContainerProvider.php index 6de374c33..70a8b4f97 100644 --- a/packages/core/tests/Charcoal/CoreContainerProvider.php +++ b/packages/core/tests/Charcoal/CoreContainerProvider.php @@ -44,9 +44,8 @@ class CoreContainerProvider * Register the unit tests required services. * * @param Container $container A DI container. - * @return void */ - public function registerBaseServices(Container $container) + public function registerBaseServices(Container $container): void { $this->registerConfig($container); $this->registerSource($container); @@ -58,15 +57,12 @@ public function registerBaseServices(Container $container) * Setup the application configset. * * @param Container $container A DI container. - * @return void */ - public function registerConfig(Container $container) + public function registerConfig(Container $container): void { - $container['config'] = function () { - return new AppConfig([ - 'base_path' => realpath(__DIR__ . '/../../..') - ]); - }; + $container['config'] = (fn(): \Charcoal\App\AppConfig => new AppConfig([ + 'base_path' => realpath(__DIR__ . '/../../..') + ])); } /** @@ -75,11 +71,10 @@ public function registerConfig(Container $container) * Note: Uses SQLite to create a database in memory. * * @param Container $container A DI container. - * @return void */ - public function registerSource(Container $container) + public function registerSource(Container $container): void { - $container['database'] = function () { + $container['database'] = function (): \PDO { $pdo = new PDO('sqlite::memory:'); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); return $pdo; @@ -90,60 +85,48 @@ public function registerSource(Container $container) * Setup the application's logging interface. * * @param Container $container A DI container. - * @return void */ - public function registerLogger(Container $container) + public function registerLogger(Container $container): void { - $container['logger'] = function () { - return new NullLogger(); - }; + $container['logger'] = (fn(): \Psr\Log\NullLogger => new NullLogger()); } /** * Setup the application's caching interface. * * @param Container $container A DI container. - * @return void */ - public function registerCache(Container $container) + public function registerCache(Container $container): void { - $container['cache'] = function () { - return new Pool(); - }; + $container['cache'] = (fn(): \Stash\Pool => new Pool()); } /** * Setup the application's translator service. * * @param Container $container A DI container. - * @return void */ - public function registerTranslator(Container $container) + public function registerTranslator(Container $container): void { - $container['locales/manager'] = function () { - return new LocalesManager([ - 'locales' => [ - 'en' => [ 'locale' => 'en-US' ] - ] - ]); - }; + $container['locales/manager'] = (fn(): \Charcoal\Translator\LocalesManager => new LocalesManager([ + 'locales' => [ + 'en' => [ 'locale' => 'en-US' ] + ] + ])); - $container['translator'] = function (Container $container) { - return new Translator([ - 'manager' => $container['locales/manager'] - ]); - }; + $container['translator'] = (fn(Container $container): \Charcoal\Translator\Translator => new Translator([ + 'manager' => $container['locales/manager'] + ])); } /** * Setup the application's translator service. * * @param Container $container A DI container. - * @return void */ - public function registerMultilingualTranslator(Container $container) + public function registerMultilingualTranslator(Container $container): void { - $container['locales/manager'] = function () { + $container['locales/manager'] = function (): \Charcoal\Translator\LocalesManager { $manager = new LocalesManager([ 'locales' => [ 'en' => [ @@ -178,7 +161,7 @@ public function registerMultilingualTranslator(Container $container) return $manager; }; - $container['translator'] = function (Container $container) { + $container['translator'] = function (Container $container): \Charcoal\Translator\Translator { $translator = new Translator([ 'manager' => $container['locales/manager'] ]); @@ -198,106 +181,91 @@ public function registerMultilingualTranslator(Container $container) * Setup the framework's metadata loader interface. * * @param Container $container A DI container. - * @return void */ - public function registerMetadataLoader(Container $container) + public function registerMetadataLoader(Container $container): void { - $container['metadata/loader'] = function (Container $container) { - return new MetadataLoader([ - 'cache' => $container['cache'], - 'logger' => $container['logger'], - 'base_path' => $container['config']['base_path'], - 'paths' => [ - 'metadata', - // Standalone - 'vendor/charcoal/property/metadata', - // Monorepo - '/../property/metadata' - ] - ]); - }; + $container['metadata/loader'] = (fn(Container $container): \Charcoal\Model\Service\MetadataLoader => new MetadataLoader([ + 'cache' => $container['cache'], + 'logger' => $container['logger'], + 'base_path' => $container['config']['base_path'], + 'paths' => [ + 'metadata', + // Standalone + 'vendor/charcoal/property/metadata', + // Monorepo + '/../property/metadata' + ] + ])); } /** * Setup the framework's data source factory. * * @param Container $container A DI container. - * @return void */ - public function registerSourceFactory(Container $container) + public function registerSourceFactory(Container $container): void { - $container['source/factory'] = function (Container $container) { - return new Factory([ - 'map' => [ - 'database' => DatabaseSource::class - ], - 'arguments' => [[ - 'logger' => $container['logger'], - 'cache' => $container['cache'], - 'pdo' => $container['database'] - ]] - ]); - }; + $container['source/factory'] = (fn(Container $container): \Charcoal\Factory\GenericFactory => new Factory([ + 'map' => [ + 'database' => DatabaseSource::class + ], + 'arguments' => [[ + 'logger' => $container['logger'], + 'cache' => $container['cache'], + 'pdo' => $container['database'] + ]] + ])); } /** * Setup the framework's model factory. * * @param Container $container A DI container. - * @return void */ - public function registerModelFactory(Container $container) + public function registerModelFactory(Container $container): void { - $container['model/factory'] = function (Container $container) { - return new Factory([ - 'arguments' => [[ - 'container' => $container, - 'logger' => $container['logger'], - 'metadata_loader' => $container['metadata/loader'], - 'source_factory' => $container['source/factory'], - 'property_factory' => $container['property/factory'] - ]] - ]); - }; + $container['model/factory'] = (fn(Container $container): \Charcoal\Factory\GenericFactory => new Factory([ + 'arguments' => [[ + 'container' => $container, + 'logger' => $container['logger'], + 'metadata_loader' => $container['metadata/loader'], + 'source_factory' => $container['source/factory'], + 'property_factory' => $container['property/factory'] + ]] + ])); } /** * Setup the framework's property factory. * * @param Container $container A DI container. - * @return void */ - public function registerPropertyFactory(Container $container) + public function registerPropertyFactory(Container $container): void { - $container['property/factory'] = function (Container $container) { - return new Factory([ - 'resolver_options' => [ - 'prefix' => '\\Charcoal\\Property\\', - 'suffix' => 'Property' - ], - 'arguments' => [[ - 'container' => $container, - 'database' => $container['database'], - 'logger' => $container['logger'], - 'translator' => $container['translator'] - ]] - ]); - }; + $container['property/factory'] = (fn(Container $container): \Charcoal\Factory\GenericFactory => new Factory([ + 'resolver_options' => [ + 'prefix' => '\\Charcoal\\Property\\', + 'suffix' => 'Property' + ], + 'arguments' => [[ + 'container' => $container, + 'database' => $container['database'], + 'logger' => $container['logger'], + 'translator' => $container['translator'] + ]] + ])); } /** * Setup the framework's collection loader interface. * * @param Container $container A DI container. - * @return void */ - public function registerModelCollectionLoader(Container $container) + public function registerModelCollectionLoader(Container $container): void { - $container['model/collection/loader'] = function (Container $container) { - return new CollectionLoader([ - 'logger' => $container['logger'], - 'cache' => $container['cache'] - ]); - }; + $container['model/collection/loader'] = (fn(Container $container): \Charcoal\Loader\CollectionLoader => new CollectionLoader([ + 'logger' => $container['logger'], + 'cache' => $container['cache'] + ])); } } diff --git a/packages/core/tests/Charcoal/Loader/CollectionLoaderTest.php b/packages/core/tests/Charcoal/Loader/CollectionLoaderTest.php index 11b110546..ca9c8e74e 100644 --- a/packages/core/tests/Charcoal/Loader/CollectionLoaderTest.php +++ b/packages/core/tests/Charcoal/Loader/CollectionLoaderTest.php @@ -20,27 +20,18 @@ use Charcoal\Tests\CoreContainerIntegrationTrait; use Charcoal\Tests\ReflectionsTrait; -/** - * - */ +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Loader\CollectionLoader::class, 'camelize')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Loader\CollectionLoader::class, 'getter')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Loader\CollectionLoader::class, 'setter')] class CollectionLoaderTest extends AbstractTestCase { use CoreContainerIntegrationTrait; use ReflectionsTrait; - /** - * @var CollectionLoader - */ - private $loader; + private \Charcoal\Loader\CollectionLoader $loader; - /** - * @var Model - */ - private $model; + private \Charcoal\Model\Model $model; - /** - * @return void - */ protected function setUp(): void { $this->model = $this->createModel(); @@ -49,10 +40,7 @@ protected function setUp(): void $this->loader = $this->createCollectionLoader(); } - /** - * @return CollectionLoader - */ - public function createCollectionLoader() + public function createCollectionLoader(): \Charcoal\Loader\CollectionLoader { $container = $this->getContainer(); @@ -63,18 +51,13 @@ public function createCollectionLoader() ]] ]); - $loader = new CollectionLoader([ + return new CollectionLoader([ 'logger' => $container['logger'], 'factory' => $factory, ]); - - return $loader; } - /** - * @return Model - */ - public function createModel() + public function createModel(): \Charcoal\Model\Model { $container = $this->getContainer(); @@ -117,10 +100,7 @@ public function createModel() return $model; } - /** - * @return void - */ - public function testSetData() + public function testSetData(): void { $loader = $this->loader; $loader->setModel($this->model); @@ -134,30 +114,21 @@ public function testSetData() $this->assertEquals([ 'id', 'test' ], $loader->properties()); } - /** - * @return void - */ - public function testSetDataIsChainable() + public function testSetDataIsChainable(): void { $loader = $this->loader; $ret = $loader->setData([]); $this->assertSame($ret, $loader); } - /** - * @return void - */ - public function testDefaultCollection() + public function testDefaultCollection(): void { $loader = $this->loader; $collection = $loader->createCollection(); $this->assertInstanceOf(Collection::class, $collection); } - /** - * @return void - */ - public function testCustomCollectionClass() + public function testCustomCollectionClass(): void { $loader = $this->loader; @@ -177,10 +148,7 @@ public function testCustomCollectionClass() $this->assertInternalType('array', $collection); } - /** - * @return void - */ - public function testAll() + public function testAll(): void { $loader = $this->loader; $loader->setModel($this->model) @@ -194,7 +162,7 @@ public function testAll() $this->assertTrue($loader->hasModel()); - $collection = $loader->load(); + $loader->load(); $this->assertEquals(1, 1); @@ -204,13 +172,8 @@ public function testAll() /** * Test camelization. * - * @covers \Charcoal\Loader\CollectionLoader::camelize - * @covers \Charcoal\Loader\CollectionLoader::getter - * @covers \Charcoal\Loader\CollectionLoader::setter - * - * @return void */ - public function testCamelize() + public function testCamelize(): void { $loader = $this->loader; diff --git a/packages/core/tests/Charcoal/Mock/BadStorableMock.php b/packages/core/tests/Charcoal/Mock/BadStorableMock.php index 40765d358..fced583e3 100644 --- a/packages/core/tests/Charcoal/Mock/BadStorableMock.php +++ b/packages/core/tests/Charcoal/Mock/BadStorableMock.php @@ -1,5 +1,7 @@ fail; } @@ -70,7 +71,8 @@ protected function preSave() * @see StorableTrait::postSave() * @return boolean TRUE to indicate object was created. */ - protected function postSave() + #[\Override] + protected function postSave(): bool { return !$this->fail; } @@ -82,7 +84,8 @@ protected function postSave() * @param string[] $keys Optional list of properties to update. * @return boolean TRUE to proceed with update; FALSE to stop update. */ - protected function preUpdate(array $keys = null) + #[\Override] + protected function preUpdate(?array $keys = null): bool { return $this->fail; } @@ -94,7 +97,8 @@ protected function preUpdate(array $keys = null) * @param string[] $keys Optional list of properties to update. * @return boolean TRUE to indicate object was updated. */ - protected function postUpdate(array $keys = null) + #[\Override] + protected function postUpdate(?array $keys = null): bool { return !$this->fail; } @@ -105,7 +109,8 @@ protected function postUpdate(array $keys = null) * @see StorableTrait::preDelete() * @return boolean TRUE to proceed with deletion; FALSE to stop deletion. */ - protected function preDelete() + #[\Override] + protected function preDelete(): bool { return $this->fail; } @@ -116,7 +121,8 @@ protected function preDelete() * @see StorableTrait::postDelete() * @return boolean TRUE to indicate object was deleted. */ - protected function postDelete() + #[\Override] + protected function postDelete(): bool { return !$this->fail; } diff --git a/packages/core/tests/Charcoal/Mock/FilterCollectionClass.php b/packages/core/tests/Charcoal/Mock/FilterCollectionClass.php index b7bbac07b..a6b15238c 100644 --- a/packages/core/tests/Charcoal/Mock/FilterCollectionClass.php +++ b/packages/core/tests/Charcoal/Mock/FilterCollectionClass.php @@ -23,7 +23,7 @@ class FilterCollectionClass implements * @param array $data Optional expression data. * @return FilterInterface */ - protected function createFilter(array $data = null) + protected function createFilter(?array $data = null): \Charcoal\Source\Filter { $filter = new Filter(); if ($data !== null) { diff --git a/packages/core/tests/Charcoal/Mock/GenericModel.php b/packages/core/tests/Charcoal/Mock/GenericModel.php index 64e28d826..584f4b326 100644 --- a/packages/core/tests/Charcoal/Mock/GenericModel.php +++ b/packages/core/tests/Charcoal/Mock/GenericModel.php @@ -1,5 +1,7 @@ [ @@ -55,9 +57,8 @@ public function __construct(array $data = null) /** * @param mixed $name The name of the model. - * @return self */ - public function setName($name) + public function setName($name): static { $this->name = $name; return $this; @@ -71,10 +72,7 @@ public function name() return $this->name; } - /** - * @return string - */ - public function icon() + public function icon(): string { return ''; } diff --git a/packages/core/tests/Charcoal/Mock/MockModule.php b/packages/core/tests/Charcoal/Mock/MockModule.php index e9c7f93da..cebde9ac0 100644 --- a/packages/core/tests/Charcoal/Mock/MockModule.php +++ b/packages/core/tests/Charcoal/Mock/MockModule.php @@ -1,5 +1,7 @@ $data The expression data; * as an associative array. - * @return self */ - public function setData(array $data) + #[\Override] + public function setData(array $data): static { parent::setData($data); @@ -40,7 +40,8 @@ public function setData(array $data) * * @return array An associative array. */ - public function defaultData() + #[\Override] + public function defaultData(): array { return [ 'property' => null, @@ -60,7 +61,8 @@ public function defaultData() * * @return array An associative array. */ - public function data() + #[\Override] + public function data(): array { return [ 'property' => $this->property(), @@ -80,9 +82,8 @@ public function data() * * @see OrderCollectionTrait::createOrder() * @param array $data Optional expression data. - * @return self */ - protected function createOrder(array $data = null) + protected function createOrder(?array $data = null): static { $order = new static(); if ($data !== null) { @@ -104,8 +105,6 @@ public function traverse(callable $callable) /** * Clone this expression and its subtree of expressions. - * - * @return void */ public function __clone() { diff --git a/packages/core/tests/Charcoal/Mock/SourceMock.php b/packages/core/tests/Charcoal/Mock/SourceMock.php index c423156ae..b17ff8e66 100644 --- a/packages/core/tests/Charcoal/Mock/SourceMock.php +++ b/packages/core/tests/Charcoal/Mock/SourceMock.php @@ -1,5 +1,7 @@ $this->logger @@ -88,7 +83,7 @@ public function offsetExists($offset) */ public function offsetGet($offset) { - return isset($this->data[$offset]) ? $this->data[$offset] : null; + return $this->data[$offset] ?? null; } /** @@ -96,9 +91,8 @@ public function offsetGet($offset) * * @param mixed $offset The offset to assign the value to. * @param mixed $value The value to set. - * @return void */ - public function offsetSet($offset, $value) + public function offsetSet($offset, $value): void { if ($offset === null) { $this->data[] = $value; @@ -111,9 +105,8 @@ public function offsetSet($offset, $value) * Unset an offset. * * @param mixed $offset The offset to unset. - * @return void */ - public function offsetUnset($offset) + public function offsetUnset($offset): void { unset($this->data[$offset]); } diff --git a/packages/core/tests/Charcoal/Mock/ValidatableClass.php b/packages/core/tests/Charcoal/Mock/ValidatableClass.php index 142032f8a..a2004ff0b 100644 --- a/packages/core/tests/Charcoal/Mock/ValidatableClass.php +++ b/packages/core/tests/Charcoal/Mock/ValidatableClass.php @@ -20,9 +20,8 @@ class ValidatableClass implements ValidatableInterface /** * @param array|null $data Validator data. - * @return ValidatorClass */ - public function createValidator(array $data = null) + public function createValidator(?array $data = null): \Charcoal\Tests\Mock\ValidatorClass { $v = new ValidatorClass(); if ($data !== null) { diff --git a/packages/core/tests/Charcoal/Mock/ValidatorClass.php b/packages/core/tests/Charcoal/Mock/ValidatorClass.php index cae083a0d..43d7d2373 100644 --- a/packages/core/tests/Charcoal/Mock/ValidatorClass.php +++ b/packages/core/tests/Charcoal/Mock/ValidatorClass.php @@ -1,5 +1,7 @@ obj = $this->getMockForAbstractClass(AbstractMetadata::class); } - /** - * @return void - */ - public function testSetData() + public function testSetData(): void { $obj = $this->obj; $ret = $obj->merge([ @@ -39,10 +33,7 @@ public function testSetData() $this->assertEquals('bar', $obj->foo); } - /** - * @return void - */ - public function testArrayAccessOffsetExists() + public function testArrayAccessOffsetExists(): void { $obj = $this->obj; $this->assertFalse(isset($obj['x'])); diff --git a/packages/core/tests/Charcoal/Model/CollectionTest.php b/packages/core/tests/Charcoal/Model/CollectionTest.php index 9ef435593..f13ccda9d 100644 --- a/packages/core/tests/Charcoal/Model/CollectionTest.php +++ b/packages/core/tests/Charcoal/Model/CollectionTest.php @@ -41,9 +41,6 @@ class CollectionTest extends AbstractTestCase */ protected $map; - /** - * @return void - */ protected function setUp(): void { $this->map = [ @@ -66,97 +63,66 @@ protected function setUp(): void // Test \Charcoal\Model\CollectionInterface // ============================================================================================= - - /** - * @return void - */ - public function testCollectionIsConstructed() + public function testCollectionIsConstructed(): void { $c = new Collection; $this->assertSame([], $c->all()); - $c = new Collection(null); + $c = new Collection(); $this->assertSame([], $c->all()); } - /** - * @return void - */ - public function testConstructMethodWithAcceptableData() + public function testConstructMethodWithAcceptableData(): void { [$o1] = $this->arr; $c = new Collection($o1); $this->assertSame([ self::OBJ_1 => $o1 ], $c->all()); } - /** - * @return void - */ - public function testConstructMethodWithUnacceptableData() + public function testConstructMethodWithUnacceptableData(): void { $this->expectException(InvalidArgumentException::class); - $c = new Collection('foo'); + new Collection('foo'); } - /** - * @return void - */ - public function testConstructMethodFromArray() + public function testConstructMethodFromArray(): void { $c = new Collection($this->arr); $this->assertEquals($this->map, $c->all()); } - /** - * @return void - */ - public function testConstructMethodFromTraversable() + public function testConstructMethodFromTraversable(): void { $c = new Collection(new ArrayObject($this->arr)); $this->assertEquals($this->map, $c->all()); } - /** - * @return void - */ - public function testConstructMethodFromCollection() + public function testConstructMethodFromCollection(): void { $c = new Collection(new Collection($this->arr)); $this->assertEquals($this->map, $c->all()); } - /** - * @return void - */ - public function testValues() + public function testValues(): void { $c = new Collection($this->arr); $this->assertEquals($this->arr, $c->values()); } - /** - * @return void - */ - public function testKeys() + public function testKeys(): void { $c = new Collection($this->arr); $this->assertEquals(array_keys($this->map), $c->keys()); } - /** - * @return void - */ - public function testBaseCollection() + public function testBaseCollection(): void { $c = new Collection($this->arr); $this->assertInstanceOf(Collection::class, $c->toBase()); } - /** - * @return void - */ - public function testEmptyCollection() + public function testEmptyCollection(): void { $c = new Collection; @@ -165,10 +131,7 @@ public function testEmptyCollection() $this->assertEquals(null, $c->last()); } - /** - * @return void - */ - public function testFirstItemInCollection() + public function testFirstItemInCollection(): void { [$o1, $o2, $o3, $o4, $o5] = $this->arr; $c = new Collection($this->arr); @@ -176,10 +139,7 @@ public function testFirstItemInCollection() $this->assertEquals($o1, $c->first()); } - /** - * @return void - */ - public function testLastItemInCollection() + public function testLastItemInCollection(): void { [$o1, $o2, $o3, $o4, $o5] = $this->arr; $c = new Collection($this->arr); @@ -187,16 +147,12 @@ public function testLastItemInCollection() $this->assertEquals($o5, $c->last()); } - /** - * @return void - */ - public function testArrayableItems() + public function testArrayableItems(): void { $c = new Collection; $class = new ReflectionClass($c); $method = $class->getMethod('asArray'); - $method->setAccessible(true); $items = new Collection($this->arr); $array = $method->invokeArgs($c, [ $items ]); @@ -211,10 +167,7 @@ public function testArrayableItems() $this->assertSame($this->arr, $array); } - /** - * @return void - */ - public function testRemoveKey() + public function testRemoveKey(): void { [$o1] = $this->arr; @@ -227,30 +180,21 @@ public function testRemoveKey() $this->assertFalse(isset($c[self::OBJ_1])); } - /** - * @return void - */ - public function testAddAcceptableData() + public function testAddAcceptableData(): void { [$o1] = $this->arr; $c = new Collection; $this->assertSame([ $o1->id() => $o1 ], $c->add($o1)->all()); } - /** - * @return void - */ - public function testAddUnacceptableData() + public function testAddUnacceptableData(): void { $this->expectException(InvalidArgumentException::class); $c = new Collection; $c->add('foo'); } - /** - * @return void - */ - public function testGet() + public function testGet(): void { [$o1, $o2, $o3, $o4, $o5] = $this->arr; $c = new Collection($this->arr); @@ -258,10 +202,7 @@ public function testGet() $this->assertSame($o1, $c->get($o1)); } - /** - * @return void - */ - public function testHas() + public function testHas(): void { [$o1, $o2, $o3, $o4, $o5] = $this->arr; $c = new Collection($this->arr); @@ -270,28 +211,19 @@ public function testHas() $this->assertFalse($c->offsetExists('missing')); } - /** - * @return void - */ - public function testClear() + public function testClear(): void { $c = new Collection($this->arr); $this->assertSame([], $c->clear()->all()); } - /** - * @return void - */ - public function testMergeNull() + public function testMergeNull(): void { $c = new Collection($this->arr); $this->assertEquals($this->map, $c->merge(null)->all()); } - /** - * @return void - */ - public function testMergeArray() + public function testMergeArray(): void { [$o1, $o2, $o3, $o4, $o5] = $this->arr; $c = new Collection([ $o1, $o2, $o3, $o4 ]); @@ -299,10 +231,7 @@ public function testMergeArray() $this->assertEquals($this->map, $c->merge([ $o5 ])->all()); } - /** - * @return void - */ - public function testMergeCollection() + public function testMergeCollection(): void { [$o1, $o2, $o3, $o4, $o5] = $this->arr; $c1 = new Collection([ $o1, $o2, $o3, $o4 ]); @@ -313,11 +242,7 @@ public function testMergeCollection() // Test \IteratorAggregate // ============================================================================================= - - /** - * @return void - */ - public function testIterable() + public function testIterable(): void { $c = new Collection($this->arr); @@ -326,10 +251,7 @@ public function testIterable() $this->assertEquals($this->map, $i->getArrayCopy()); } - /** - * @return void - */ - public function testCachingIterator() + public function testCachingIterator(): void { [$o1, $o2, $o3, $o4, $o5] = $this->arr; $c = new Collection($this->arr); @@ -353,11 +275,7 @@ public function testCachingIterator() // Test \Countable // ============================================================================================= - - /** - * @return void - */ - public function testCountable() + public function testCountable(): void { $c = new Collection($this->arr); $this->assertCount(count($this->arr), $c); @@ -365,11 +283,7 @@ public function testCountable() // Test \ArrayAccess // ============================================================================================= - - /** - * @return void - */ - public function testArrayAccess() + public function testArrayAccess(): void { [$o1, $o2, $o3, $o4, $o5] = $this->arr; @@ -390,10 +304,7 @@ public function testArrayAccess() $this->assertEquals($o4, $c[-1]); } - /** - * @return void - */ - public function testArrayAccessOffsetExists() + public function testArrayAccessOffsetExists(): void { $c = new Collection($this->arr); $this->assertTrue($c->offsetExists(0)); @@ -401,10 +312,7 @@ public function testArrayAccessOffsetExists() $this->assertFalse($c->offsetExists(5)); } - /** - * @return void - */ - public function testArrayAccessOffsetGet() + public function testArrayAccessOffsetGet(): void { [$o1, $o2] = $this->arr; @@ -413,10 +321,7 @@ public function testArrayAccessOffsetGet() $this->assertEquals($o2, $c->offsetGet(1)); } - /** - * @return void - */ - public function testArrayAccessOffsetGetWithNegativeOffset() + public function testArrayAccessOffsetGetWithNegativeOffset(): void { [$o1, $o2, $o3, $o4, $o5] = $this->arr; @@ -426,19 +331,13 @@ public function testArrayAccessOffsetGetWithNegativeOffset() $this->assertEquals($o3, $c->offsetGet(-1)); } - /** - * @return void - */ - public function testArrayAccessOffsetGetOnNonExist() + public function testArrayAccessOffsetGetOnNonExist(): void { $c = new Collection($this->arr); $this->assertEquals(null, $c->offsetGet(10)); } - /** - * @return void - */ - public function testArrayAccessOffsetSet() + public function testArrayAccessOffsetSet(): void { [$o1, $o2] = $this->arr; $c = new Collection($o1); @@ -447,11 +346,7 @@ public function testArrayAccessOffsetSet() $this->assertEquals($o2, $c[1]); } - /** - * - * @return void - */ - public function testArrayAccessOffsetSetWithOffset() + public function testArrayAccessOffsetSetWithOffset(): void { $this->expectException(LogicException::class); [$o1] = $this->arr; @@ -460,11 +355,7 @@ public function testArrayAccessOffsetSetWithOffset() $c->offsetSet(1, $o1); } - /** - * - * @return void - */ - public function testArrayAccessOffsetSetWithKey() + public function testArrayAccessOffsetSetWithKey(): void { $this->expectException(LogicException::class); [$o1] = $this->arr; @@ -473,10 +364,7 @@ public function testArrayAccessOffsetSetWithKey() $c->offsetSet(self::OBJ_1, $o1); } - /** - * @return void - */ - public function testArrayAccessOffsetUnset() + public function testArrayAccessOffsetUnset(): void { $c = new Collection($this->arr); @@ -484,10 +372,7 @@ public function testArrayAccessOffsetUnset() $this->assertEquals(null, $c[self::OBJ_2]); } - /** - * @return void - */ - public function testArrayAccessOffsetUnsetWithKey() + public function testArrayAccessOffsetUnsetWithKey(): void { $c = new Collection($this->arr); diff --git a/packages/core/tests/Charcoal/Model/ModelMetadataTest.php b/packages/core/tests/Charcoal/Model/ModelMetadataTest.php index 0af4aefce..61f8e1372 100644 --- a/packages/core/tests/Charcoal/Model/ModelMetadataTest.php +++ b/packages/core/tests/Charcoal/Model/ModelMetadataTest.php @@ -13,23 +13,14 @@ */ class ModelMetadataTest extends AbstractTestCase { - /** - * @var ModelMetadata - */ - private $obj; - - /** - * @return void - */ + private \Charcoal\Model\ModelMetadata $obj; + protected function setUp(): void { $this->obj = new ModelMetadata(); } - /** - * @return void - */ - public function testSetIdent() + public function testSetIdent(): void { $ret = $this->obj->setIdent('foo'); $this->assertSame($ret, $this->obj); @@ -39,10 +30,7 @@ public function testSetIdent() $this->obj->setIdent(false); } - /** - * @return void - */ - public function testArrayAccessGet() + public function testArrayAccessGet(): void { $obj = $this->obj; $obj->foo = 'bar'; @@ -50,10 +38,7 @@ public function testArrayAccessGet() $this->assertEquals($obj->foo, $obj['foo']); } - /** - * @return void - */ - public function testArrayAccessSet() + public function testArrayAccessSet(): void { $obj = $this->obj; $obj['foo'] = 'bar'; @@ -61,25 +46,19 @@ public function testArrayAccessSet() $this->assertEquals($obj->foo, $obj['foo']); } - /** - * @return void - */ - public function testArrayAccessUnset() + public function testArrayAccessUnset(): void { $obj = $this->obj; - $this->assertObjectNotHasAttribute('foo', $obj); + $this->assertFalse(property_exists($obj, 'foo')); $obj['foo'] = 'bar'; - $this->assertObjectHasAttribute('foo', $obj); + $this->assertTrue(property_exists($obj, 'foo')); unset($obj['foo']); //$this->assertObjectNotHasAttribute('foo', $obj); } - /** - * @return void - */ - public function testMerge() + public function testMerge(): void { $data = [ 'foo' => 'bar', @@ -93,10 +72,7 @@ public function testMerge() $this->assertEquals($obj->bar, 'foo'); } - /** - * @return void - */ - public function testMergeIsChainable() + public function testMergeIsChainable(): void { $obj = $this->obj; $ret = $obj->merge([]); diff --git a/packages/core/tests/Charcoal/Model/ModelTest.php b/packages/core/tests/Charcoal/Model/ModelTest.php index dad743937..198a70ad4 100644 --- a/packages/core/tests/Charcoal/Model/ModelTest.php +++ b/packages/core/tests/Charcoal/Model/ModelTest.php @@ -24,10 +24,8 @@ class ModelTest extends AbstractTestCase /** * Retrieve the model's mock metadata. - * - * @return array */ - private function getModelMetadata() + private function getModelMetadata(): array { return [ 'properties' => [ @@ -75,10 +73,8 @@ private function createModel() /** * Drop the SQL table. - * - * @return void */ - private function dropTable() + private function dropTable(): void { $container = $this->getContainer(); @@ -87,10 +83,8 @@ private function dropTable() /** * Retrieve the model's mock object data. - * - * @return array */ - private function getHuxleyData() + private function getHuxleyData(): array { return [ 'id' => 1, @@ -104,7 +98,7 @@ private function getHuxleyData() * * @return integer The saved object ID. */ - private function saveHuxley() + private function saveHuxley(): bool { $obj = $this->obj; $obj->setData($this->getHuxleyData()); @@ -116,8 +110,6 @@ private function saveHuxley() * Set up the test. * * Create the SQL table for the test, dropping any existing table. - * - * @return void */ protected function setUp(): void { @@ -130,17 +122,12 @@ protected function setUp(): void * Tear down the test. * * Drop any existing SQL table. - * - * @return void */ protected function tearDown(): void { $this->dropTable(); } - /** - * @return void - */ - public function testConstructor() + public function testConstructor(): void { $obj = $this->obj; @@ -148,10 +135,7 @@ public function testConstructor() $this->assertInstanceOf(ModelInterface::class, $obj); } - /** - * @return void - */ - public function testSetData() + public function testSetData(): void { $obj = $this->obj; $ret = $obj->setData([ 'name' => 'Orwell' ]); @@ -160,10 +144,7 @@ public function testSetData() $this->assertEquals('Orwell', $obj->name); } - /** - * @return void - */ - public function testSetFlatData() + public function testSetFlatData(): void { $obj = $this->obj; $ret = $obj->setFlatData([ 'name' => 'Clarke' ]); @@ -172,22 +153,16 @@ public function testSetFlatData() $this->assertEquals('Clarke', $obj->name); } - /** - * @return void - */ - public function testSave() + public function testSave(): void { $ret = $this->saveHuxley(); $this->assertEquals(1, $ret); } - /** - * @return void - */ - public function testLoad() + public function testLoad(): void { - $ret = $this->saveHuxley(); + $this->saveHuxley(); $obj1 = $this->createModel(); $obj1->load(1); @@ -195,10 +170,7 @@ public function testLoad() $this->assertEquals($this->getHuxleyData(), $obj1->data()); } - /** - * @return void - */ - public function testUpdate() + public function testUpdate(): void { $ret = $this->saveHuxley(); @@ -226,10 +198,7 @@ public function testUpdate() $this->assertEquals('Screenwriter', $obj2['role']); } - /** - * @return void - */ - public function testDelete() + public function testDelete(): void { $ret = $this->saveHuxley(); @@ -242,10 +211,7 @@ public function testDelete() $this->assertEquals(null, $obj1['id']); } - /** - * @return void - */ - public function testSerializeUnserialize() + public function testSerializeUnserialize(): void { $obj = $this->obj; $data = $this->getHuxleyData(); @@ -265,10 +231,7 @@ public function testSerializeUnserialize() $this->assertEquals('Huxley', $obj2['name']); } - /** - * @return void - */ - public function testJsonSerialize() + public function testJsonSerialize(): void { $obj = $this->obj; $data = $this->getHuxleyData(); diff --git a/packages/core/tests/Charcoal/Model/ModelValidatorTest.php b/packages/core/tests/Charcoal/Model/ModelValidatorTest.php index 9fd4736e7..d8d88e5f9 100644 --- a/packages/core/tests/Charcoal/Model/ModelValidatorTest.php +++ b/packages/core/tests/Charcoal/Model/ModelValidatorTest.php @@ -1,5 +1,7 @@ getContainer(); @@ -31,20 +30,14 @@ protected function model() ]); } - /** - * @return void - */ - public function testConstructor() + public function testConstructor(): void { $model = $this->model(); $obj = new ModelValidator($model); $this->assertInstanceOf(ModelValidator::class, $obj); } - /** - * @return void - */ - public function testValidateModel() + public function testValidateModel(): void { $model = $this->model(); $model->setMetadata([ diff --git a/packages/core/tests/Charcoal/Model/Service/MetadataLoaderTest.php b/packages/core/tests/Charcoal/Model/Service/MetadataLoaderTest.php index fd02a4cae..e8198d818 100644 --- a/packages/core/tests/Charcoal/Model/Service/MetadataLoaderTest.php +++ b/packages/core/tests/Charcoal/Model/Service/MetadataLoaderTest.php @@ -1,5 +1,7 @@ getContainer(); @@ -33,10 +32,7 @@ protected function setUp(): void ]); } - /** - * @return void - */ - public function testLoadData() + public function testLoadData(): void { $this->assertInstanceOf(MetadataLoader::class, $this->obj); //$ret = $this->obj->load('test', $this->); diff --git a/packages/core/tests/Charcoal/Model/Service/ModelLoaderBuilderTest.php b/packages/core/tests/Charcoal/Model/Service/ModelLoaderBuilderTest.php index fd973062a..0648993fc 100644 --- a/packages/core/tests/Charcoal/Model/Service/ModelLoaderBuilderTest.php +++ b/packages/core/tests/Charcoal/Model/Service/ModelLoaderBuilderTest.php @@ -24,9 +24,6 @@ class ModelLoaderBuilderTest extends AbstractTestCase */ public $obj; - /** - * @return void - */ protected function setUp(): void { $container = $this->getContainer(); @@ -52,19 +49,13 @@ protected function setUp(): void ]); } - /** - * @return void - */ - public function testBuild() + public function testBuild(): void { $ret = $this->obj->build(GenericModel::class, 'name'); $this->assertInstanceOf(ModelLoader::class, $ret); } - /** - * @return void - */ - public function testInvokable() + public function testInvokable(): void { $builder = $this->obj; $ret = $builder(GenericModel::class); diff --git a/packages/core/tests/Charcoal/Model/Service/ModelLoaderTest.php b/packages/core/tests/Charcoal/Model/Service/ModelLoaderTest.php index 3390e30a0..5553c5fab 100644 --- a/packages/core/tests/Charcoal/Model/Service/ModelLoaderTest.php +++ b/packages/core/tests/Charcoal/Model/Service/ModelLoaderTest.php @@ -1,5 +1,7 @@ expectException(Exception::class); $this->obj->load('foobar'); diff --git a/packages/core/tests/Charcoal/Model/ServiceProvider/ModelServiceProviderTest.php b/packages/core/tests/Charcoal/Model/ServiceProvider/ModelServiceProviderTest.php index d2e5704a2..915f47222 100644 --- a/packages/core/tests/Charcoal/Model/ServiceProvider/ModelServiceProviderTest.php +++ b/packages/core/tests/Charcoal/Model/ServiceProvider/ModelServiceProviderTest.php @@ -51,9 +51,6 @@ class ModelServiceProviderTest extends AbstractTestCase public $obj; - /** - * @return void - */ protected function setUp(): void { $this->obj = new ModelServiceProvider(); @@ -70,9 +67,8 @@ protected function setUp(): void * - view * * @todo Use ContainerIntegrationTrait? - * @return Container */ - private function container() + private function container(): \Pimple\Container { $container = new Container(); @@ -117,10 +113,7 @@ private function container() return $container; } - /** - * @return void - */ - public function testFactories() + public function testFactories(): void { $container = $this->container(); @@ -136,10 +129,7 @@ public function testFactories() $this->assertInstanceOf(FactoryInterface::class, $container['source/factory']); } - /** - * @return void - */ - public function testRegisterSetsModelBuilder() + public function testRegisterSetsModelBuilder(): void { $container = $this->container(); $this->obj->register($container); @@ -148,10 +138,7 @@ public function testRegisterSetsModelBuilder() $this->assertInstanceOf(ModelBuilder::class, $container['model/builder']); } - /** - * @return void - */ - public function testRegisterSetsModelLoaderBuilder() + public function testRegisterSetsModelLoaderBuilder(): void { $container = $this->container(); $this->obj->register($container); @@ -160,10 +147,7 @@ public function testRegisterSetsModelLoaderBuilder() $this->assertInstanceOf(ModelLoaderBuilder::class, $container['model/loader/builder']); } - /** - * @return void - */ - public function testRegisterSetsMetadataLoader() + public function testRegisterSetsMetadataLoader(): void { $container = $this->container(); $this->obj->register($container); @@ -172,17 +156,14 @@ public function testRegisterSetsMetadataLoader() $this->assertInstanceOf(MetadataLoader::class, $container['metadata/loader']); } - /** - * @return void - */ - public function testExtraMetadataPaths() + public function testExtraMetadataPaths(): void { $container = new Container([ 'config' => [ - 'base_path' => dirname(dirname(dirname(dirname(__DIR__)))), + 'base_path' => dirname(__DIR__, 4), ], 'module/classes' => [ - 'Charcoal\\Tests\\Mock\\MockModule', + \Charcoal\Tests\Mock\MockModule::class, ], ]); diff --git a/packages/core/tests/Charcoal/ReflectionsTrait.php b/packages/core/tests/Charcoal/ReflectionsTrait.php index 3a63ff2b1..047d2c172 100644 --- a/packages/core/tests/Charcoal/ReflectionsTrait.php +++ b/packages/core/tests/Charcoal/ReflectionsTrait.php @@ -18,13 +18,10 @@ trait ReflectionsTrait * * @param mixed $class The class name or object that contains the method. * @param string $name The method name to reflect. - * @return ReflectionMethod */ - public function getMethod($class, $name) + public function getMethod($class, $name): \ReflectionMethod { - $reflected = new ReflectionMethod($class, $name); - $reflected->setAccessible(true); - return $reflected; + return new ReflectionMethod($class, $name); } /** @@ -38,7 +35,7 @@ public function getMethod($class, $name) public function callMethod($object, $name, array $args = []) { $method = $this->getMethod($object, $name); - if (empty($args)) { + if ($args === []) { return $method->invoke($object); } else { return $method->invokeArgs($object, $args); @@ -65,13 +62,10 @@ public function callMethodWith($object, $name, ...$args) * * @param mixed $class The class name or object that contains the property. * @param string $name The property name to reflect. - * @return ReflectionProperty */ - public function getProperty($class, $name) + public function getProperty($class, $name): \ReflectionProperty { - $reflected = new ReflectionProperty($class, $name); - $reflected->setAccessible(true); - return $reflected; + return new ReflectionProperty($class, $name); } /** @@ -92,9 +86,8 @@ public function getPropertyValue($object, $name) * @param mixed $object The object to access. * @param string $name The property name to affect. * @param mixed $value The new value. - * @return void */ - public function setPropertyValue($object, $name, $value) + public function setPropertyValue($object, $name, $value): void { $this->getProperty($object, $name)->setValue($object, $value); } diff --git a/packages/core/tests/Charcoal/Source/AbstractExpressionTest.php b/packages/core/tests/Charcoal/Source/AbstractExpressionTest.php index a82b15ba5..2aea20c62 100644 --- a/packages/core/tests/Charcoal/Source/AbstractExpressionTest.php +++ b/packages/core/tests/Charcoal/Source/AbstractExpressionTest.php @@ -37,10 +37,8 @@ final protected function createExpression() * 1. Default state * 2. Mutated state * 3. Chainable method - * - * @return void */ - public function testName() + public function testName(): void { $obj = $this->createExpression(); @@ -57,10 +55,8 @@ public function testName() /** * Test "name" property with invalid value. - * - * @return void */ - public function testNameWithInvalidValue() + public function testNameWithInvalidValue(): void { $this->expectException(InvalidArgumentException::class); $this->createExpression()->setName(0); @@ -74,10 +70,8 @@ public function testNameWithInvalidValue() * 2. Mutated state * 3. Chainable method * 4. Cast value to boolean - * - * @return void */ - public function testActive() + public function testActive(): void { $obj = $this->createExpression(); @@ -102,13 +96,12 @@ public function testActive() /** * Test value parsing. * - * @dataProvider provideParsableValues * * @param mixed $value The value to test. * @param mixed $expected The expected result. - * @return void */ - public function testParseValue($value, $expected) + #[\PHPUnit\Framework\Attributes\DataProvider('provideParsableValues')] + public function testParseValue($value, int|string|bool|null $expected): void { $obj = $this->createExpression(); @@ -119,9 +112,8 @@ public function testParseValue($value, $expected) * Provide data for value parsing. * * @used-by self::testParseValue() - * @return array */ - public function provideParsableValues() + public function provideParsableValues(): array { $container = $this->getContainer(); @@ -144,13 +136,12 @@ public function provideParsableValues() /** * Test value quoting. * - * @dataProvider provideQuotableValues * * @param mixed $value The value to test. * @param mixed $expected The expected result. - * @return void */ - public function testQuoteValue($value, $expected) + #[\PHPUnit\Framework\Attributes\DataProvider('provideQuotableValues')] + public function testQuoteValue(int|string|bool|\stdClass|array|null $value, int|string|\stdClass|array|null $expected): void { $obj = $this->createExpression(); @@ -161,9 +152,8 @@ public function testQuoteValue($value, $expected) * Provide data for value quoting. * * @used-by self::testQuoteValue() - * @return array */ - public function provideQuotableValues() + public static function provideQuotableValues(): array { $obj = new stdClass(); @@ -182,14 +172,13 @@ public function provideQuotableValues() /** * Test field quoting. * - * @dataProvider provideQuotableIdentifiers * * @param mixed $fieldName The field name. * @param mixed $tableName The table name. * @param mixed $expected The expected identifier. - * @return void */ - public function testQuoteIdentifier($fieldName, $tableName, $expected) + #[\PHPUnit\Framework\Attributes\DataProvider('provideQuotableIdentifiers')] + public function testQuoteIdentifier(?string $fieldName, ?string $tableName, string $expected): void { $obj = $this->createExpression(); @@ -200,9 +189,8 @@ public function testQuoteIdentifier($fieldName, $tableName, $expected) * Provide data for field quoting. * * @used-by self::testQuoteIdentifier() - * @return array */ - public function provideQuotableIdentifiers() + public static function provideQuotableIdentifiers(): array { return [ [ null, null, '' ], @@ -216,10 +204,8 @@ public function provideQuotableIdentifiers() /** * Test field quoting with invalid field name. - * - * @return void */ - public function testQuoteIdentifierWithInvalidFieldName() + public function testQuoteIdentifierWithInvalidFieldName(): void { $this->expectException(InvalidArgumentException::class); $obj = $this->createExpression(); @@ -228,10 +214,8 @@ public function testQuoteIdentifierWithInvalidFieldName() /** * Test field quoting with blank table name. - * - * @return void */ - public function testQuoteIdentifierWithBlankTableName() + public function testQuoteIdentifierWithBlankTableName(): void { $this->expectException(InvalidArgumentException::class); $obj = $this->createExpression(); @@ -240,10 +224,8 @@ public function testQuoteIdentifierWithBlankTableName() /** * Test field quoting with invalid table name. - * - * @return void */ - public function testQuoteIdentifierWithInvalidTableName() + public function testQuoteIdentifierWithInvalidTableName(): void { $this->expectException(InvalidArgumentException::class); $obj = $this->createExpression(); @@ -253,14 +235,13 @@ public function testQuoteIdentifierWithInvalidTableName() /** * Test value differentiation. * - * @dataProvider provideDiffValues * * @param mixed $a The custom value. * @param mixed $b The default value. * @param mixed $expected The expected result. - * @return void */ - public function testDiffValues($a, $b, $expected) + #[\PHPUnit\Framework\Attributes\DataProvider('provideDiffValues')] + public function testDiffValues(int $a, int|string $b, int $expected): void { $obj = $this->createExpression(); @@ -271,9 +252,8 @@ public function testDiffValues($a, $b, $expected) * Provide data for value differentiation. * * @used-by self::testDiffValues() - * @return array */ - public function provideDiffValues() + public static function provideDiffValues(): array { return [ 'Same Type' => [ 5, 5, 0 ], @@ -284,13 +264,12 @@ public function provideDiffValues() /** * Test callable detection. * - * @dataProvider provideCallableValues * * @param mixed $value The value to test. * @param mixed $expected The expected result. - * @return void */ - public function testIsCallable($value, $expected) + #[\PHPUnit\Framework\Attributes\DataProvider('provideCallableValues')] + public function testIsCallable(string|\Closure|null $value, bool $expected): void { $obj = $this->createExpression(); @@ -301,14 +280,13 @@ public function testIsCallable($value, $expected) * Provide data for callable detection. * * @used-by self::testIsCallable() - * @return array */ - public function provideCallableValues() + public static function provideCallableValues(): array { return [ 'Null Type' => [ null, false ], 'String Type' => [ 'strval', false ], - 'Closure' => [ function () { + 'Closure' => [ function (): void { }, true ], ]; } diff --git a/packages/core/tests/Charcoal/Source/AbstractSourceTest.php b/packages/core/tests/Charcoal/Source/AbstractSourceTest.php index 744c195a0..1238899a6 100644 --- a/packages/core/tests/Charcoal/Source/AbstractSourceTest.php +++ b/packages/core/tests/Charcoal/Source/AbstractSourceTest.php @@ -34,6 +34,25 @@ /** * Test {@see AbstractSource} and {@see SourceInterface}. */ +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\AbstractSource::class, 'setProperties')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\AbstractSource::class, 'addProperties')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\AbstractSource::class, 'resolvePropertyName')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\AbstractSource::class, 'hasProperties')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\AbstractSource::class, 'addProperty')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\AbstractSource::class, 'removeProperty')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\AbstractSource::class, 'addFilter')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\AbstractSource::class, 'parseFilterWithModel')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\AbstractSource::class, 'createFilter')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\AbstractSource::class, 'addOrder')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\AbstractSource::class, 'parseOrderWithModel')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\AbstractSource::class, 'createOrder')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\AbstractSource::class, 'pagination')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\AbstractSource::class, 'hasPagination')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\AbstractSource::class, 'setPagination')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\AbstractSource::class, 'createPagination')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\AbstractSource::class, 'camelize')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\AbstractSource::class, 'getter')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\AbstractSource::class, 'setter')] class AbstractSourceTest extends AbstractTestCase { use AssertionsTrait; @@ -49,8 +68,6 @@ class AbstractSourceTest extends AbstractTestCase /** * Setup the test. - * - * @return void */ protected function setUp(): void { @@ -83,10 +100,8 @@ final public function createProperty() * - clear the filters * - clear the orders * - @todo clear the pagination - * - * @return void */ - public function testReset() + public function testReset(): void { $obj = $this->obj; $filter = $this->createFilter(); @@ -114,10 +129,8 @@ public function testReset() * Assert that the `setData` method: * - is chainable * - set the data (properties, filters, orders & pagination) - * - * @return void */ - public function testSetData() + public function testSetData(): void { $obj = $this->obj; $ret = $obj->setData([ @@ -137,10 +150,8 @@ public function testSetData() * Assert that the `setModel` method: * - is chainable * - set the model (retrievable with the `model` method) - * - * @return void */ - public function testSetModel() + public function testSetModel(): void { $container = $this->getContainer(); @@ -159,10 +170,7 @@ public function testSetModel() $this->assertSame($model, $obj->model()); } - /** - * @return void - */ - public function testModelWithoutSetThrowsException() + public function testModelWithoutSetThrowsException(): void { $obj = $this->obj; $this->expectException(RuntimeException::class); @@ -175,13 +183,8 @@ public function testModelWithoutSetThrowsException() * - set the properties * - reset the properties, when called again * - * @covers \Charcoal\Source\AbstractSource::setProperties - * @covers \Charcoal\Source\AbstractSource::addProperties - * @covers \Charcoal\Source\AbstractSource::resolvePropertyName - * - * @return void */ - public function testSetProperties() + public function testSetProperties(): void { $obj = $this->obj; $ret = $obj->setProperties([ 'foo' ]); @@ -199,11 +202,8 @@ public function testSetProperties() * 1. Empty; Default state * 2. Populated; Mutated state * - * @covers \Charcoal\Source\AbstractSource::hasProperties - * - * @return void */ - public function testHasProperties() + public function testHasProperties(): void { $obj = $this->obj; @@ -218,12 +218,8 @@ public function testHasProperties() /** * Test property collection appending. * - * @covers \Charcoal\Source\AbstractSource::addProperty - * @covers \Charcoal\Source\AbstractSource::resolvePropertyName - * - * @return void */ - public function testAddProperty() + public function testAddProperty(): void { $obj = $this->obj; $this->assertEquals([], $obj->properties()); @@ -238,12 +234,8 @@ public function testAddProperty() /** * Test property collection appending. * - * @covers \Charcoal\Source\AbstractSource::removeProperty - * @covers \Charcoal\Source\AbstractSource::resolvePropertyName - * - * @return void */ - public function testRemoveProperty() + public function testRemoveProperty(): void { $obj = $this->obj; $obj->setProperties([ 'foo', 'bar', 'qux' ]); @@ -256,11 +248,8 @@ public function testRemoveProperty() /** * Test failure when appending an invalid property name. * - * @covers \Charcoal\Source\AbstractSource::resolvePropertyName - * - * @return void */ - public function testInvalidPropertyNameResolution() + public function testInvalidPropertyNameResolution(): void { $obj = $this->obj; $this->expectException(InvalidArgumentException::class); @@ -270,11 +259,8 @@ public function testInvalidPropertyNameResolution() /** * Test failure when appending an blank property name. * - * @covers \Charcoal\Source\AbstractSource::resolvePropertyName - * - * @return void */ - public function testBlankPropertyNameResolution() + public function testBlankPropertyNameResolution(): void { $obj = $this->obj; $this->expectException(InvalidArgumentException::class); @@ -284,11 +270,8 @@ public function testBlankPropertyNameResolution() /** * Test failure when appending a unnamed property object. * - * @covers \Charcoal\Source\AbstractSource::resolvePropertyName - * - * @return void */ - public function testAnonymousPropertyNameResolution() + public function testAnonymousPropertyNameResolution(): void { $obj = $this->obj; $prop = $this->createProperty()->setIdent(''); @@ -299,11 +282,8 @@ public function testAnonymousPropertyNameResolution() /** * Test appending a named property object. * - * @covers \Charcoal\Source\AbstractSource::resolvePropertyName - * - * @return void */ - public function testNamedPropertyNameResolution() + public function testNamedPropertyNameResolution(): void { $obj = $this->obj; $prop = $this->createProperty(); @@ -329,11 +309,8 @@ public function testNamedPropertyNameResolution() * an Expression object with given extra data is returned * 6. Chainable method * - * @covers \Charcoal\Source\AbstractSource::addFilter - * - * @return void */ - public function testAddFilter() + public function testAddFilter(): void { $obj = $this->obj; @@ -394,11 +371,8 @@ public function testAddFilter() * 3. If a tree of expressions is passed, the source will traverse * all expressions. * - * @covers \Charcoal\Source\AbstractSource::parseFilterWithModel - * - * @return void */ - public function testParseFilterWithModel() + public function testParseFilterWithModel(): void { $model = $this->createModel(); $source = $this->obj; @@ -438,11 +412,8 @@ public function testParseFilterWithModel() * 2. Instance of {@see Filter} * * @see \Charcoal\Tests\Source\FilterTest::testCreateFilter - * @covers \Charcoal\Source\AbstractSource::createFilter - * - * @return void */ - public function testCreateFilter() + public function testCreateFilter(): void { $result = $this->callMethodWith($this->obj, 'createFilter', [ 'name' => 'foo' ]); $this->assertInstanceOf(Filter::class, $result); @@ -467,11 +438,8 @@ public function testCreateFilter() * an Expression object with given extra data is returned * 6. Chainable method * - * @covers \Charcoal\Source\AbstractSource::addOrder - * - * @return void */ - public function testAddOrder() + public function testAddOrder(): void { $obj = $this->obj; @@ -530,11 +498,8 @@ public function testAddOrder() * 2. If a tree of expressions is passed, the source will traverse * all expressions. * - * @covers \Charcoal\Source\AbstractSource::parseOrderWithModel - * - * @return void */ - public function testParseOrderWithModel() + public function testParseOrderWithModel(): void { $model = $this->createModel(); $source = $this->obj; @@ -567,11 +532,8 @@ public function testParseOrderWithModel() * 1. Instance of {@see ExpressionInterface} * 2. Instance of {@see Order} * - * @covers \Charcoal\Source\AbstractSource::createOrder - * - * @return void */ - public function testCreateOrder() + public function testCreateOrder(): void { $result = $this->callMethodWith($this->obj, 'createOrder', [ 'name' => 'foo' ]); $this->assertInstanceOf(Order::class, $result); @@ -586,12 +548,8 @@ public function testCreateOrder() * 1. Default state is NULL * 2. Create paginator if state is NULL * - * @covers \Charcoal\Source\AbstractSource::pagination - * @covers \Charcoal\Source\AbstractSource::hasPagination - * - * @return void */ - public function testGetPagination() + public function testGetPagination(): void { /** 1. Default state is NULL */ $this->assertFalse($this->obj->hasPagination()); @@ -612,11 +570,8 @@ public function testGetPagination() * 4. Accepts up to two numeric arguments * 5. Chainable method * - * @covers \Charcoal\Source\AbstractSource::setPagination - * - * @return void */ - public function testSetPagination() + public function testSetPagination(): void { $obj = $this->obj; @@ -653,11 +608,8 @@ public function testSetPagination() /** * Test the failure when assigning an invalid pagination expression. * - * @covers \Charcoal\Source\AbstractSource::setPagination - * - * @return void */ - public function testProcessExpressionWithInvalidValue() + public function testProcessExpressionWithInvalidValue(): void { $this->expectException(InvalidArgumentException::class); $this->obj->setPagination(false); @@ -670,11 +622,8 @@ public function testProcessExpressionWithInvalidValue() * 1. Instance of {@see ExpressionInterface} * 2. Instance of {@see PaginationInterface} * - * @covers \Charcoal\Source\AbstractSource::createPagination - * - * @return void */ - public function testCreatePagination() + public function testCreatePagination(): void { $result = $this->callMethodWith($this->obj, 'createPagination', [ 'name' => 'foo' ]); $this->assertInstanceOf(Pagination::class, $result); @@ -682,10 +631,7 @@ public function testCreatePagination() $this->assertEquals('foo', $result->name()); } - /** - * @return void - */ - public function testSetPage() + public function testSetPage(): void { $obj = $this->obj; $this->assertEquals(1, $obj->page()); @@ -698,10 +644,7 @@ public function testSetPage() $obj->setPage('foo'); } - /** - * @return void - */ - public function testNumPerPage() + public function testNumPerPage(): void { $obj = $this->obj; $this->assertEquals(Pagination::DEFAULT_COUNT, $obj->numPerPage()); @@ -713,10 +656,7 @@ public function testNumPerPage() $obj->setNumPerPage('foobar'); } - /** - * @return void - */ - public function testCreateConfig() + public function testCreateConfig(): void { $obj = $this->obj; $config = $obj->createConfig([ 'type' => 'foo' ]); @@ -727,13 +667,8 @@ public function testCreateConfig() /** * Test camelization. * - * @covers \Charcoal\Source\AbstractSource::camelize - * @covers \Charcoal\Source\AbstractSource::getter - * @covers \Charcoal\Source\AbstractSource::setter - * - * @return void */ - public function testCamelize() + public function testCamelize(): void { $obj = $this->obj; @@ -748,9 +683,8 @@ public function testCamelize() * Create a query filter expression, for testing. * * @param array $data Optional expression data. - * @return Filter */ - final public function createFilter(array $data = null) + final public function createFilter(?array $data = null): \Charcoal\Source\Filter { $expr = new Filter(); if ($data !== null) { @@ -763,9 +697,8 @@ final public function createFilter(array $data = null) * Create query sorting expression, for testing. * * @param array $data Optional expression data. - * @return Order */ - final public function createOrder(array $data = null) + final public function createOrder(?array $data = null): \Charcoal\Source\Order { $expr = new Order(); if ($data !== null) { @@ -778,9 +711,8 @@ final public function createOrder(array $data = null) * Create query pagination expression, for testing. * * @param array $data Optional expression data. - * @return Pagination */ - final public function createPagination(array $data = null) + final public function createPagination(?array $data = null): \Charcoal\Source\Pagination { $expr = new Pagination(); if ($data !== null) { @@ -806,10 +738,8 @@ final protected function createModel() /** * Retrieve the model's mock metadata. - * - * @return array */ - final protected function getModelMetadata() + final protected function getModelMetadata(): array { return [ 'properties' => [ diff --git a/packages/core/tests/Charcoal/Source/Database/DatabaseExpressionTest.php b/packages/core/tests/Charcoal/Source/Database/DatabaseExpressionTest.php index 083ff6141..f7f34bccd 100644 --- a/packages/core/tests/Charcoal/Source/Database/DatabaseExpressionTest.php +++ b/packages/core/tests/Charcoal/Source/Database/DatabaseExpressionTest.php @@ -21,20 +21,16 @@ class DatabaseExpressionTest extends AbstractTestCase /** * Create expression for testing. - * - * @return DatabaseExpression */ - final protected function createExpression() + final protected function createExpression(): \Charcoal\Source\Database\DatabaseExpression { return new DatabaseExpression(); } /** * Test influence of "active" property on SQL compilation. - * - * @return void */ - public function testInactiveExpression() + public function testInactiveExpression(): void { $obj = $this->createExpression(); $obj->setCondition(' /* xyzzy */ '); @@ -48,10 +44,8 @@ public function testInactiveExpression() /** * Test "condition" property. - * - * @return void */ - public function testCustomSql() + public function testCustomSql(): void { $obj = $this->createExpression(); @@ -61,10 +55,8 @@ public function testCustomSql() /** * Test invalid custom SQL. - * - * @return void */ - public function testCustomSqlWithoutQuery() + public function testCustomSqlWithoutQuery(): void { $obj = $this->createExpression(); diff --git a/packages/core/tests/Charcoal/Source/Database/DatabaseFilterTest.php b/packages/core/tests/Charcoal/Source/Database/DatabaseFilterTest.php index 9e4e0b543..e13b8525a 100644 --- a/packages/core/tests/Charcoal/Source/Database/DatabaseFilterTest.php +++ b/packages/core/tests/Charcoal/Source/Database/DatabaseFilterTest.php @@ -21,6 +21,7 @@ /** * Test {@see DatabaseFilter}. */ +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\Database\DatabaseFilter::class, 'isNegating')] class DatabaseFilterTest extends AbstractTestCase { use CoreContainerIntegrationTrait; @@ -29,10 +30,8 @@ class DatabaseFilterTest extends AbstractTestCase /** * Create expression for testing. - * - * @return DatabaseFilter */ - final protected function createExpression() + final protected function createExpression(): \Charcoal\Source\Database\DatabaseFilter { return new DatabaseFilter(); } @@ -56,10 +55,8 @@ final public function createProperty() * Test default table name for default data values. * * @see \Charcoal\Tests\Source\Database\DatabaseOrderTest::testDefaultValues() - * - * @return void */ - public function testDefaultValues() + public function testDefaultValues(): void { $obj = $this->createExpression(); @@ -71,10 +68,8 @@ public function testDefaultValues() /** * Test influence of "active" property on SQL compilation. - * - * @return void */ - public function testInactiveExpression() + public function testInactiveExpression(): void { $obj = $this->createExpression(); $obj->setProperty('foo')->setValue('Charcoal'); @@ -94,11 +89,8 @@ public function testInactiveExpression() * 2. Negatable Operators * 3. Ignored Operators * - * @covers \Charcoal\Source\Database\DatabaseFilter::isNegating - * - * @return void */ - public function testNegation() + public function testNegation(): void { $obj = $this->createExpression(); @@ -119,10 +111,8 @@ public function testNegation() /** * Test SQL without conditions. - * - * @return void */ - public function testBlankSql() + public function testBlankSql(): void { $obj = $this->createExpression(); @@ -131,10 +121,8 @@ public function testBlankSql() /** * Test invalid SQL predicate. - * - * @return void */ - public function testSqlWithoutPredicate() + public function testSqlWithoutPredicate(): void { $obj = $this->createExpression(); @@ -147,13 +135,12 @@ public function testSqlWithoutPredicate() /** * Test nested filters. * - * @dataProvider providedNestedExpressions * * @param array $conditions The expressions to define. * @param string $expected The expected compiled SQL string. - * @return void */ - public function testNestedSql(array $conditions, $expected) + #[\PHPUnit\Framework\Attributes\DataProvider('providedNestedExpressions')] + public function testNestedSql(array $conditions, $expected): void { $obj = $this->createExpression(); $obj->addFilters($conditions); @@ -165,9 +152,8 @@ public function testNestedSql(array $conditions, $expected) * * @example [ [ , ] ] * @used-by self::testNestedSql() - * @return array */ - public function providedNestedExpressions() + public function providedNestedExpressions(): array { return [ 'One Level' => $this->nestedExpressionsDataset1(), @@ -179,9 +165,8 @@ public function providedNestedExpressions() * Dataset #1 for testing nested expressions. * * @used-by self::providedNestedExpressions() - * @return array */ - protected function nestedExpressionsDataset1() + protected function nestedExpressionsDataset1(): array { $time = new DateTime('3 days ago'); @@ -213,9 +198,8 @@ protected function nestedExpressionsDataset1() * Dataset #2 for testing nested expressions. * * @used-by self::providedNestedExpressions() - * @return array */ - protected function nestedExpressionsDataset2() + protected function nestedExpressionsDataset2(): array { $time = date('Y-m-d'); @@ -265,10 +249,8 @@ protected function nestedExpressionsDataset2() /** * Test nested filters has precedence over other features. - * - * @return void */ - public function testNestedSqlPrecedence() + public function testNestedSqlPrecedence(): void { $obj = $this->createExpression(); @@ -282,10 +264,8 @@ public function testNestedSqlPrecedence() /** * Test invalid SQL nested filters. - * - * @return void */ - public function testSqlWithoutNestedExpressions() + public function testSqlWithoutNestedExpressions(): void { $obj = $this->createExpression(); @@ -297,10 +277,8 @@ public function testSqlWithoutNestedExpressions() /** * Test "condition" property with and without placeholders. - * - * @return void */ - public function testCustomSql() + public function testCustomSql(): void { $obj = $this->createExpression(); @@ -310,10 +288,8 @@ public function testCustomSql() /** * Test the negation of the "condition" property with the "operator" property. - * - * @return void */ - public function testCustomSqlNegation() + public function testCustomSqlNegation(): void { $obj = $this->createExpression(); @@ -323,10 +299,8 @@ public function testCustomSqlNegation() /** * Test "condition" property has precedence over other features. - * - * @return void */ - public function testCustomSqlPrecedence() + public function testCustomSqlPrecedence(): void { $obj = $this->createExpression(); @@ -340,10 +314,8 @@ public function testCustomSqlPrecedence() /** * Test invalid custom SQL. - * - * @return void */ - public function testCustomSqlWithoutQuery() + public function testCustomSqlWithoutQuery(): void { $obj = $this->createExpression(); @@ -355,10 +327,8 @@ public function testCustomSqlWithoutQuery() /** * Test condition compilation. - * - * @return void */ - public function testCompileConditions() + public function testCompileConditions(): void { $obj = $this->createExpression(); @@ -369,10 +339,8 @@ public function testCompileConditions() /** * Test basic SQL operator without a value. - * - * @return void */ - public function testSqlOperatorWithoutValue() + public function testSqlOperatorWithoutValue(): void { $obj = $this->createExpression(); @@ -388,12 +356,11 @@ public function testSqlOperatorWithoutValue() /** * Test comparison SQL operators. * - * @dataProvider provideComparisonOperators * * @param string $operator A SQL operator. - * @return void */ - public function testSqlComparisonOperators($operator) + #[\PHPUnit\Framework\Attributes\DataProvider('provideComparisonOperators')] + public function testSqlComparisonOperators(string $operator): void { $obj = $this->createExpression(); $obj->setData([ @@ -408,12 +375,11 @@ public function testSqlComparisonOperators($operator) /** * Test condition-style SQL operators ("value" is ignored). * - * @dataProvider provideConditionalOperators * * @param string $operator A SQL operator. - * @return void */ - public function testSqlConditionalOperators($operator) + #[\PHPUnit\Framework\Attributes\DataProvider('provideConditionalOperators')] + public function testSqlConditionalOperators(string $operator): void { $obj = $this->createExpression(); $obj->setData([ @@ -428,12 +394,11 @@ public function testSqlConditionalOperators($operator) /** * Test NOT-style SQL operators ("value" is ignored). * - * @dataProvider provideNegationOperators * * @param string $operator A SQL operator. - * @return void */ - public function testSqlNegationOperators($operator) + #[\PHPUnit\Framework\Attributes\DataProvider('provideNegationOperators')] + public function testSqlNegationOperators(string $operator): void { $obj = $this->createExpression(); $obj->setData([ @@ -448,14 +413,13 @@ public function testSqlNegationOperators($operator) /** * Test list-based SQL operators. * - * @dataProvider provideSetOperators * * @param string $operator A SQL operator. * @param string $delimiter The set's delimiter. * @param string $expected The expected result. - * @return void */ - public function testSqlSetOperators($operator, $delimiter, $expected) + #[\PHPUnit\Framework\Attributes\DataProvider('provideSetOperators')] + public function testSqlSetOperators(string $operator, string $delimiter, string $expected): void { $obj = $this->createExpression(); @@ -475,14 +439,13 @@ public function testSqlSetOperators($operator, $delimiter, $expected) /** * Test list-based SQL operator without a value. * - * @dataProvider provideSetOperators * * @param string $operator A SQL operator. * @param string $delimiter The set's delimiter. * @param string $expected Unused; The expected result. - * @return void */ - public function testSqlSetOperatorsWithoutValue($operator, $delimiter, $expected) + #[\PHPUnit\Framework\Attributes\DataProvider('provideSetOperators')] + public function testSqlSetOperatorsWithoutValue(string $operator, string $delimiter, string $expected): void { $obj = $this->createExpression(); @@ -497,10 +460,8 @@ public function testSqlSetOperatorsWithoutValue($operator, $delimiter, $expected /** * Test SQL function. - * - * @return void */ - public function testSqlFunction() + public function testSqlFunction(): void { $obj = $this->createExpression(); $obj->setData([ @@ -515,10 +476,8 @@ public function testSqlFunction() /** * Test SQL condition with multiple field names. - * - * @return void */ - public function testSqlFields() + public function testSqlFields(): void { $container = $this->getContainer(); @@ -543,9 +502,8 @@ public function testSqlFields() * Provide data for simple operators. * * @used-by self::testSqlComparisonOperators() - * @return array */ - public function provideComparisonOperators() + public static function provideComparisonOperators(): array { return [ [ '=' ], [ '!=' ], @@ -559,9 +517,8 @@ public function provideComparisonOperators() * Provide data for sets-style operators. * * @used-by self::testSqlSetOperators() - * @return array */ - public function provideSetOperators() + public static function provideSetOperators(): array { return [ 'FIND_IN_SET' => [ 'FIND_IN_SET', ',', 'FIND_IN_SET(\'%2$s\', %1$s)' ], @@ -574,9 +531,8 @@ public function provideSetOperators() * Provide data for condition-style operators. * * @used-by self::testSqlConditionalOperators() - * @return array */ - public function provideConditionalOperators() + public static function provideConditionalOperators(): array { return [ [ 'IS NULL' ], [ 'IS NOT NULL' ], @@ -590,9 +546,8 @@ public function provideConditionalOperators() * Provide data for logical NOT operators. * * @used-by self::testSqlNegationOperators() - * @return array */ - public function provideNegationOperators() + public static function provideNegationOperators(): array { return [ [ '!' ], diff --git a/packages/core/tests/Charcoal/Source/Database/DatabaseOrderTest.php b/packages/core/tests/Charcoal/Source/Database/DatabaseOrderTest.php index f0c502fe1..f24263e71 100644 --- a/packages/core/tests/Charcoal/Source/Database/DatabaseOrderTest.php +++ b/packages/core/tests/Charcoal/Source/Database/DatabaseOrderTest.php @@ -23,10 +23,8 @@ class DatabaseOrderTest extends AbstractTestCase /** * Create expression for testing. - * - * @return DatabaseOrder */ - final protected function createExpression() + final protected function createExpression(): \Charcoal\Source\Database\DatabaseOrder { return new DatabaseOrder(); } @@ -35,10 +33,8 @@ final protected function createExpression() * Test default table name for default data values. * * @see \Charcoal\Tests\Source\Database\DatabaseFilterTest::testDefaultValues() - * - * @return void */ - public function testDefaultValues() + public function testDefaultValues(): void { $obj = $this->createExpression(); @@ -50,10 +46,8 @@ public function testDefaultValues() /** * Test influence of "active" property on SQL compilation. - * - * @return void */ - public function testInactiveExpression() + public function testInactiveExpression(): void { $obj = $this->createExpression(); $obj->setMode('asc')->setProperty('foo'); @@ -67,10 +61,8 @@ public function testInactiveExpression() /** * Test SQL without a mode. - * - * @return void */ - public function testBlankSql() + public function testBlankSql(): void { $obj = $this->createExpression(); @@ -80,10 +72,8 @@ public function testBlankSql() /** * Test SQL with custom mode and placeholders. - * - * @return void */ - public function testSqlCustomMode() + public function testSqlCustomMode(): void { $obj = $this->createExpression(); @@ -94,10 +84,8 @@ public function testSqlCustomMode() /** * Test that "custom" and "values" mode have precedence over other features * when the mode is undefined. - * - * @return void */ - public function testSqlModeResolutionAndPrecedence() + public function testSqlModeResolutionAndPrecedence(): void { $obj = $this->createExpression(); @@ -114,10 +102,8 @@ public function testSqlModeResolutionAndPrecedence() /** * Test SQL with random mode. - * - * @return void */ - public function testSqlRandomMode() + public function testSqlRandomMode(): void { $obj = $this->createExpression(); @@ -128,13 +114,12 @@ public function testSqlRandomMode() /** * Test SQL with direction mode. * - * @dataProvider provideSqlDirectionMode * * @param mixed $mode The directional mode to set. * @param mixed $expected The expected SQL direction. - * @return void */ - public function testSqlDirectionMode($mode, $expected) + #[\PHPUnit\Framework\Attributes\DataProvider('provideSqlDirectionMode')] + public function testSqlDirectionMode(string $mode, string $expected): void { $obj = $this->createExpression(); @@ -149,9 +134,8 @@ public function testSqlDirectionMode($mode, $expected) * Provide data for selecting directional ordering. * * @used-by self::testSqlDirectionMode() - * @return array */ - public function provideSqlDirectionMode() + public static function provideSqlDirectionMode(): array { return [ [ 'asc', 'ASC' ], @@ -161,10 +145,8 @@ public function provideSqlDirectionMode() /** * Test direction mode without property. - * - * @return void */ - public function testSqlDirectionModeWithoutProperty() + public function testSqlDirectionModeWithoutProperty(): void { $obj = $this->createExpression(); @@ -174,10 +156,8 @@ public function testSqlDirectionModeWithoutProperty() /** * Test SQL with values mode. - * - * @return void */ - public function testSqlValuesMode() + public function testSqlValuesMode(): void { $obj = $this->createExpression(); $obj->setMode('values') @@ -189,10 +169,8 @@ public function testSqlValuesMode() /** * Test values mode without property. - * - * @return void */ - public function testSqlValuesModeWithoutProperty() + public function testSqlValuesModeWithoutProperty(): void { $obj = $this->createExpression(); @@ -204,10 +182,8 @@ public function testSqlValuesModeWithoutProperty() /** * Test values mode without values. - * - * @return void */ - public function testSqlValuesModeWithoutValues() + public function testSqlValuesModeWithoutValues(): void { $obj = $this->createExpression(); @@ -219,10 +195,8 @@ public function testSqlValuesModeWithoutValues() /** * Test invalid custom SQL. - * - * @return void */ - public function testSqlCustomModeWithoutQuery() + public function testSqlCustomModeWithoutQuery(): void { $obj = $this->createExpression(); @@ -234,10 +208,8 @@ public function testSqlCustomModeWithoutQuery() /** * Test invalid property SQL. - * - * @return void */ - public function testSqlWithoutModeWithoutProperty() + public function testSqlWithoutModeWithoutProperty(): void { $obj = $this->createExpression(); @@ -249,10 +221,8 @@ public function testSqlWithoutModeWithoutProperty() /** * Test helper methods. - * - * @return void */ - public function testPrepareValues() + public function testPrepareValues(): void { $obj = $this->createExpression(); diff --git a/packages/core/tests/Charcoal/Source/Database/DatabasePaginationTest.php b/packages/core/tests/Charcoal/Source/Database/DatabasePaginationTest.php index 1314f565b..f97d5fb88 100644 --- a/packages/core/tests/Charcoal/Source/Database/DatabasePaginationTest.php +++ b/packages/core/tests/Charcoal/Source/Database/DatabasePaginationTest.php @@ -21,20 +21,16 @@ class DatabasePaginationTest extends AbstractTestCase /** * Create expression for testing. - * - * @return DatabasePagination */ - final protected function createExpression() + final protected function createExpression(): \Charcoal\Source\Database\DatabasePagination { return new DatabasePagination(); } /** * Test influence of "active" property on SQL compilation. - * - * @return void */ - public function testInactiveExpression() + public function testInactiveExpression(): void { $obj = $this->createExpression(); $obj->setNumPerPage(10); @@ -48,10 +44,8 @@ public function testInactiveExpression() /** * Test "page" property without "num_per_page". - * - * @return void */ - public function testSqlOffsetWithoutLimit() + public function testSqlOffsetWithoutLimit(): void { $obj = $this->createExpression(); @@ -64,10 +58,8 @@ public function testSqlOffsetWithoutLimit() /** * Test "page" property with "num_per_page". - * - * @return void */ - public function testSqlOffsetWithLimit() + public function testSqlOffsetWithLimit(): void { $obj = $this->createExpression(); @@ -83,10 +75,8 @@ public function testSqlOffsetWithLimit() /** * Test "num_per_page" property without "page". - * - * @return void */ - public function testSqlLimitWithoutOffset() + public function testSqlLimitWithoutOffset(): void { $obj = $this->createExpression(); @@ -99,10 +89,8 @@ public function testSqlLimitWithoutOffset() /** * Test helper methods. - * - * @return void */ - public function testUtilities() + public function testUtilities(): void { $obj = $this->createExpression(); diff --git a/packages/core/tests/Charcoal/Source/DatabaseExpressionTestTrait.php b/packages/core/tests/Charcoal/Source/DatabaseExpressionTestTrait.php index f9f24b0f0..d5661ffca 100644 --- a/packages/core/tests/Charcoal/Source/DatabaseExpressionTestTrait.php +++ b/packages/core/tests/Charcoal/Source/DatabaseExpressionTestTrait.php @@ -35,10 +35,8 @@ abstract public function testInactiveExpression(); * * Assertions: * 1. Implements {@see ExpressionInterface} - * - * @return void */ - public function testConstruct() + public function testConstruct(): void { $obj = $this->createExpression(); diff --git a/packages/core/tests/Charcoal/Source/DatabaseSourceConfigTest.php b/packages/core/tests/Charcoal/Source/DatabaseSourceConfigTest.php index 8890b86a2..9f74a4141 100644 --- a/packages/core/tests/Charcoal/Source/DatabaseSourceConfigTest.php +++ b/packages/core/tests/Charcoal/Source/DatabaseSourceConfigTest.php @@ -13,10 +13,7 @@ */ class DatabaseSourceConfigTest extends AbstractTestCase { - /** - * @return void - */ - public function testDefaultData() + public function testDefaultData(): void { $obj = new DatabaseSourceConfig(); $defaults = $obj->defaults(); @@ -26,20 +23,14 @@ public function testDefaultData() $this->assertEquals($obj->hostname(), $defaults['hostname']); } - /** - * @return void - */ - public function testMerge() + public function testMerge(): void { $obj = new DatabaseSourceConfig(); $ret = $obj->merge([]); $this->assertSame($ret, $obj); } - /** - * @return void - */ - public function testSetHostname() + public function testSetHostname(): void { $obj = new DatabaseSourceConfig(); $this->assertEquals('localhost', $obj->hostname()); @@ -51,10 +42,7 @@ public function testSetHostname() $obj->setHostname(false); } - /** - * @return void - */ - public function testSetUsername() + public function testSetUsername(): void { $obj = new DatabaseSourceConfig(); $this->assertEquals(null, $obj->username()); @@ -66,10 +54,7 @@ public function testSetUsername() $obj->setUsername(false); } - /** - * @return void - */ - public function testSetPassword() + public function testSetPassword(): void { $obj = new DatabaseSourceConfig(); $this->assertEquals('', $obj->password()); @@ -81,10 +66,7 @@ public function testSetPassword() $obj->setPassword(false); } - /** - * @return void - */ - public function testSetDatabase() + public function testSetDatabase(): void { $obj = new DatabaseSourceConfig(); $this->assertEquals(null, $obj->database()); @@ -96,10 +78,7 @@ public function testSetDatabase() $obj->setDatabase(false); } - /** - * @return void - */ - public function testSetDisableUtf8() + public function testSetDisableUtf8(): void { $obj = new DatabaseSourceConfig(); $this->assertEquals(false, $obj->disableUtf8()); diff --git a/packages/core/tests/Charcoal/Source/DatabaseSourceTest.php b/packages/core/tests/Charcoal/Source/DatabaseSourceTest.php index 213989c22..d95f6fb98 100644 --- a/packages/core/tests/Charcoal/Source/DatabaseSourceTest.php +++ b/packages/core/tests/Charcoal/Source/DatabaseSourceTest.php @@ -1,5 +1,7 @@ getContainer(); diff --git a/packages/core/tests/Charcoal/Source/DatabaseTestModel.php b/packages/core/tests/Charcoal/Source/DatabaseTestModel.php index e52ed51e6..1548f4804 100644 --- a/packages/core/tests/Charcoal/Source/DatabaseTestModel.php +++ b/packages/core/tests/Charcoal/Source/DatabaseTestModel.php @@ -1,5 +1,7 @@ getMockForTrait(ExpressionFieldTrait::class); - - return $obj; + return $this->getMockForTrait(ExpressionFieldTrait::class); } /** @@ -69,10 +65,8 @@ final public function createProperty() * 3. Chainable method * 4. Accepts Property * 5. Accepts NULL - * - * @return void */ - public function testProperty() + public function testProperty(): void { $obj = $this->createField(); @@ -100,10 +94,8 @@ public function testProperty() /** * Test the "property" determiner. - * - * @return void */ - public function testHasProperty() + public function testHasProperty(): void { $obj = $this->createField(); $this->assertFalse($obj->hasProperty()); @@ -114,10 +106,8 @@ public function testHasProperty() /** * Test "property" property with blank value. - * - * @return void */ - public function testPropertyWithBlankValue() + public function testPropertyWithBlankValue(): void { $this->expectException(InvalidArgumentException::class); $this->createField()->setProperty(''); @@ -125,10 +115,8 @@ public function testPropertyWithBlankValue() /** * Test "property" property with invalid property. - * - * @return void */ - public function testPropertyWithInvalidProperty() + public function testPropertyWithInvalidProperty(): void { $container = $this->getContainer(); $property = $container['property/factory']->create('generic'); @@ -139,10 +127,8 @@ public function testPropertyWithInvalidProperty() /** * Test "property" property with invalid value. - * - * @return void */ - public function testPropertyWithInvalidValue() + public function testPropertyWithInvalidValue(): void { $this->expectException(InvalidArgumentException::class); $this->createField()->setProperty([]); @@ -156,10 +142,8 @@ public function testPropertyWithInvalidValue() * 2. Mutated state * 3. Chainable method * 4. Accepts NULL - * - * @return void */ - public function testTable() + public function testTable(): void { $obj = $this->createField(); @@ -181,10 +165,8 @@ public function testTable() /** * Test the "table" determiner. - * - * @return void */ - public function testHasTable() + public function testHasTable(): void { $obj = $this->createField(); $this->assertFalse($obj->hasTable()); @@ -195,10 +177,8 @@ public function testHasTable() /** * Test "table" property with blank value. - * - * @return void */ - public function testTableWithBlankValue() + public function testTableWithBlankValue(): void { $this->expectException(InvalidArgumentException::class); $this->createField()->setTable(''); @@ -206,10 +186,8 @@ public function testTableWithBlankValue() /** * Test "table" property with invalid value. - * - * @return void */ - public function testTableWithInvalidValue() + public function testTableWithInvalidValue(): void { $this->expectException(InvalidArgumentException::class); $this->createField()->setTable([]); @@ -222,10 +200,8 @@ public function testTableWithInvalidValue() * 1. Default state * 2. With column name * 3. With property instance - * - * @return void */ - public function testFieldNames() + public function testFieldNames(): void { $obj = $this->createField(); @@ -252,10 +228,8 @@ public function testFieldNames() * 1. Default state * 2. With column name * 3. With property instance - * - * @return void */ - public function testFieldName() + public function testFieldName(): void { $obj = $this->createField(); @@ -282,10 +256,8 @@ public function testFieldName() * 1. Default state * 2. With column name * 3. With table name - * - * @return void */ - public function testFieldIdentifiers() + public function testFieldIdentifiers(): void { $obj = $this->createField(); @@ -310,10 +282,8 @@ public function testFieldIdentifiers() * 1. Default state * 2. With column name * 3. With table name - * - * @return void */ - public function testFieldIdentifier() + public function testFieldIdentifier(): void { $obj = $this->createField(); diff --git a/packages/core/tests/Charcoal/Source/ExpressionTest.php b/packages/core/tests/Charcoal/Source/ExpressionTest.php index a486017e5..04aded817 100644 --- a/packages/core/tests/Charcoal/Source/ExpressionTest.php +++ b/packages/core/tests/Charcoal/Source/ExpressionTest.php @@ -21,10 +21,8 @@ class ExpressionTest extends AbstractTestCase /** * Create expression for testing. - * - * @return Expression */ - final protected function createExpression() + final protected function createExpression(): \Charcoal\Source\Expression { return new Expression(); } @@ -33,9 +31,8 @@ final protected function createExpression() * Provide data for value parsing. * * @used-by ExpressionTestTrait::testDefaultValues() - * @return array */ - final public function provideDefaultValues() + final public function provideDefaultValues(): array { return [ 'condition' => [ 'condition', null ], @@ -54,10 +51,8 @@ final public function provideDefaultValues() * 4. Trimmed value * 5. Accepts NULL * 6. Swaps blank string for NULL - * - * @return void */ - public function testConditionExpression() + public function testConditionExpression(): void { $obj = $this->createExpression(); @@ -87,10 +82,8 @@ public function testConditionExpression() /** * Test the conditional check of "condition". - * - * @return void */ - public function testHasConditionExpression() + public function testHasConditionExpression(): void { $obj = $this->createExpression(); @@ -99,16 +92,14 @@ public function testHasConditionExpression() $obj->setCondition(' '); $this->assertFalse($obj->hasCondition()); - $that = $obj->setCondition('1 = 1'); + $obj->setCondition('1 = 1'); $this->assertTrue($obj->hasCondition()); } /** * Test "condition" property with invalid value. - * - * @return void */ - public function testConditionExpressionWithInvalidValue() + public function testConditionExpressionWithInvalidValue(): void { $this->expectException(InvalidArgumentException::class); $this->createExpression()->setCondition([]); @@ -120,10 +111,8 @@ public function testConditionExpressionWithInvalidValue() * Assertions: * 1. Mutate all options * 2. Partially mutated state - * - * @return void */ - public function testData() + public function testData(): void { /** 1. Mutate all options */ $mutation = [ diff --git a/packages/core/tests/Charcoal/Source/ExpressionTestFieldTrait.php b/packages/core/tests/Charcoal/Source/ExpressionTestFieldTrait.php index 9207605d8..7e173ba6c 100644 --- a/packages/core/tests/Charcoal/Source/ExpressionTestFieldTrait.php +++ b/packages/core/tests/Charcoal/Source/ExpressionTestFieldTrait.php @@ -15,10 +15,8 @@ trait ExpressionTestFieldTrait { /** * Test deprecated "table_name" property. - * - * @return void */ - public function testDeprecatedTableNameExpression() + public function testDeprecatedTableNameExpression(): void { $obj = $this->createExpression(); @@ -30,19 +28,16 @@ public function testDeprecatedTableNameExpression() * Test "table_name" property deprecation notice. * * @used-by self::testDeprecatedTableNameErrorInPhp7() - * - * @return void */ - public function delegatedTestDeprecatedTableNameError() + public function delegatedTestDeprecatedTableNameError(): void { $this->createExpression()->setData([ 'table_name' => 'foobar' ]); } /** * @requires PHP >= 7.0 - * @return void */ - public function testDeprecatedTableNameErrorInPhp7() + public function testDeprecatedTableNameErrorInPhp7(): void { $this->expectDeprecation(); $this->delegatedTestDeprecatedTableNameError(); @@ -53,11 +48,10 @@ public function testDeprecatedTableNameErrorInPhp7() * * @param ExpressionFieldInterface $obj The expression to test. * @param array|null $expected The expected data subset. - * @return void */ - public function assertStructHasFieldData(ExpressionFieldInterface $obj, array $expected = null) + public function assertStructHasFieldData(ExpressionFieldInterface $obj, ?array $expected = null): void { - if (empty($expected)) { + if ($expected === null || $expected === []) { $expected = [ 'property' => 'col', 'table' => 'tbl', diff --git a/packages/core/tests/Charcoal/Source/ExpressionTestTrait.php b/packages/core/tests/Charcoal/Source/ExpressionTestTrait.php index 5356bcaaa..f83b1be31 100644 --- a/packages/core/tests/Charcoal/Source/ExpressionTestTrait.php +++ b/packages/core/tests/Charcoal/Source/ExpressionTestTrait.php @@ -40,10 +40,8 @@ abstract public function provideDefaultValues(); * * Assertions: * 1. Implements {@see ExpressionInterface} - * - * @return void */ - final public function testConstruct() + final public function testConstruct(): void { $obj = $this->createExpression(); @@ -56,10 +54,8 @@ final public function testConstruct() * * Assertions: * 1. Getter returns an array - * - * @return void */ - final public function testDefaultValuesMethod() + final public function testDefaultValuesMethod(): void { $obj = $this->createExpression(); @@ -74,9 +70,8 @@ final public function testDefaultValuesMethod() * * @param mixed $key The data key test. * @param mixed $expected The expected data value. - * @return void */ - final public function testDefaultValues($key, $expected) + final public function testDefaultValues($key, $expected): void { $obj = $this->createExpression(); $data = $obj->defaultData(); @@ -91,10 +86,8 @@ final public function testDefaultValues($key, $expected) * Assertions: * 1. Getter returns an array * 2. Setter is chainable - * - * @return void */ - final public function testDataMethod() + final public function testDataMethod(): void { $obj = $this->createExpression(); @@ -108,10 +101,8 @@ final public function testDataMethod() /** * Test data structure with default state. - * - * @return void */ - final public function testDefaultData() + final public function testDefaultData(): void { $obj = $this->createExpression(); $this->assertEquals($obj->defaultData(), $obj->data()); @@ -122,11 +113,10 @@ final public function testDefaultData() * * @param ExpressionInterface $obj The expression to test. * @param array|null $expected The expected data subset. - * @return void */ - final public function assertStructHasBasicData(ExpressionInterface $obj, array $expected = null) + final public function assertStructHasBasicData(ExpressionInterface $obj, ?array $expected = null): void { - if (empty($expected)) { + if ($expected === null || $expected === []) { $expected = [ 'active' => false, 'name' => 'foo', @@ -151,10 +141,8 @@ final public function assertStructHasBasicData(ExpressionInterface $obj, array $ * Assertions: * 1. Serialization from default state * 2. Serialization from mutated state - * - * @return void */ - public function testJsonSerializable() + public function testJsonSerializable(): void { $obj = $this->createExpression(); @@ -181,16 +169,14 @@ public function testJsonSerializable() * Assertions: * 1. Serialization from default state * 2. Serialization from mutated state - * - * @return void */ - public function testSerializable() + public function testSerializable(): void { $obj = $this->createExpression(); /** 1. Serialization from default state */ $that = unserialize(serialize($obj)); - $this->assertInstanceOf(get_class($obj), $that); + $this->assertInstanceOf($obj::class, $that); $this->assertEquals($obj, $that); $this->assertTrue($that->active()); $this->assertNull($that->name()); @@ -202,7 +188,7 @@ public function testSerializable() ]; $obj->setData($mutation); $that = unserialize(serialize($obj)); - $this->assertInstanceOf(get_class($obj), $that); + $this->assertInstanceOf($obj::class, $that); $this->assertEquals($obj, $that); $this->assertFalse($that->active()); $this->assertEquals('foo', $that->name()); diff --git a/packages/core/tests/Charcoal/Source/FilterCollectionTraitTest.php b/packages/core/tests/Charcoal/Source/FilterCollectionTraitTest.php index 6993a1028..6c4c470c1 100644 --- a/packages/core/tests/Charcoal/Source/FilterCollectionTraitTest.php +++ b/packages/core/tests/Charcoal/Source/FilterCollectionTraitTest.php @@ -20,6 +20,14 @@ /** * Test {@see FilterCollectionTrait} and {@see FilterCollectionInterface}. */ +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\FilterCollectionTrait::class, 'createFilter')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\FilterCollectionTrait::class, 'filters')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\FilterCollectionTrait::class, 'hasFilters')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\FilterCollectionTrait::class, 'setFilters')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\FilterCollectionTrait::class, 'addFilters')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\FilterCollectionTrait::class, 'addFilter')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\FilterCollectionTrait::class, 'processFilter')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\FilterCollectionTrait::class, 'traverseFilters')] class FilterCollectionTraitTest extends AbstractTestCase { use AssertionsTrait; @@ -28,10 +36,8 @@ class FilterCollectionTraitTest extends AbstractTestCase /** * Create mock object for testing. - * - * @return FilterCollectionClass */ - final public function createCollector() + final public function createCollector(): \Charcoal\Tests\Mock\FilterCollectionClass { return new FilterCollectionClass(); } @@ -40,9 +46,8 @@ final public function createCollector() * Create expression for testing. * * @param array $data Optional expression data. - * @return Filter */ - final protected function createExpression(array $data = null) + final protected function createExpression(?array $data = null): \Charcoal\Source\Filter { $expr = new Filter(); if ($data !== null) { @@ -58,11 +63,8 @@ final protected function createExpression(array $data = null) * 1. Instance of {@see ExpressionInterface} * 2. Instance of {@see FilterInterface} * - * @covers \Charcoal\Source\FilterCollectionTrait::createFilter - * - * @return void */ - public function testCreateExpression() + public function testCreateExpression(): void { $obj = $this->createCollector(); @@ -78,11 +80,8 @@ public function testCreateExpression() * 1. Empty; Default state * 2. Populated; Mutated state * - * @covers \Charcoal\Source\FilterCollectionTrait::filters - * - * @return void */ - public function testGetExpressions() + public function testGetExpressions(): void { $obj = $this->createCollector(); @@ -103,11 +102,8 @@ public function testGetExpressions() * 1. Empty; Default state * 2. Populated; Mutated state * - * @covers \Charcoal\Source\FilterCollectionTrait::hasFilters - * - * @return void */ - public function testHasExpressions() + public function testHasExpressions(): void { $obj = $this->createCollector(); @@ -126,11 +122,8 @@ public function testHasExpressions() * 1. Replaces expressions with a new collection * 2. Chainable method * - * @covers \Charcoal\Source\FilterCollectionTrait::setFilters - * - * @return void */ - public function testSetExpressions() + public function testSetExpressions(): void { $obj = $this->createCollector(); $exp1 = $this->createExpression(); @@ -157,11 +150,8 @@ public function testSetExpressions() * 1. Appends an array of items to the internal collection * 2. Chainable method * - * @covers \Charcoal\Source\FilterCollectionTrait::addFilters - * - * @return void */ - public function testAddExpressions() + public function testAddExpressions(): void { $obj = $this->createCollector(); $exp1 = $this->createExpression(); @@ -184,11 +174,8 @@ public function testAddExpressions() /** * Test the mass addition of expressions with names. * - * @covers \Charcoal\Source\FilterCollectionTrait::addFilters - * - * @return void */ - public function testAddExpressionsMap() + public function testAddExpressionsMap(): void { $obj = $this->createCollector(); $map = [ @@ -216,11 +203,8 @@ public function testAddExpressionsMap() * 1. Appends one item to the internal collection * 2. Chainable method * - * @covers \Charcoal\Source\FilterCollectionTrait::addFilter - * - * @return void */ - public function testAddExpression() + public function testAddExpression(): void { $obj = $this->createCollector(); $expr = $this->createExpression(); @@ -251,11 +235,8 @@ public function testAddExpression() * 4. If an instance of {@see FilterInterface} is provided, * the Expression object is used as is. * - * @covers \Charcoal\Source\FilterCollectionTrait::processFilter - * - * @return void */ - public function testProcessExpression() + public function testProcessExpression(): void { $obj = $this->createCollector(); @@ -275,9 +256,7 @@ public function testProcessExpression() $this->assertArrayContains($struct, $result->data()); /** 3. Closure */ - $lambda = function (FilterInterface $expr, FilterCollectionInterface $tested) use ($struct) { - return $expr->setData($struct); - }; + $lambda = (fn(FilterInterface $expr, FilterCollectionInterface $tested) => $expr->setData($struct)); $result = $this->callMethodWith($obj, 'processFilter', $lambda); $this->assertInstanceOf(FilterInterface::class, $result); $this->assertArrayContains($struct, $result->data()); @@ -291,11 +270,8 @@ public function testProcessExpression() /** * Test the failure when parsing an invalid expression. * - * @covers \Charcoal\Source\FilterCollectionTrait::processFilter - * - * @return void */ - public function testProcessExpressionWithInvalidValue() + public function testProcessExpressionWithInvalidValue(): void { $obj = $this->createCollector(); @@ -310,11 +286,8 @@ public function testProcessExpressionWithInvalidValue() * 1. Applies callback to internal collection * 2. Chainable method * - * @covers \Charcoal\Source\FilterCollectionTrait::traverseFilters - * - * @return void */ - public function testTraverseExpressions() + public function testTraverseExpressions(): void { $obj = $this->createCollector(); $exp1 = $this->createExpression(); @@ -327,7 +300,7 @@ public function testTraverseExpressions() /** 1. Traverse internal collection */ $obj->addFilters([ $exp1, $exp4 ]); - $that = $obj->traverseFilters(function (FilterInterface $exp) { + $that = $obj->traverseFilters(function (FilterInterface $exp): void { $exp->setProperty('foo'); }); diff --git a/packages/core/tests/Charcoal/Source/FilterTest.php b/packages/core/tests/Charcoal/Source/FilterTest.php index 61acabdea..737ccea23 100644 --- a/packages/core/tests/Charcoal/Source/FilterTest.php +++ b/packages/core/tests/Charcoal/Source/FilterTest.php @@ -17,6 +17,9 @@ /** * Test {@see Filter} and {@see FilterInterface}. */ +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\Filter::class, '__clone')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\Filter::class, 'count')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\Filter::class, 'createFilter')] class FilterTest extends AbstractTestCase { use CoreContainerIntegrationTrait; @@ -29,7 +32,7 @@ class FilterTest extends AbstractTestCase * * @return Order */ - final protected function createExpression() + final protected function createExpression(): \Charcoal\Source\Filter { return new Filter(); } @@ -39,10 +42,8 @@ final protected function createExpression() * * Assertions: * 1. Implements {@see FilterInterface} - * - * @return void */ - public function testFilterConstruct() + public function testFilterConstruct(): void { $obj = $this->createExpression(); @@ -53,11 +54,8 @@ public function testFilterConstruct() /** * Test deep cloning of expression trees. * - * @covers \Charcoal\Source\Filter::__clone - * - * @return void */ - public function testDeepCloning() + public function testDeepCloning(): void { $obj = $this->createExpression(); $obj->addFilters([ @@ -88,9 +86,8 @@ public function testDeepCloning() * Provide data for value parsing. * * @used-by ExpressionTestTrait::testDefaultValues() - * @return array */ - final public function provideDefaultValues() + final public function provideDefaultValues(): array { return [ 'property' => [ 'property', null ], @@ -116,10 +113,8 @@ final public function provideDefaultValues() * * Note: {@see Filter::value()} uses {@see \Charcoal\Source\AbstractExpression::parseValue()}. * Tests for `parseValue()` are performed in {@see ExpressionTestTrait::testParseValue()}. - * - * @return void */ - public function testValue() + public function testValue(): void { $obj = $this->createExpression(); @@ -137,10 +132,8 @@ public function testValue() /** * Test deprecated "val" property. - * - * @return void */ - public function testDeprecatedValExpression() + public function testDeprecatedValExpression(): void { $obj = $this->createExpression(); @@ -152,21 +145,14 @@ public function testDeprecatedValExpression() * Test "val" property deprecation notice. * * @used-by self::testDeprecatedValErrorInPhp7() - * - * @return void */ - public function delegatedTestDeprecatedValError() + public function delegatedTestDeprecatedValError(): void { $this->createExpression()->setData([ 'val' => 'qux' ]); } - /** - * - * - * @requires PHP >= 7.0 - * @return void - */ - public function testDeprecatedValErrorInPhp7() + #[\PHPUnit\Framework\Attributes\RequiresPhp('>= 7.0')] + public function testDeprecatedValErrorInPhp7(): void { $this->expectDeprecation(); $this->delegatedTestDeprecatedValError(); @@ -181,10 +167,8 @@ public function testDeprecatedValErrorInPhp7() * 2. Mutated state * 3. Chainable method * 4. Accepts mixed case - * - * @return void */ - public function testOperator() + public function testOperator(): void { $obj = $this->createExpression(); @@ -206,10 +190,8 @@ public function testOperator() /** * Test "operator" property with unsupported operator. - * - * @return void */ - public function testOperatorWithUnsupportedOperator() + public function testOperatorWithUnsupportedOperator(): void { $this->expectException(InvalidArgumentException::class); $this->createExpression()->setOperator('foo'); @@ -217,10 +199,8 @@ public function testOperatorWithUnsupportedOperator() /** * Test "operator" property with invalid value. - * - * @return void */ - public function testOperatorWithInvalidValue() + public function testOperatorWithInvalidValue(): void { $this->expectException(InvalidArgumentException::class); $this->createExpression()->setOperator(42); @@ -235,10 +215,8 @@ public function testOperatorWithInvalidValue() * 3. Chainable method * 4. Accepts mixed case * 5. Accepts NULL - * - * @return void */ - public function testFunc() + public function testFunc(): void { $obj = $this->createExpression(); @@ -264,10 +242,8 @@ public function testFunc() /** * Test "func" property with unsupported func. - * - * @return void */ - public function testFuncWithUnsupportedFunction() + public function testFuncWithUnsupportedFunction(): void { $this->expectException(InvalidArgumentException::class); $this->createExpression()->setFunc('xyzzy'); @@ -275,10 +251,8 @@ public function testFuncWithUnsupportedFunction() /** * Test "func" property with invalid value. - * - * @return void */ - public function testFuncWithInvalidValue() + public function testFuncWithInvalidValue(): void { $this->expectException(InvalidArgumentException::class); $this->createExpression()->setFunc(33); @@ -292,10 +266,8 @@ public function testFuncWithInvalidValue() * 2. Mutated state * 3. Chainable method * 4. Accepts mixed case - * - * @return void */ - public function testConjunction() + public function testConjunction(): void { $obj = $this->createExpression(); @@ -317,10 +289,8 @@ public function testConjunction() /** * Test "conjunction" property with unsupported conjunction. - * - * @return void */ - public function testConjunctionWithUnsupportedConjunction() + public function testConjunctionWithUnsupportedConjunction(): void { $this->expectException(InvalidArgumentException::class); $this->createExpression()->setConjunction('qux'); @@ -328,10 +298,8 @@ public function testConjunctionWithUnsupportedConjunction() /** * Test "conjunction" property with invalid value. - * - * @return void */ - public function testConjunctionWithInvalidValue() + public function testConjunctionWithInvalidValue(): void { $this->expectException(InvalidArgumentException::class); $this->createExpression()->setConjunction(11); @@ -339,10 +307,8 @@ public function testConjunctionWithInvalidValue() /** * Test deprecated "operand" property. - * - * @return void */ - public function testDeprecatedOperandExpression() + public function testDeprecatedOperandExpression(): void { $obj = $this->createExpression(); @@ -354,21 +320,14 @@ public function testDeprecatedOperandExpression() * Test "operand" property deprecation notice. * * @used-by self::testDeprecatedOperandErrorInPhp7() - * - * @return void */ - public function delegatedTestDeprecatedOperandError() + public function delegatedTestDeprecatedOperandError(): void { $this->createExpression()->setData([ 'operand' => 'XOR' ]); } - /** - * - * - * @requires PHP >= 7.0 - * @return void - */ - public function testDeprecatedOperandErrorInPhp7() + #[\PHPUnit\Framework\Attributes\RequiresPhp('>= 7.0')] + public function testDeprecatedOperandErrorInPhp7(): void { $this->expectDeprecation(); $this->delegatedTestDeprecatedOperandError(); @@ -381,11 +340,8 @@ public function testDeprecatedOperandErrorInPhp7() * 1. Default state * 2. Mutated state * - * @covers \Charcoal\Source\Filter::count - * - * @return void */ - public function testCount() + public function testCount(): void { $obj = $this->createExpression(); @@ -405,11 +361,8 @@ public function testCount() * 2. Instance of {@see Filter} * * @see \Charcoal\Tests\Source\AbstractSourceTest::testCreateFilter - * @covers \Charcoal\Source\Filter::createFilter - * - * @return void */ - public function testCreateFilter() + public function testCreateFilter(): void { $obj = $this->createExpression(); @@ -426,10 +379,8 @@ public function testCreateFilter() * 1. Mutate all options * 2. Partially mutated state * 3. Mutation via aliases - * - * @return void */ - public function testData() + public function testData(): void { /** 1. Mutate all options */ $exp1 = $this->createExpression(); @@ -514,10 +465,8 @@ public function testData() * Test deprecated "string" property. * * @see OrderTest::testDeprecatedStringExpression() - * - * @return void */ - public function testDeprecatedStringExpression() + public function testDeprecatedStringExpression(): void { $obj = $this->createExpression(); @@ -531,19 +480,14 @@ public function testDeprecatedStringExpression() * @see OrderTest::testDeprecatedStringError() * * @used-by self::testDeprecatedStringErrorInPhp7() - * - * @return void */ - public function delegatedTestDeprecatedStringError() + public function delegatedTestDeprecatedStringError(): void { $this->createExpression()->setData([ 'string' => '1 = 1' ]); } - /** - * @requires PHP >= 7.0 - * @return void - */ - public function testDeprecatedStringErrorInPhp7() + #[\PHPUnit\Framework\Attributes\RequiresPhp('>= 7.0')] + public function testDeprecatedStringErrorInPhp7(): void { $this->expectDeprecation(); $this->delegatedTestDeprecatedStringError(); diff --git a/packages/core/tests/Charcoal/Source/OrderCollectionTraitTest.php b/packages/core/tests/Charcoal/Source/OrderCollectionTraitTest.php index fa2c91048..5dfc2bbc6 100644 --- a/packages/core/tests/Charcoal/Source/OrderCollectionTraitTest.php +++ b/packages/core/tests/Charcoal/Source/OrderCollectionTraitTest.php @@ -21,6 +21,14 @@ /** * Test {@see OrderCollectionTrait} and {@see OrderCollectionInterface}. */ +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\OrderCollectionTrait::class, 'createOrder')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\OrderCollectionTrait::class, 'orders')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\OrderCollectionTrait::class, 'hasOrders')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\OrderCollectionTrait::class, 'setOrders')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\OrderCollectionTrait::class, 'addOrders')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\OrderCollectionTrait::class, 'addOrder')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\OrderCollectionTrait::class, 'processOrder')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\OrderCollectionTrait::class, 'traverseOrders')] class OrderCollectionTraitTest extends AbstractTestCase { use AssertionsTrait; @@ -29,10 +37,8 @@ class OrderCollectionTraitTest extends AbstractTestCase /** * Create mock object for testing. - * - * @return OrderCollectionClass */ - final public function createCollector() + final public function createCollector(): \Charcoal\Tests\Mock\OrderCollectionClass { return new OrderCollectionClass(); } @@ -41,9 +47,8 @@ final public function createCollector() * Create expression for testing. * * @param array $data Optional expression data. - * @return Order */ - final protected function createExpression(array $data = null) + final protected function createExpression(?array $data = null): \Charcoal\Source\Order { $expr = new Order(); if ($data !== null) { @@ -59,11 +64,8 @@ final protected function createExpression(array $data = null) * 1. Instance of {@see ExpressionInterface} * 2. Instance of {@see OrderInterface} * - * @covers \Charcoal\Source\OrderCollectionTrait::createOrder - * - * @return void */ - public function testCreateExpression() + public function testCreateExpression(): void { $obj = $this->createCollector(); @@ -79,11 +81,8 @@ public function testCreateExpression() * 1. Empty; Default state * 2. Populated; Mutated state * - * @covers \Charcoal\Source\OrderCollectionTrait::orders - * - * @return void */ - public function testGetExpressions() + public function testGetExpressions(): void { $obj = $this->createCollector(); @@ -104,11 +103,8 @@ public function testGetExpressions() * 1. Empty; Default state * 2. Populated; Mutated state * - * @covers \Charcoal\Source\OrderCollectionTrait::hasOrders - * - * @return void */ - public function testHasExpressions() + public function testHasExpressions(): void { $obj = $this->createCollector(); @@ -127,11 +123,8 @@ public function testHasExpressions() * 1. Replaces expressions with a new collection * 2. Chainable method * - * @covers \Charcoal\Source\OrderCollectionTrait::setOrders - * - * @return void */ - public function testSetExpressions() + public function testSetExpressions(): void { $obj = $this->createCollector(); $exp1 = $this->createExpression(); @@ -158,11 +151,8 @@ public function testSetExpressions() * 1. Appends an array of items to the internal collection * 2. Chainable method * - * @covers \Charcoal\Source\OrderCollectionTrait::addOrders - * - * @return void */ - public function testAddExpressions() + public function testAddExpressions(): void { $obj = $this->createCollector(); $exp1 = $this->createExpression(); @@ -185,11 +175,8 @@ public function testAddExpressions() /** * Test the mass addition of expressions with names. * - * @covers \Charcoal\Source\OrderCollectionTrait::addOrders - * - * @return void */ - public function testAddExpressionsMap() + public function testAddExpressionsMap(): void { $obj = $this->createCollector(); $map = [ @@ -217,11 +204,8 @@ public function testAddExpressionsMap() * 1. Appends one item to the internal collection * 2. Chainable method * - * @covers \Charcoal\Source\OrderCollectionTrait::addOrder - * - * @return void */ - public function testAddExpression() + public function testAddExpression(): void { $obj = $this->createCollector(); $expr = $this->createExpression(); @@ -252,11 +236,8 @@ public function testAddExpression() * 4. If an instance of {@see OrderInterface} is provided, * the Expression object is used as is. * - * @covers \Charcoal\Source\OrderCollectionTrait::processOrder - * - * @return void */ - public function testProcessExpression() + public function testProcessExpression(): void { $obj = $this->createCollector(); @@ -276,9 +257,7 @@ public function testProcessExpression() $this->assertArrayContains($struct, $result->data()); /** 3. Closure */ - $lambda = function (OrderInterface $expr, OrderCollectionInterface $tested) use ($struct) { - return $expr->setData($struct); - }; + $lambda = (fn(OrderInterface $expr, OrderCollectionInterface $tested) => $expr->setData($struct)); $result = $this->callMethodWith($obj, 'processOrder', $lambda); $this->assertInstanceOf(OrderInterface::class, $result); $this->assertArrayContains($struct, $result->data()); @@ -292,11 +271,8 @@ public function testProcessExpression() /** * Test the failure when parsing an invalid expression. * - * @covers \Charcoal\Source\OrderCollectionTrait::processOrder - * - * @return void */ - public function testProcessExpressionWithInvalidValue() + public function testProcessExpressionWithInvalidValue(): void { $obj = $this->createCollector(); @@ -311,11 +287,8 @@ public function testProcessExpressionWithInvalidValue() * 1. Replaces expressions with a new collection * 2. Chainable method * - * @covers \Charcoal\Source\OrderCollectionTrait::traverseOrders - * - * @return void */ - public function testTraverseExpressions() + public function testTraverseExpressions(): void { $obj = $this->createCollector(); $exp1 = new OrderTree(); @@ -329,7 +302,7 @@ public function testTraverseExpressions() $obj->addOrders([ $exp1 ]); $i = 0; - $that = $obj->traverseOrders(function (OrderInterface $exp) use (&$i) { + $that = $obj->traverseOrders(function (OrderInterface $exp) use (&$i): void { $i++; $exp->setProperty('foo'); }); diff --git a/packages/core/tests/Charcoal/Source/OrderTest.php b/packages/core/tests/Charcoal/Source/OrderTest.php index 3274bfcb8..fd5457c32 100644 --- a/packages/core/tests/Charcoal/Source/OrderTest.php +++ b/packages/core/tests/Charcoal/Source/OrderTest.php @@ -23,10 +23,8 @@ class OrderTest extends AbstractTestCase /** * Create expression for testing. - * - * @return Order */ - final protected function createExpression() + final protected function createExpression(): \Charcoal\Source\Order { return new Order(); } @@ -36,10 +34,8 @@ final protected function createExpression() * * Assertions: * 1. Implements {@see OrderInterface} - * - * @return void */ - public function testOrderConstruct() + public function testOrderConstruct(): void { $obj = $this->createExpression(); @@ -51,9 +47,8 @@ public function testOrderConstruct() * Provide data for value parsing. * * @used-by ExpressionTestTrait::testDefaultValues() - * @return array */ - final public function provideDefaultValues() + final public function provideDefaultValues(): array { return [ 'property' => [ 'property', null ], @@ -76,10 +71,8 @@ final public function provideDefaultValues() * 3. Chainable method * 4. Accepts NULL * 5. Unsupported direction sets DESC - * - * @return void */ - public function testDirection() + public function testDirection(): void { $obj = $this->createExpression(); @@ -98,16 +91,14 @@ public function testDirection() $this->assertNull($obj->direction()); /** 5. Unsupported Direction */ - $that = $obj->setDirection('foo'); + $obj->setDirection('foo'); $this->assertEquals('DESC', $obj->direction()); } /** * Test "direction" property with invalid value. - * - * @return void */ - public function testDirectionWithInvalidValue() + public function testDirectionWithInvalidValue(): void { $this->expectException(InvalidArgumentException::class); $this->createExpression()->setDirection(0); @@ -122,10 +113,8 @@ public function testDirectionWithInvalidValue() * 3. Chainable method * 4. Accepts mixed case * 5. Accepts NULL - * - * @return void */ - public function testMode() + public function testMode(): void { $obj = $this->createExpression(); @@ -151,10 +140,8 @@ public function testMode() /** * Test "direction" property when selecting a direction "mode". - * - * @return void */ - public function testDirectionMode() + public function testDirectionMode(): void { $obj = $this->createExpression(); @@ -172,10 +159,8 @@ public function testDirectionMode() /** * Test "mode" property with unsupported mode. - * - * @return void */ - public function testModeWithUnsupportedMode() + public function testModeWithUnsupportedMode(): void { $this->expectException(InvalidArgumentException::class); $this->createExpression()->setMode('foobar'); @@ -183,10 +168,8 @@ public function testModeWithUnsupportedMode() /** * Test "mode" property with invalid value. - * - * @return void */ - public function testModeWithInvalidValue() + public function testModeWithInvalidValue(): void { $this->expectException(InvalidArgumentException::class); $this->createExpression()->setMode([]); @@ -200,10 +183,8 @@ public function testModeWithInvalidValue() * 2. Mutated state * 3. Chainable method * 4. Accepts NULL - * - * @return void */ - public function testValues() + public function testValues(): void { $obj = $this->createExpression(); @@ -233,10 +214,8 @@ public function testValues() /** * Test "mode" property with blank string. - * - * @return void */ - public function testValuesWithBlankValue() + public function testValuesWithBlankValue(): void { $this->expectException(InvalidArgumentException::class); $this->createExpression()->setValues(''); @@ -244,10 +223,8 @@ public function testValuesWithBlankValue() /** * Test "mode" property with blank string. - * - * @return void */ - public function testValuesWithEmptyArray() + public function testValuesWithEmptyArray(): void { $this->expectException(InvalidArgumentException::class); $this->createExpression()->setValues([]); @@ -255,10 +232,8 @@ public function testValuesWithEmptyArray() /** * Test "mode" property with invalid value. - * - * @return void */ - public function testValuesWithInvalidValue() + public function testValuesWithInvalidValue(): void { $this->expectException(InvalidArgumentException::class); $this->createExpression()->setValues(42); @@ -271,10 +246,8 @@ public function testValuesWithInvalidValue() * 1. Mutate all options * 2. Partially mutated state * 3. Auto-set mode from "condition" - * - * @return void */ - public function testData() + public function testData(): void { /** 1. Mutate all options */ $values = [ 'foo', 'baz', 'qux' ]; @@ -342,10 +315,8 @@ public function testData() * Test deprecated "string" property. * * @see FilterTest::testDeprecatedStringExpression() - * - * @return void */ - public function testDeprecatedStringExpression() + public function testDeprecatedStringExpression(): void { $obj = $this->createExpression(); @@ -359,21 +330,14 @@ public function testDeprecatedStringExpression() * @see FilterTest::testDeprecatedStringError() * * @used-by self::testDeprecatedStringErrorInPhp7() - * - * @return void */ - public function delegatedTestDeprecatedStringError() + public function delegatedTestDeprecatedStringError(): void { $this->createExpression()->setData([ 'string' => '1 = 1' ]); } - /** - * - * - * @requires PHP >= 7.0 - * @return void - */ - public function testDeprecatedStringErrorInPhp7() + #[\PHPUnit\Framework\Attributes\RequiresPhp('>= 7.0')] + public function testDeprecatedStringErrorInPhp7(): void { $this->expectDeprecation(); $this->delegatedTestDeprecatedStringError(); diff --git a/packages/core/tests/Charcoal/Source/PaginationTest.php b/packages/core/tests/Charcoal/Source/PaginationTest.php index 8f75981ba..c8cf75dc2 100644 --- a/packages/core/tests/Charcoal/Source/PaginationTest.php +++ b/packages/core/tests/Charcoal/Source/PaginationTest.php @@ -21,10 +21,8 @@ class PaginationTest extends AbstractTestCase /** * Create expression for testing. - * - * @return Pagination */ - final protected function createExpression() + final protected function createExpression(): \Charcoal\Source\Pagination { return new Pagination(); } @@ -34,10 +32,8 @@ final protected function createExpression() * * Assertions: * 1. Implements {@see PaginationInterface} - * - * @return void */ - public function testPaginationConstruct() + public function testPaginationConstruct(): void { $obj = $this->createExpression(); @@ -49,9 +45,8 @@ public function testPaginationConstruct() * Provide data for value parsing. * * @used-by ExpressionTestTrait::testDefaultValues() - * @return array */ - final public function provideDefaultValues() + final public function provideDefaultValues(): array { return [ 'page num' => [ 'page', 1 ], @@ -70,10 +65,8 @@ final public function provideDefaultValues() * 3. Chainable method * 4. Accepts float * 5. Swaps zero for one - * - * @return void */ - public function testPageNum() + public function testPageNum(): void { $obj = $this->createExpression(); @@ -99,10 +92,8 @@ public function testPageNum() /** * Test "page" property with negative value. - * - * @return void */ - public function testPageNumWithNegativeValue() + public function testPageNumWithNegativeValue(): void { $obj = $this->createExpression(); @@ -112,10 +103,8 @@ public function testPageNumWithNegativeValue() /** * Test "page" property with invalid value. - * - * @return void */ - public function testPageNumWithInvalidValue() + public function testPageNumWithInvalidValue(): void { $obj = $this->createExpression(); @@ -131,10 +120,8 @@ public function testPageNumWithInvalidValue() * 2. Mutated state * 3. Chainable method * 4. Accepts float - * - * @return void */ - public function testNumPerPage() + public function testNumPerPage(): void { $obj = $this->createExpression(); @@ -156,10 +143,8 @@ public function testNumPerPage() /** * Test "num_per_page" property with negative value. - * - * @return void */ - public function testNumPerPageWithNegativeValue() + public function testNumPerPageWithNegativeValue(): void { $obj = $this->createExpression(); @@ -169,10 +154,8 @@ public function testNumPerPageWithNegativeValue() /** * Test "num_per_page" property with invalid value. - * - * @return void */ - public function testNumPerPageWithInvalidValue() + public function testNumPerPageWithInvalidValue(): void { $obj = $this->createExpression(); @@ -187,10 +170,8 @@ public function testNumPerPageWithInvalidValue() * 1. Mutate all options * 2. Partially mutated state * 3. Mutation via aliases - * - * @return void */ - public function testData() + public function testData(): void { /** 1. Mutate all options */ $mutation = [ @@ -244,10 +225,8 @@ public function testData() /** * Test lowest possible index. - * - * @return void */ - public function testFirst() + public function testFirst(): void { $obj = $this->createExpression(); @@ -262,10 +241,8 @@ public function testFirst() /** * Test highest possible index. - * - * @return void */ - public function testLast() + public function testLast(): void { $obj = $this->createExpression(); diff --git a/packages/core/tests/Charcoal/Source/SourceConfigTest.php b/packages/core/tests/Charcoal/Source/SourceConfigTest.php index f84c5653e..78bc901e6 100644 --- a/packages/core/tests/Charcoal/Source/SourceConfigTest.php +++ b/packages/core/tests/Charcoal/Source/SourceConfigTest.php @@ -13,10 +13,7 @@ */ class SourceConfigTest extends AbstractTestCase { - /** - * @return void - */ - public function testDefaultData() + public function testDefaultData(): void { $obj = new SourceConfig(); $defaults = $obj->defaults(); @@ -24,10 +21,7 @@ public function testDefaultData() $this->assertEquals($obj->type(), $defaults['type']); } - /** - * @return void - */ - public function testSetType() + public function testSetType(): void { $obj = new SourceConfig(); $ret = $obj->setType('foo'); diff --git a/packages/core/tests/Charcoal/Source/StorableTraitTest.php b/packages/core/tests/Charcoal/Source/StorableTraitTest.php index 83ae6750d..270adafbd 100644 --- a/packages/core/tests/Charcoal/Source/StorableTraitTest.php +++ b/packages/core/tests/Charcoal/Source/StorableTraitTest.php @@ -25,6 +25,24 @@ /** * Test {@see StorableTrait} and {@see StorableInterface}. */ +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\StorableTrait::class, 'setKey')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\StorableTrait::class, 'key')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\StorableTrait::class, 'setId')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\StorableTrait::class, 'id')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\StorableTrait::class, 'setSourceFactory')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\StorableTrait::class, 'sourceFactory')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\StorableTrait::class, 'createSource')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\StorableTrait::class, 'setSource')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\StorableTrait::class, 'source')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\StorableTrait::class, 'save')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\StorableTrait::class, 'preSave')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\StorableTrait::class, 'postSave')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\StorableTrait::class, 'update')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\StorableTrait::class, 'preUpdate')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\StorableTrait::class, 'postUpdate')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\StorableTrait::class, 'delete')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\StorableTrait::class, 'preDelete')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Source\StorableTrait::class, 'postDelete')] class StorableTraitTest extends AbstractTestCase { use ReflectionsTrait; @@ -38,8 +56,6 @@ class StorableTraitTest extends AbstractTestCase /** * Setup the test. - * - * @return void */ protected function setUp(): void { @@ -48,10 +64,8 @@ protected function setUp(): void /** * Create datasource repository for testing. - * - * @return SourceMock */ - final protected function createSource() + final protected function createSource(): \Charcoal\Tests\Mock\SourceMock { return new SourceMock([ 'logger' => new NullLogger() @@ -66,12 +80,8 @@ final protected function createSource() * 2. Mutated state * 3. Chainable method * - * @covers \Charcoal\Source\StorableTrait::setKey - * @covers \Charcoal\Source\StorableTrait::key - * - * @return void */ - public function testKey() + public function testKey(): void { $obj = $this->obj; @@ -89,11 +99,8 @@ public function testKey() /** * Test for invalid data type when assigning a primary object key. * - * @covers \Charcoal\Source\StorableTrait::setKey - * - * @return void */ - public function testKeyWithInvalidDataType() + public function testKeyWithInvalidDataType(): void { $this->expectException(InvalidArgumentException::class); $this->obj->setKey(null); @@ -102,11 +109,8 @@ public function testKeyWithInvalidDataType() /** * Test for invalid character set when assigning a primary object key. * - * @covers \Charcoal\Source\StorableTrait::setKey - * - * @return void */ - public function testKeyWithInvalidCharacters() + public function testKeyWithInvalidCharacters(): void { $this->expectException(InvalidArgumentException::class); $this->obj->setKey('foo-bar'); @@ -120,12 +124,8 @@ public function testKeyWithInvalidCharacters() * 2. Mutated state * 3. Chainable method * - * @covers \Charcoal\Source\StorableTrait::setId - * @covers \Charcoal\Source\StorableTrait::id - * - * @return void */ - public function testId() + public function testId(): void { $obj = $this->obj; @@ -149,11 +149,8 @@ public function testId() /** * Test for invalid data type when assigning a unique object ID. * - * @covers \Charcoal\Source\StorableTrait::setId - * - * @return void */ - public function testIdWithInvalidDataType() + public function testIdWithInvalidDataType(): void { $this->expectException(InvalidArgumentException::class); $this->obj->setId(null); @@ -162,12 +159,8 @@ public function testIdWithInvalidDataType() /** * Test the unique object ID with an alternate primary key. * - * @covers \Charcoal\Source\StorableTrait::setId - * @covers \Charcoal\Source\StorableTrait::id - * - * @return void */ - public function testAltId() + public function testAltId(): void { $obj = $this->obj; @@ -180,12 +173,8 @@ public function testAltId() /** * Test repository factory. * - * @covers \Charcoal\Source\StorableTrait::setSourceFactory - * @covers \Charcoal\Source\StorableTrait::sourceFactory - * - * @return void */ - public function testSourceFactory() + public function testSourceFactory(): void { $obj = $this->obj; @@ -203,11 +192,8 @@ public function testSourceFactory() /** * Test for missing repository factory. * - * @covers \Charcoal\Source\StorableTrait::sourceFactory - * - * @return void */ - public function testMissingSourceFactory() + public function testMissingSourceFactory(): void { $this->expectException(RuntimeException::class); $this->callMethod($this->obj, 'sourceFactory'); @@ -223,13 +209,8 @@ public function testMissingSourceFactory() * 4. Storable can create a repository * 5. Chainable method * - * @covers \Charcoal\Source\StorableTrait::createSource - * @covers \Charcoal\Source\StorableTrait::setSource - * @covers \Charcoal\Source\StorableTrait::source - * - * @return void */ - public function testSource() + public function testSource(): void { $obj = $this->obj; @@ -262,13 +243,8 @@ public function testSource() * 2. Fail Early * 3. Fail Late * - * @covers \Charcoal\Source\StorableTrait::save - * @covers \Charcoal\Source\StorableTrait::preSave - * @covers \Charcoal\Source\StorableTrait::postSave - * - * @return void */ - public function testSave() + public function testSave(): void { $src = $this->createSource(); @@ -296,13 +272,8 @@ public function testSave() * 2. Fail Early * 3. Fail Late * - * @covers \Charcoal\Source\StorableTrait::update - * @covers \Charcoal\Source\StorableTrait::preUpdate - * @covers \Charcoal\Source\StorableTrait::postUpdate - * - * @return void */ - public function testUpdate() + public function testUpdate(): void { $src = $this->createSource(); @@ -330,13 +301,8 @@ public function testUpdate() * 2. Fail Early * 3. Fail Late * - * @covers \Charcoal\Source\StorableTrait::delete - * @covers \Charcoal\Source\StorableTrait::preDelete - * @covers \Charcoal\Source\StorableTrait::postDelete - * - * @return void */ - public function testDelete() + public function testDelete(): void { $src = $this->createSource(); diff --git a/packages/core/tests/Charcoal/Validator/ValidatableTraitTest.php b/packages/core/tests/Charcoal/Validator/ValidatableTraitTest.php index 4116f8ae4..0601dc331 100644 --- a/packages/core/tests/Charcoal/Validator/ValidatableTraitTest.php +++ b/packages/core/tests/Charcoal/Validator/ValidatableTraitTest.php @@ -16,18 +16,12 @@ class ValidatableTraitTest extends AbstractTestCase */ public $obj; - /** - * @return void - */ protected function setUp(): void { $this->obj = new ValidatableClass(); } - /** - * @return void - */ - public function testConstructor() + public function testConstructor(): void { $obj = $this->obj; $this->assertInstanceOf(ValidatableClass::class, $obj); diff --git a/packages/core/tests/Charcoal/Validator/ValidatorResultTest.php b/packages/core/tests/Charcoal/Validator/ValidatorResultTest.php index c15badce4..957449c73 100644 --- a/packages/core/tests/Charcoal/Validator/ValidatorResultTest.php +++ b/packages/core/tests/Charcoal/Validator/ValidatorResultTest.php @@ -13,20 +13,14 @@ */ class ValidatorResultTest extends AbstractTestCase { - /** - * @return void - */ - public function testSetData() + public function testSetData(): void { $obj = new ValidatorResult(); $ret = $obj->setData([]); $this->assertSame($ret, $obj); } - /** - * @return void - */ - public function testSetIdent() + public function testSetIdent(): void { $obj = new ValidatorResult(); $this->assertEquals(null, $obj->ident()); @@ -39,10 +33,7 @@ public function testSetIdent() $obj->setIdent(false); } - /** - * @return void - */ - public function testSetLevel() + public function testSetLevel(): void { $obj = new ValidatorResult(); $this->assertEquals(null, $obj->level()); @@ -55,20 +46,14 @@ public function testSetLevel() $obj->setLevel(false); } - /** - * @return void - */ - public function testSetLevelWithInvalidLevelsThrowException() + public function testSetLevelWithInvalidLevelsThrowException(): void { $obj = new ValidatorResult(); $this->expectException(InvalidArgumentException::class); $obj->setLevel('foo'); } - /** - * @return void - */ - public function testSetMessage() + public function testSetMessage(): void { $obj = new ValidatorResult(); $this->assertEquals('', $obj->message()); @@ -81,10 +66,7 @@ public function testSetMessage() $obj->setMessage(false); } - /** - * @return void - */ - public function testSetTs() + public function testSetTs(): void { $obj = new ValidatorResult(); $ret = $obj->setTs('2015-01-01 00:00:00'); diff --git a/packages/core/tests/Charcoal/Validator/ValidatorTest.php b/packages/core/tests/Charcoal/Validator/ValidatorTest.php index 687dd3dc9..4aa900fd9 100644 --- a/packages/core/tests/Charcoal/Validator/ValidatorTest.php +++ b/packages/core/tests/Charcoal/Validator/ValidatorTest.php @@ -26,28 +26,19 @@ class ValidatorTest extends AbstractTestCase */ public $model; - /** - * @return void - */ protected function setUp(): void { $this->model = new ValidatableClass(); $this->obj = new ValidatorClass($this->model); } - /** - * @return void - */ - public function testConstructor() + public function testConstructor(): void { $obj = $this->obj; - $this->assertInstanceOf('\Charcoal\Validator\AbstractValidator', $obj); + $this->assertInstanceOf(\Charcoal\Validator\AbstractValidator::class, $obj); } - /** - * @return void - */ - public function testError() + public function testError(): void { $obj = $this->obj; $ret = $obj->error('foo'); @@ -55,10 +46,7 @@ public function testError() // var_dump($obj->errorResults()); } - /** - * @return void - */ - public function testWarning() + public function testWarning(): void { $obj = $this->obj; $ret = $obj->warning('foo'); @@ -66,10 +54,7 @@ public function testWarning() // var_dump($obj->warningResults()); } - /** - * @return void - */ - public function testNotice() + public function testNotice(): void { $obj = $this->obj; $ret = $obj->notice('foo'); @@ -77,10 +62,7 @@ public function testNotice() // var_dump($obj->noticeResults()); } - /** - * @return void - */ - public function testAddResult() + public function testAddResult(): void { $result = [ 'ident' => 'bar', @@ -100,11 +82,8 @@ public function testAddResult() $obj->addResult(false); } - /** - * @group time-sensitive - * @return void - */ - public function testResults() + #[\PHPUnit\Framework\Attributes\Group('time-sensitive')] + public function testResults(): void { $result = [ 'ident' => 'bar', @@ -128,10 +107,7 @@ public function testResults() $this->assertEquals([ ValidatorClass::ERROR => [ $expectedResult ] ], $actualResult); } - /** - * @return void - */ - public function testErrorResults() + public function testErrorResults(): void { $result1 = [ 'ident' => 'bar', @@ -161,10 +137,7 @@ public function testErrorResults() $this->assertEquals([ $expectedResult ], $actualResult); } - /** - * @return void - */ - public function testWarningResults() + public function testWarningResults(): void { $result1 = [ 'ident' => 'bar', @@ -194,10 +167,7 @@ public function testWarningResults() $this->assertEquals([ $expectedResult ], $actualResult); } - /** - * @return void - */ - public function testNoticeResults() + public function testNoticeResults(): void { $result1 = [ 'ident' => 'bar', @@ -227,10 +197,7 @@ public function testNoticeResults() $this->assertEquals([ $expectedResult ], $actualResult); } - /** - * @return void - */ - public function testMerge() + public function testMerge(): void { $result1 = [ 'ident' => 'bar', diff --git a/packages/email/composer.json b/packages/email/composer.json index 054321e32..34cf96651 100644 --- a/packages/email/composer.json +++ b/packages/email/composer.json @@ -1,8 +1,12 @@ { "name": "charcoal/email", - "type": "library", "description": "Email sending and queueing for Charcoal", - "keywords": ["charcoal", "email", "queue", "phpmailer"], + "keywords": [ + "charcoal", + "email", + "queue", + "phpmailer" + ], "homepage": "https://charcoal.locomotive.ca", "license": "MIT", "authors": [ @@ -11,13 +15,9 @@ "homepage": "https://locomotive.ca" } ], - "extra": { - "branch-alias": { - "dev-main": "5.x-dev" - } - }, + "type": "library", "require": { - "php": "^7.4 || ^8.0", + "php": "^8.3", "psr/log": "^1.0", "phpmailer/phpmailer": "~6.0", "charcoal/config": "^5.1", @@ -28,11 +28,11 @@ }, "require-dev": { "pimple/pimple": "^3.0", - "phpunit/phpunit": "^9.5", + "phpunit/phpunit": "^12.5", "squizlabs/php_codesniffer": "^3.5", "php-coveralls/php-coveralls": "^2.2", "mustache/mustache": "^2.11", - "phpstan/phpstan": "^1.6" + "phpstan/phpstan": "^2.0" }, "autoload": { "psr-4": { @@ -44,8 +44,10 @@ "Charcoal\\Tests\\": "tests/Charcoal" } }, - "replace": { - "locomotivemtl/charcoal-email": "*" + "extra": { + "branch-alias": { + "dev-main": "5.x-dev" + } }, "scripts": { "test": [ @@ -65,6 +67,9 @@ "phpcbf": "php vendor/bin/phpcbf -ps --colors src/ tests/", "phpstan": "php vendor/bin/phpstan analyze -l1 src/ tests/" }, + "replace": { + "locomotivemtl/charcoal-email": "*" + }, "minimum-stability": "dev", "prefer-stable": true } diff --git a/packages/email/src/Charcoal/Email/Api/V1/LinkAction.php b/packages/email/src/Charcoal/Email/Api/V1/LinkAction.php index c0d2cc1fd..0a347ee66 100644 --- a/packages/email/src/Charcoal/Email/Api/V1/LinkAction.php +++ b/packages/email/src/Charcoal/Email/Api/V1/LinkAction.php @@ -19,37 +19,18 @@ */ class LinkAction { - /** - * @var string - */ - private $linkId; - - /** - * @var Tracker - */ - private $tracker; - - /** - * @var FactoryInterface - */ - private $modelFactory; - /** * @param string $linkId Link ID. * @param Tracker $tracker Tracker service. * @param FactoryInterface $modelFactory Model factory, to create Link objects. */ - public function __construct(string $linkId, Tracker $tracker, FactoryInterface $modelFactory) + public function __construct(private readonly string $linkId, private readonly Tracker $tracker, private readonly FactoryInterface $modelFactory) { - $this->linkId = $linkId; - $this->tracker = $tracker; - $this->modelFactory = $modelFactory; } /** * @param Request $request PSR-7 Request. * @param Response $response PSR-7 Response. - * @return Response */ public function __invoke(Request $request, Response $response): Response { diff --git a/packages/email/src/Charcoal/Email/Api/V1/OpenAction.php b/packages/email/src/Charcoal/Email/Api/V1/OpenAction.php index 96b45f563..7c1fbcc92 100644 --- a/packages/email/src/Charcoal/Email/Api/V1/OpenAction.php +++ b/packages/email/src/Charcoal/Email/Api/V1/OpenAction.php @@ -17,30 +17,17 @@ */ class OpenAction { - /** - * @var string - */ - private $emailId; - - /** - * @var Tracker - */ - private $tracker; - /** * @param string $emailId Email log ID. * @param Tracker $tracker Tracker service. */ - public function __construct(string $emailId, Tracker $tracker) + public function __construct(private readonly string $emailId, private readonly Tracker $tracker) { - $this->emailId = $emailId; - $this->tracker = $tracker; } /** * @param Request $request PSR-7 Request. * @param Response $response PSR-7 Response. - * @return Response */ public function __invoke(Request $request, Response $response): Response { @@ -55,7 +42,7 @@ public function __invoke(Request $request, Response $response): Response /** * @return boolean|false|string */ - private function getBlankPng() + private function getBlankPng(): string { return base64_decode('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAAACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII='); } diff --git a/packages/email/src/Charcoal/Email/ApiModule.php b/packages/email/src/Charcoal/Email/ApiModule.php index 1a1d3a98d..37fac45b5 100644 --- a/packages/email/src/Charcoal/Email/ApiModule.php +++ b/packages/email/src/Charcoal/Email/ApiModule.php @@ -19,28 +19,23 @@ class ApiModule extends AbstractModule { public const BASE_PATH = '/email/v1'; - /** - * @return self - */ - public function setUp() + #[\Override] + public function setUp(): static { $this->setupPublicRoutes(); return $this; } - /** - * @return void - */ - private function setupPublicRoutes() + private function setupPublicRoutes(): void { $container = $this->app()->getContainer(); - $this->app()->group(self::BASE_PATH, function () use ($container) { + $this->app()->group(self::BASE_PATH, function () use ($container): void { $group = $this; - $group->get('/link/{linkId}', function (Request $request, Response $response, array $args) use ($container) { + $group->get('/link/{linkId}', function (Request $request, Response $response, array $args) use ($container): \Psr\Http\Message\ResponseInterface { $action = new LinkAction( $args['linkId'], $container['email/tracker'], @@ -49,7 +44,7 @@ private function setupPublicRoutes() return $action($request, $response); }); - $group->get('/open/{emailId}[.png]', function (Request $request, Response $response, array $args) use ($container) { + $group->get('/open/{emailId}[.png]', function (Request $request, Response $response, array $args) use ($container): \Psr\Http\Message\ResponseInterface { $action = new OpenAction( $args['emailId'], $container['email/tracker'] diff --git a/packages/email/src/Charcoal/Email/Email.php b/packages/email/src/Charcoal/Email/Email.php index 670bac83e..01d36dabb 100644 --- a/packages/email/src/Charcoal/Email/Email.php +++ b/packages/email/src/Charcoal/Email/Email.php @@ -46,38 +46,28 @@ class Email extends AbstractEntity implements /** * The campaign ID. - * - * @var string */ - private $campaign; + private ?string $campaign = null; /** * The recipient email address(es). - * - * @var array */ - private $to = []; + private array $to = []; /** * The CC recipient email address(es). - * - * @var array */ - private $cc = []; + private array $cc = []; /** * The BCC recipient email address(es). - * - * @var array */ - private $bcc = []; + private array $bcc = []; /** * The sender's email address. - * - * @var string */ - private $from; + private ?string $from = null; /** * The email address to reply to the message. @@ -88,24 +78,18 @@ class Email extends AbstractEntity implements /** * The email subject. - * - * @var string */ - private $subject; + private ?string $subject = null; /** * The HTML message body. - * - * @var string */ - private $msgHtml; + private ?string $msgHtml = null; /** * The plain-text message body. - * - * @var string */ - private $msgTxt; + private ?string $msgTxt = null; /** * @var array @@ -133,35 +117,18 @@ class Email extends AbstractEntity implements /** * The data to pass onto the view controller. - * - * @var array */ - private $templateData = []; + private array $templateData = []; - /** - * @var PHPMailer - */ - private $phpMailer; + private \PHPMailer\PHPMailer\PHPMailer $phpMailer; - /** - * @var FactoryInterface - */ - private $templateFactory; + private \Charcoal\Factory\FactoryInterface $templateFactory; - /** - * @var FactoryInterface - */ - private $queueItemFactory; + private \Charcoal\Factory\FactoryInterface $queueItemFactory; - /** - * @var FactoryInterface - */ - private $logFactory; + private \Charcoal\Factory\FactoryInterface $logFactory; - /** - * @var Tracker - */ - private $tracker; + private \Charcoal\Email\Services\Tracker $tracker; /** * Construct a new Email object with the given dependencies. @@ -191,9 +158,8 @@ public function __construct(array $data) * Set the campaign ID. * * @param string $campaign The campaign identifier. - * @return self */ - public function setCampaign(string $campaign) + public function setCampaign(string $campaign): static { $this->campaign = $campaign; return $this; @@ -203,10 +169,8 @@ public function setCampaign(string $campaign) * Get the campaign identifier. * * If it has not been explicitely set, it will be auto-generated (with uniqid). - * - * @return string */ - public function campaign() + public function campaign(): string { if ($this->campaign === null) { $this->campaign = $this->generateCampaign(); @@ -219,9 +183,8 @@ public function campaign() * * @param string|array $email The recipient email address(es). * @throws InvalidArgumentException If the email address is invalid. - * @return self */ - public function setTo($email) + public function setTo($email): static { if (is_string($email)) { $email = [ $email ]; @@ -253,9 +216,8 @@ public function setTo($email) * * @param mixed $email The recipient email address to add. * @throws InvalidArgumentException If the email address is invalid. - * @return self */ - public function addTo($email) + public function addTo($email): static { $this->to[] = $this->parseEmail($email); return $this; @@ -266,7 +228,7 @@ public function addTo($email) * * @return string[] */ - public function to() + public function to(): array { return $this->to; } @@ -276,9 +238,8 @@ public function to() * * @param string|array $email The CC recipient email address(es). * @throws InvalidArgumentException If the email address is invalid. - * @return self */ - public function setCc($email) + public function setCc($email): static { if (is_string($email)) { $email = [ $email ]; @@ -310,9 +271,8 @@ public function setCc($email) * * @param mixed $email The CC recipient email address to add. * @throws InvalidArgumentException If the email address is invalid. - * @return self */ - public function addCc($email) + public function addCc($email): static { $this->cc[] = $this->parseEmail($email); return $this; @@ -323,7 +283,7 @@ public function addCc($email) * * @return string[] */ - public function cc() + public function cc(): array { return $this->cc; } @@ -333,9 +293,8 @@ public function cc() * * @param string|array $email The BCC recipient email address(es). * @throws InvalidArgumentException If the email address is invalid. - * @return self */ - public function setBcc($email) + public function setBcc($email): static { if (is_string($email)) { // Means we have a straight email @@ -368,9 +327,8 @@ public function setBcc($email) * * @param mixed $email The BCC recipient email address to add. * @throws InvalidArgumentException If the email address is invalid. - * @return self */ - public function addBcc($email) + public function addBcc($email): static { $this->bcc[] = $this->parseEmail($email); return $this; @@ -381,7 +339,7 @@ public function addBcc($email) * * @return string[] */ - public function bcc() + public function bcc(): array { return $this->bcc; } @@ -391,10 +349,9 @@ public function bcc() * * @param string|array $email An email address. * @throws InvalidArgumentException If the email is not a string or an array. - * @return self * @todo Implement optional "Sender" field. */ - public function setFrom($email) + public function setFrom($email): static { $this->from = $this->parseEmail($email); return $this; @@ -405,7 +362,7 @@ public function setFrom($email) * * @return string */ - public function from() + public function from(): ?string { if ($this->from === null) { $this->setFrom($this->config()->defaultFrom()); @@ -418,9 +375,8 @@ public function from() * * @param mixed $email The sender's "Reply-To" email address. * @throws InvalidArgumentException If the email is not a string or an array. - * @return self */ - public function setReplyTo($email) + public function setReplyTo($email): static { $this->replyTo = $this->parseEmail($email); return $this; @@ -443,9 +399,8 @@ public function replyTo() * Set the email subject. * * @param string $subject The email subject. - * @return self */ - public function setSubject(string $subject) + public function setSubject(string $subject): static { $this->subject = $subject; return $this; @@ -465,9 +420,8 @@ public function subject(): string * Set the email's HTML message body. * * @param string $body The HTML message body. - * @return self */ - public function setMsgHtml(string $body) + public function setMsgHtml(string $body): static { $this->msgHtml = $body; return $this; @@ -478,8 +432,6 @@ public function setMsgHtml(string $body) * * If the message is not explitely set, it will be * auto-generated from a template view. - * - * @return string */ public function msgHtml(): string { @@ -493,9 +445,8 @@ public function msgHtml(): string * Set the email's plain-text message body. * * @param string $body The message's text body. - * @return self */ - public function setMsgTxt(string $body) + public function setMsgTxt(string $body): static { $this->msgTxt = $body; return $this; @@ -506,8 +457,6 @@ public function setMsgTxt(string $body) * * If the plain-text message is not explitely set, * it will be auto-generated from the HTML message. - * - * @return string */ public function msgTxt(): string { @@ -521,9 +470,8 @@ public function msgTxt(): string * Set the email's attachments. * * @param array $attachments The file attachments. - * @return self */ - public function setAttachments(array $attachments) + public function setAttachments(array $attachments): static { foreach ($attachments as $att) { $this->addAttachment($att); @@ -535,9 +483,8 @@ public function setAttachments(array $attachments) * Add an attachment to the email. * * @param mixed $attachment A single file attachment. - * @return self */ - public function addAttachment($attachment) + public function addAttachment($attachment): static { $this->attachments[] = $attachment; return $this; @@ -557,18 +504,15 @@ public function attachments() * Enable or disable logging for this particular email. * * @param boolean $log The log-enabled flag. - * @return self */ - public function setLogEnabled($log) + public function setLogEnabled($log): static { - $this->logEnabled = !!$log; + $this->logEnabled = (bool) $log; return $this; } /** * Determine if logging is enabled for this particular email. - * - * @return boolean */ public function logEnabled(): bool { @@ -582,18 +526,15 @@ public function logEnabled(): bool * Enable or disable email open tracking for this particular email. * * @param boolean $track The track flag. - * @return self */ - public function setTrackOpenEnabled($track) + public function setTrackOpenEnabled($track): static { - $this->trackOpenEnabled = !!$track; + $this->trackOpenEnabled = (bool) $track; return $this; } /** * Determine if email open tracking is enabled for this particular email. - * - * @return boolean */ public function trackOpenEnabled(): bool { @@ -607,18 +548,15 @@ public function trackOpenEnabled(): bool * Enable or disable email links tracking for this particular email. * * @param boolean $track The track flag. - * @return self */ - public function setTrackLinksEnabled($track) + public function setTrackLinksEnabled($track): static { - $this->trackLinksEnabled = !!$track; + $this->trackLinksEnabled = (bool) $track; return $this; } /** * Determine if email links tracking is enabled for this particular email. - * - * @return boolean */ public function trackLinksEnabled(): bool { @@ -694,10 +632,10 @@ public function send(): bool $logId = uniqid(); - if ($this->trackOpenEnabled() === true) { + if ($this->trackOpenEnabled()) { $this->tracker->addOpenTrackingImage($this, $logId); } - if ($this->trackLinksEnabled() === true) { + if ($this->trackLinksEnabled()) { $this->tracker->replaceLinksWithTracker($this, $logId); } @@ -717,7 +655,7 @@ public function send(): bool throw new EmailNotSentException($e->getMessage(), $e->getCode(), $e); } - if ($this->logEnabled() === true) { + if ($this->logEnabled()) { try { $this->logSend($ret, $logId, $mail); } catch (PDOException $e) { @@ -760,9 +698,8 @@ protected function setSmtpOptions(PHPMailer $mail) * Enqueue the email for each recipient. * * @param mixed $ts A date/time to initiate the queue processing. - * @return self */ - public function queue($ts = null) + public function queue($ts = null): static { $recipients = $this->to(); $author = $this->from(); @@ -773,7 +710,7 @@ public function queue($ts = null) $queueId = $this->queueId(); foreach ($recipients as $to) { - if (is_string($to) && !empty($to)) { + if (is_string($to) && ($to !== '' && $to !== '0')) { $queueItem = $this->queueItemFactory()->create(EmailQueueItem::class); $queueItem->setTo($to); @@ -800,7 +737,7 @@ public function queue($ts = null) * @param array $data The template data. * @return Email Chainable */ - public function setTemplateData(array $data) + public function setTemplateData(array $data): static { $this->templateData = $data; return $this; @@ -808,8 +745,6 @@ public function setTemplateData(array $data) /** * Get the template data for the view. - * - * @return array */ public function templateData(): array { @@ -847,15 +782,12 @@ public function viewController() * @param FactoryInterface $factory The factory to use to create email template objects. * @return Email Chainable */ - protected function setTemplateFactory(FactoryInterface $factory) + protected function setTemplateFactory(FactoryInterface $factory): static { $this->templateFactory = $factory; return $this; } - /** - * @return FactoryInterface - */ protected function templateFactory(): FactoryInterface { return $this->templateFactory; @@ -865,15 +797,12 @@ protected function templateFactory(): FactoryInterface * @param FactoryInterface $factory The factory to use to create email queue item objects. * @return Email Chainable */ - protected function setQueueItemFactory(FactoryInterface $factory) + protected function setQueueItemFactory(FactoryInterface $factory): static { $this->queueItemFactory = $factory; return $this; } - /** - * @return FactoryInterface - */ protected function queueItemFactory(): FactoryInterface { return $this->queueItemFactory; @@ -883,15 +812,12 @@ protected function queueItemFactory(): FactoryInterface * @param FactoryInterface $factory The factory to use to create log objects. * @return Email Chainable */ - protected function setLogFactory(FactoryInterface $factory) + protected function setLogFactory(FactoryInterface $factory): static { $this->logFactory = $factory; return $this; } - /** - * @return FactoryInterface - */ protected function logFactory(): FactoryInterface { return $this->logFactory; @@ -899,9 +825,8 @@ protected function logFactory(): FactoryInterface /** * @param Tracker $tracker Tracker service. - * @return void */ - public function setTracker(Tracker $tracker) + public function setTracker(Tracker $tracker): void { $this->tracker = $tracker; } @@ -910,25 +835,16 @@ public function setTracker(Tracker $tracker) * Get the email's HTML message from the template, if applicable. * * @see ViewableInterface::render() - * @return string */ protected function generateMsgHtml(): string { $templateIdent = $this->templateIdent(); - if (!$templateIdent) { - $message = ''; - } else { - $message = $this->render($templateIdent); - } - - return $message; + return $templateIdent ? $this->render($templateIdent) : ''; } /** * Generates a unique identifier ideal for a campaign ID. - * - * @return string */ protected function generateCampaign(): string { @@ -984,18 +900,17 @@ protected function stripHtml(string $html): string '#]*?>.*?#siu' ], '', - $str + (string) $str ); - $str = strip_tags($str); + $str = strip_tags((string) $str); // Trim whitespace $str = str_replace("\t", '', $str); $str = preg_replace('#\n\r|\r\n#', "\n", $str); - $str = preg_replace('#\n{3,}#', "\n\n", $str); - $str = preg_replace('/ {2,}/', ' ', $str); - $str = implode("\n", array_map('trim', explode("\n", $str))); - $str = trim($str) . "\n"; - return $str; + $str = preg_replace('#\n{3,}#', "\n\n", (string) $str); + $str = preg_replace('/ {2,}/', ' ', (string) $str); + $str = implode("\n", array_map(trim(...), explode("\n", (string) $str))); + return trim($str) . "\n"; } /** @@ -1004,7 +919,6 @@ protected function stripHtml(string $html): string * @param boolean $result Success or failure. * @param string $logId Email log id. * @param PHPMailer $mailer The raw mailer. - * @return void */ protected function logSend(bool $result, string $logId, PHPMailer $mailer): void { @@ -1045,10 +959,8 @@ protected function logSend(bool $result, string $logId, PHPMailer $mailer): void /** * Temporary hack to fulfills the Configurable Interface. - * - * @return EmailConfig */ - public function createConfig() + public function createConfig(): \Charcoal\Email\EmailConfig { // This should really be avoided. $this->logger->warning('AbstractEmail::createConfig() was called, but should not.'); diff --git a/packages/email/src/Charcoal/Email/EmailAwareTrait.php b/packages/email/src/Charcoal/Email/EmailAwareTrait.php index 8a0de6228..51c732278 100644 --- a/packages/email/src/Charcoal/Email/EmailAwareTrait.php +++ b/packages/email/src/Charcoal/Email/EmailAwareTrait.php @@ -19,16 +19,12 @@ trait EmailAwareTrait /** * @param Parser $parser Email parser service. - * @return void */ protected function setParser(Parser $parser): void { $this->parser = $parser; } - /** - * @return Parser - */ protected function getParser(): Parser { if ($this->parser === null) { @@ -40,7 +36,6 @@ protected function getParser(): Parser /** * @param mixed $email An email value (either a string or an array). * @throws InvalidArgumentException If the email is invalid. - * @return string */ protected function parseEmail($email): string { @@ -52,7 +47,6 @@ protected function parseEmail($email): string * * @param mixed $var An email array (containing an "email" key and optionally a "name" key). * @throws InvalidArgumentException If the email is invalid. - * @return array|null */ protected function emailToArray($var): ?array { @@ -64,7 +58,6 @@ protected function emailToArray($var): ?array * * @param array $arr An email array (containing an "email" key and optionally a "name" key). * @throws InvalidArgumentException If the email array is invalid. - * @return string */ protected function emailFromArray(array $arr): string { diff --git a/packages/email/src/Charcoal/Email/EmailConfig.php b/packages/email/src/Charcoal/Email/EmailConfig.php index 068d46e92..4f8db29d6 100644 --- a/packages/email/src/Charcoal/Email/EmailConfig.php +++ b/packages/email/src/Charcoal/Email/EmailConfig.php @@ -17,94 +17,69 @@ class EmailConfig extends AbstractConfig /** * Whether SMTP should be used. - * - * @var boolean $smtp */ - private $smtp = false; + private bool $smtp = false; /** * The SMTP hostname. - * - * @var string $smtpHostname */ - private $smtpHostname; + private ?string $smtpHostname = null; /** * The SMTP port. - * - * @var integer $smtpPort */ - private $smtpPort; + private ?int $smtpPort = null; /** * The SMTP security type. - * - * @var string $smtpSecurity */ - private $smtpSecurity = ''; + private string $smtpSecurity = ''; /** * Whether SMTP requires authentication. - * - * @var boolean $smtpAuth */ - private $smtpAuth; + private ?bool $smtpAuth = null; /** * The SMTP username. - * - * @var string $smtpUsername */ - private $smtpUsername; + private ?string $smtpUsername = null; /** * The SMTP password. - * - * @var string $smtpPassword */ - private $smtpPassword; + private ?string $smtpPassword = null; /** * The default sender's email address. - * - * @var string $defaultFrom */ - private $defaultFrom; + private ?string $defaultFrom = null; /** * The default "Reply-To" email address. - * - * @var string $defaultReplyTo */ - private $defaultReplyTo; + private ?string $defaultReplyTo = null; /** * Whether the email (open) should be tracked by default. - * - * @var boolean $defaultTrack */ - private $defaultTrackOpenEnabled; + private ?bool $defaultTrackOpenEnabled = null; /** * Whether the email (links) should be tracked by default. - * - * @var boolean $defaultTrack */ - private $defaultTrackLinksEnabled; + private ?bool $defaultTrackLinksEnabled = null; /** * Whether the email should be logged by default. - * - * @var boolean $defaultLog */ - private $defaultLogEnabled; + private ?bool $defaultLogEnabled = null; /** * Default email configuration. - * - * @return array */ + #[\Override] public function defaults(): array { return [ @@ -124,20 +99,17 @@ public function defaults(): array * * @param boolean $smtp If the email should be sent using SMTP or not. * @throws InvalidArgumentException If the SMTP state is not a boolean. - * @return self */ - public function setSmtp($smtp) + public function setSmtp($smtp): static { - $this->smtp = !!$smtp; + $this->smtp = (bool) $smtp; return $this; } /** * Determine if SMTP should be used. - * - * @return boolean */ - public function smtp() + public function smtp(): bool { return $this->smtp; } @@ -147,9 +119,8 @@ public function smtp() * * @param string $hostname The SMTP hostname. * @throws InvalidArgumentException If the SMTP hostname is not a string. - * @return self */ - public function setSmtpHostname($hostname) + public function setSmtpHostname($hostname): static { if (!is_string($hostname)) { throw new InvalidArgumentException( @@ -167,7 +138,7 @@ public function setSmtpHostname($hostname) * * @return string */ - public function smtpHostname() + public function smtpHostname(): ?string { return $this->smtpHostname; } @@ -177,9 +148,8 @@ public function smtpHostname() * * @param integer $port The SMTP port. * @throws InvalidArgumentException If the SMTP port is not an integer. - * @return self */ - public function setSmtpPort($port) + public function setSmtpPort($port): static { if (!is_int($port)) { throw new InvalidArgumentException( @@ -197,7 +167,7 @@ public function setSmtpPort($port) * * @return integer */ - public function smtpPort() + public function smtpPort(): ?int { return $this->smtpPort; } @@ -206,11 +176,10 @@ public function smtpPort() * Set whether SMTP requires authentication. * * @param boolean $auth The SMTP authentication flag (if auth is required). - * @return self */ - public function setSmtpAuth($auth) + public function setSmtpAuth($auth): static { - $this->smtpAuth = !!$auth; + $this->smtpAuth = (bool) $auth; return $this; } @@ -219,7 +188,7 @@ public function setSmtpAuth($auth) * * @return boolean */ - public function smtpAuth() + public function smtpAuth(): ?bool { return $this->smtpAuth; } @@ -229,9 +198,8 @@ public function smtpAuth() * * @param string $username The SMTP username, if using authentication. * @throws InvalidArgumentException If the SMTP username is not a string. - * @return self */ - public function setSmtpUsername($username) + public function setSmtpUsername($username): static { if (!is_string($username)) { throw new InvalidArgumentException( @@ -249,7 +217,7 @@ public function setSmtpUsername($username) * * @return string */ - public function smtpUsername() + public function smtpUsername(): ?string { return $this->smtpUsername; } @@ -259,9 +227,8 @@ public function smtpUsername() * * @param string $password The SMTP password, if using authentication. * @throws InvalidArgumentException If the SMTP password is not a string. - * @return self */ - public function setSmtpPassword($password) + public function setSmtpPassword($password): static { if (!is_string($password)) { throw new InvalidArgumentException( @@ -279,7 +246,7 @@ public function setSmtpPassword($password) * * @return string */ - public function smtpPassword() + public function smtpPassword(): ?string { return $this->smtpPassword; } @@ -289,9 +256,8 @@ public function smtpPassword() * * @param string $security The SMTP security type (empty, "TLS", or "SSL"). * @throws InvalidArgumentException If the security type is not valid (empty, "TLS", or "SSL"). - * @return self */ - public function setSmtpSecurity($security) + public function setSmtpSecurity($security): static { $security = strtoupper($security); $validSecurity = [ '', 'TLS', 'SSL' ]; @@ -309,10 +275,8 @@ public function setSmtpSecurity($security) /** * Get the SMTP security type. - * - * @return string */ - public function smtpSecurity() + public function smtpSecurity(): string { return $this->smtpSecurity; } @@ -321,9 +285,8 @@ public function smtpSecurity() * Set the default sender's email address. * * @param string|array $email The default "From" email address. - * @return self */ - public function setDefaultFrom($email) + public function setDefaultFrom($email): static { $this->defaultFrom = $this->parseEmail($email); return $this; @@ -334,7 +297,7 @@ public function setDefaultFrom($email) * * @return string */ - public function defaultFrom() + public function defaultFrom(): ?string { return $this->defaultFrom; } @@ -343,9 +306,8 @@ public function defaultFrom() * Set the default "Reply-To" email address. * * @param string|array $email The default "Reply-To" email address. - * @return self */ - public function setDefaultReplyTo($email) + public function setDefaultReplyTo($email): static { $this->defaultReplyTo = $this->parseEmail($email); return $this; @@ -356,7 +318,7 @@ public function setDefaultReplyTo($email) * * @return string */ - public function defaultReplyTo() + public function defaultReplyTo(): ?string { return $this->defaultReplyTo; } @@ -365,11 +327,10 @@ public function defaultReplyTo() * Set whether the email sending should be logged by default. * * @param boolean $log The default log flag. - * @return self */ - public function setDefaultLogEnabled($log) + public function setDefaultLogEnabled($log): static { - $this->defaultLogEnabled = !!$log; + $this->defaultLogEnabled = (bool) $log; return $this; } @@ -378,7 +339,7 @@ public function setDefaultLogEnabled($log) * * @return boolean */ - public function defaultLogEnabled() + public function defaultLogEnabled(): ?bool { return $this->defaultLogEnabled; } @@ -387,11 +348,10 @@ public function defaultLogEnabled() * Set whether the email (open) should be tracked by default. * * @param boolean $track The default track flag. - * @return self */ - public function setDefaultTrackOpenEnabled($track) + public function setDefaultTrackOpenEnabled($track): static { - $this->defaultTrackOpenEnabled = !!$track; + $this->defaultTrackOpenEnabled = (bool) $track; return $this; } @@ -400,7 +360,7 @@ public function setDefaultTrackOpenEnabled($track) * * @return boolean */ - public function defaultTrackOpenEnabled() + public function defaultTrackOpenEnabled(): ?bool { return $this->defaultTrackOpenEnabled; } @@ -409,11 +369,10 @@ public function defaultTrackOpenEnabled() * Set whether the email links should be tracked by default. * * @param boolean $track The default track flag. - * @return self */ - public function setDefaultTrackLinksEnabled($track) + public function setDefaultTrackLinksEnabled($track): static { - $this->defaultTrackLinksEnabled = !!$track; + $this->defaultTrackLinksEnabled = (bool) $track; return $this; } @@ -422,7 +381,7 @@ public function setDefaultTrackLinksEnabled($track) * * @return boolean */ - public function defaultTrackLinksEnabled() + public function defaultTrackLinksEnabled(): ?bool { return $this->defaultTrackLinksEnabled; } diff --git a/packages/email/src/Charcoal/Email/EmailInterface.php b/packages/email/src/Charcoal/Email/EmailInterface.php index ca4204066..4ff73e087 100644 --- a/packages/email/src/Charcoal/Email/EmailInterface.php +++ b/packages/email/src/Charcoal/Email/EmailInterface.php @@ -156,8 +156,6 @@ public function setMsgHtml(string $body); /** * Get the email's HTML message body. - * - * @return string */ public function msgHtml(): string; @@ -171,8 +169,6 @@ public function setMsgTxt(string $body); /** * Get the email's plain-text message body. - * - * @return string */ public function msgTxt(): string; @@ -209,8 +205,6 @@ public function setLogEnabled($log); /** * Determine if logging is enabled for this particular email. - * - * @return boolean */ public function logEnabled(): bool; @@ -224,8 +218,6 @@ public function setTrackOpenEnabled($track); /** * Determine if tracking is enabled for this particular email. - * - * @return boolean */ public function trackLinksEnabled(): bool; @@ -239,8 +231,6 @@ public function setTrackLinksEnabled($track); /** * Determine if tracking is enabled for this particular email. - * - * @return boolean */ public function trackOpenEnabled(): bool; diff --git a/packages/email/src/Charcoal/Email/EmailLog.php b/packages/email/src/Charcoal/Email/EmailLog.php index 376ebda71..1a8ec67d9 100644 --- a/packages/email/src/Charcoal/Email/EmailLog.php +++ b/packages/email/src/Charcoal/Email/EmailLog.php @@ -1,5 +1,7 @@ to = $this->parseEmail($email); - } catch (Exception $e) { + } catch (Exception) { $this->logger->warning(sprintf('Invalid "to" email: "%s"', strval($email))); } @@ -103,7 +94,7 @@ public function setTo($email) * * @return string */ - public function to() + public function to(): ?string { return $this->to; } @@ -112,13 +103,12 @@ public function to() * Set the sender's email address. * * @param string|array $email An email address. - * @return self */ - public function setFrom($email) + public function setFrom($email): static { try { $this->from = $this->parseEmail($email); - } catch (Exception $e) { + } catch (Exception) { $this->logger->warning(sprintf('Invalid "from" email: "%s"', strval($email))); } @@ -130,7 +120,7 @@ public function setFrom($email) * * @return string */ - public function from() + public function from(): ?string { return $this->from; } @@ -139,9 +129,8 @@ public function from() * Set the email subject. * * @param string $subject The email subject. - * @return self */ - public function setSubject($subject) + public function setSubject($subject): static { $this->subject = $subject; @@ -162,9 +151,8 @@ public function subject() * Set the email's HTML message body. * * @param string $body The HTML message body. - * @return self */ - public function setMsgHtml($body) + public function setMsgHtml($body): static { $this->msgHtml = $body; @@ -185,9 +173,8 @@ public function msgHtml() * Set the email's plain-text message body. * * @param string $body The plain-text mesage body. - * @return self */ - public function setMsgTxt($body) + public function setMsgTxt($body): static { $this->msgTxt = $body; @@ -208,9 +195,8 @@ public function msgTxt() * Set the campaign ID. * * @param string $campaign The campaign identifier. - * @return self */ - public function setCampaign($campaign) + public function setCampaign($campaign): static { $this->campaign = $campaign; @@ -240,9 +226,9 @@ public function campaign() * processed. */ public function process( - callable $alwaysCallback = null, - callable $successCallback = null, - callable $failureCallback = null + ?callable $alwaysCallback = null, + ?callable $successCallback = null, + ?callable $failureCallback = null ): ?bool { $email = $this->emailFactory()->create('email'); $email->setData($this->data()); @@ -269,16 +255,13 @@ public function process( // Clear cumbersome DB data $this->setMsgHtml(null) ->setMsgTxt(null); - - array_push($propsToUpdate, 'msg_html', 'msg_txt'); - + $propsToUpdate[] = 'msg_html'; + $propsToUpdate[] = 'msg_txt'; if ($successCallback !== null) { $successCallback($this); } - } else { - if ($failureCallback !== null) { - $failureCallback($this); - } + } elseif ($failureCallback !== null) { + $failureCallback($this); } $this->update(array_merge([ @@ -294,8 +277,8 @@ public function process( /** * @param Container $container Pimple DI container. - * @return void */ + #[\Override] protected function setDependencies(Container $container): void { parent::setDependencies($container); @@ -305,9 +288,9 @@ protected function setDependencies(Container $container): void /** * Hook called before saving the item. * - * @return boolean * @see \Charcoal\Queue\QueueItemTrait::preSaveQueueItem() */ + #[\Override] protected function preSave(): bool { parent::preSave(); @@ -317,9 +300,6 @@ protected function preSave(): bool return true; } - /** - * @return FactoryInterface - */ protected function emailFactory(): FactoryInterface { return $this->emailFactory; @@ -327,7 +307,6 @@ protected function emailFactory(): FactoryInterface /** * @param FactoryInterface $factory The factory to create email objects. - * @return void */ private function setEmailFactory(FactoryInterface $factory): void { diff --git a/packages/email/src/Charcoal/Email/EmailQueueManager.php b/packages/email/src/Charcoal/Email/EmailQueueManager.php index 9d94e33fe..ff6b7af4f 100644 --- a/packages/email/src/Charcoal/Email/EmailQueueManager.php +++ b/packages/email/src/Charcoal/Email/EmailQueueManager.php @@ -1,5 +1,7 @@ queueId = $queueId; @@ -110,9 +100,8 @@ public function queueId() * Set the error code. * * @param string $errorCode The error code. - * @return self */ - public function setErrorCode($errorCode) + public function setErrorCode($errorCode): static { $this->errorCode = $errorCode; @@ -134,9 +123,8 @@ public function errorCode() * * @param string $messageId The Message-ID. * @throws InvalidArgumentException If the Message-ID is not a string. - * @return self */ - public function setMessageId($messageId) + public function setMessageId($messageId): static { if (!is_string($messageId)) { throw new InvalidArgumentException( @@ -154,7 +142,7 @@ public function setMessageId($messageId) * * @return string */ - public function messageId() + public function messageId(): ?string { return $this->messageId; } @@ -164,9 +152,8 @@ public function messageId() * * @param string $campaign The campaign identifier. * @throws InvalidArgumentException If the campaign is invalid. - * @return self */ - public function setCampaign($campaign) + public function setCampaign($campaign): static { if ($campaign !== null && !is_string($campaign)) { throw new InvalidArgumentException( @@ -194,9 +181,8 @@ public function campaign() * * @param string|array $email An email address. * @throws InvalidArgumentException If the email address is invalid. - * @return self */ - public function setFrom($email) + public function setFrom($email): static { $this->from = $this->parseEmail($email); return $this; @@ -207,7 +193,7 @@ public function setFrom($email) * * @return string */ - public function from() + public function from(): ?string { return $this->from; } @@ -216,9 +202,8 @@ public function from() * Set the recipient's email address. * * @param string|array $email An email address. - * @return self */ - public function setTo($email) + public function setTo($email): static { $this->to = $this->parseEmail($email); return $this; @@ -229,7 +214,7 @@ public function setTo($email) * * @return string */ - public function to() + public function to(): ?string { return $this->to; } @@ -239,9 +224,8 @@ public function to() * * @param string $subject The email subject. * @throws InvalidArgumentException If the subject is not a string. - * @return self */ - public function setSubject($subject) + public function setSubject($subject): static { if (!is_string($subject)) { throw new InvalidArgumentException( @@ -259,7 +243,7 @@ public function setSubject($subject) * * @return string */ - public function subject() + public function subject(): ?string { return $this->subject; } @@ -267,9 +251,8 @@ public function subject() /** * @param null|string|DateTime $ts The "send date" datetime value. * @throws InvalidArgumentException If the ts is not a valid datetime value. - * @return self */ - public function setSendTs($ts) + public function setSendTs($ts): static { if ($ts === null) { $this->sendTs = null; @@ -280,7 +263,7 @@ public function setSendTs($ts) try { $ts = new DateTime($ts); } catch (Exception $e) { - throw new InvalidArgumentException($e->getMessage()); + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); } } @@ -294,23 +277,20 @@ public function setSendTs($ts) return $this; } - /** - * @return null|DateTimeInterface - */ - public function sendTs() + public function sendTs(): ?\DateTimeInterface { return $this->sendTs; } /** * @see StorableTrait::preSave() - * @return boolean */ + #[\Override] protected function preSave(): bool { parent::preSave(); - if ($this->sendTs() === null) { + if (!$this->sendTs() instanceof \DateTimeInterface) { $this->setSendTs('now'); } diff --git a/packages/email/src/Charcoal/Email/Objects/Link.php b/packages/email/src/Charcoal/Email/Objects/Link.php index 4a3ae36f3..09c489bca 100644 --- a/packages/email/src/Charcoal/Email/Objects/Link.php +++ b/packages/email/src/Charcoal/Email/Objects/Link.php @@ -24,17 +24,13 @@ class Link extends AbstractModel /** * @param string $emailId The email (log) id. - * @return self */ - public function setEmail(?string $emailId) + public function setEmail(?string $emailId): static { $this->email = $emailId; return $this; } - /** - * @return string|null - */ public function email(): ?string { return $this->email; @@ -42,17 +38,13 @@ public function email(): ?string /** * @param string $url The original (and target) URL. - * @return self */ - public function setUrl(?string $url) + public function setUrl(?string $url): static { $this->url = $url; return $this; } - /** - * @return string|null - */ public function url(): ?string { return $this->url; diff --git a/packages/email/src/Charcoal/Email/Objects/LinkLog.php b/packages/email/src/Charcoal/Email/Objects/LinkLog.php index e9d83e5aa..5f728b584 100644 --- a/packages/email/src/Charcoal/Email/Objects/LinkLog.php +++ b/packages/email/src/Charcoal/Email/Objects/LinkLog.php @@ -16,26 +16,16 @@ */ class LinkLog extends AbstractModel { - /** - * @var string|null - */ - private $link; + private ?string $link = null; - /** - * @var DateTimeInterface|null - */ - private $ts; + private ?\DateTimeInterface $ts = null; - /** - * @var string|null - */ - private $ip; + private ?string $ip = null; /** * @param string|null $linkId The link id. - * @return self */ - public function setLink(?string $linkId) + public function setLink(?string $linkId): static { $this->link = $linkId; return $this; @@ -52,9 +42,8 @@ public function link(): ?string /** * @param null|string|DateTimeInterface $ts The "timestamp" datetime value. * @throws InvalidArgumentException If the timestamp is not a valid datetime value. - * @return self */ - public function setTs($ts) + public function setTs($ts): static { if ($ts === null) { $this->ts = null; @@ -65,7 +54,7 @@ public function setTs($ts) try { $ts = new DateTime($ts); } catch (Exception $e) { - throw new InvalidArgumentException($e->getMessage()); + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); } } @@ -79,27 +68,20 @@ public function setTs($ts) return $this; } - /** - * @return null|DateTimeInterface - */ - public function ts() + public function ts(): ?\DateTimeInterface { return $this->ts; } /** * @param string|null $ip The IP address. - * @return self */ - public function setIp(?string $ip) + public function setIp(?string $ip): static { $this->ip = $ip; return $this; } - /** - * @return string|null - */ public function ip(): ?string { return $this->ip; diff --git a/packages/email/src/Charcoal/Email/Objects/OpenLog.php b/packages/email/src/Charcoal/Email/Objects/OpenLog.php index 6fca61256..0edc6e3b1 100644 --- a/packages/email/src/Charcoal/Email/Objects/OpenLog.php +++ b/packages/email/src/Charcoal/Email/Objects/OpenLog.php @@ -16,35 +16,22 @@ */ class OpenLog extends AbstractModel { - /** - * @var string|null - */ - private $email; + private ?string $email = null; - /** - * @var DateTimeInterface|null - */ - private $ts; + private ?\DateTimeInterface $ts = null; - /** - * @var string|null - */ - private $ip; + private ?string $ip = null; /** * @param string|null $emailId The email (log) id. - * @return self */ - public function setEmail(?string $emailId) + public function setEmail(?string $emailId): static { $this->email = $emailId; return $this; } - /** - * @return string|null - */ public function email(): ?string { return $this->email; @@ -53,9 +40,8 @@ public function email(): ?string /** * @param null|string|DateTimeInterface $ts The "timestamp" datetime value. * @throws InvalidArgumentException If the timestamp is not a valid datetime value. - * @return self */ - public function setTs($ts) + public function setTs($ts): static { if ($ts === null) { $this->ts = null; @@ -66,7 +52,7 @@ public function setTs($ts) try { $ts = new DateTime($ts); } catch (Exception $e) { - throw new InvalidArgumentException($e->getMessage()); + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); } } @@ -80,27 +66,20 @@ public function setTs($ts) return $this; } - /** - * @return null|DateTimeInterface - */ - public function ts() + public function ts(): ?\DateTimeInterface { return $this->ts; } /** * @param string|null $ip The IP address. - * @return self */ - public function setIp(?string $ip) + public function setIp(?string $ip): static { $this->ip = $ip; return $this; } - /** - * @return string|null - */ public function ip(): ?string { return $this->ip; diff --git a/packages/email/src/Charcoal/Email/Script/ProcessQueueScript.php b/packages/email/src/Charcoal/Email/Script/ProcessQueueScript.php index 086b89d3d..18a195eb4 100644 --- a/packages/email/src/Charcoal/Email/Script/ProcessQueueScript.php +++ b/packages/email/src/Charcoal/Email/Script/ProcessQueueScript.php @@ -27,17 +27,13 @@ class ProcessQueueScript extends AbstractScript implements CronScriptInterface { use CronScriptTrait; - /** - * @var FactoryInterface - */ - private $queueItemFactory; + private ?\Charcoal\Factory\FactoryInterface $queueItemFactory = null; /** * Process all messages currently in queue. * * @param RequestInterface $request A PSR-7 compatible Request instance. * @param ResponseInterface $response A PSR-7 compatible Response instance. - * @return ResponseInterface */ public function run(RequestInterface $request, ResponseInterface $response): ResponseInterface { @@ -67,10 +63,9 @@ public function run(RequestInterface $request, ResponseInterface $response): Res /** * Default script arguments. - * - * @return array */ - public function defaultArguments() + #[\Override] + public function defaultArguments(): array { $arguments = [ 'queue-id' => [ @@ -98,9 +93,7 @@ public function defaultArguments() 'castTo' => 'int', ], ]; - - $arguments = array_merge(parent::defaultArguments(), $arguments); - return $arguments; + return array_merge(parent::defaultArguments(), $arguments); } /** @@ -108,7 +101,7 @@ public function defaultArguments() * * @return EmailQueueManager */ - protected function makeQueueManager() + protected function makeQueueManager(): object { $cli = $this->climate(); @@ -147,8 +140,6 @@ protected function makeQueueManager() /** * Retrieve the class name of the queue manager model. - * - * @return string */ protected function getQueueManagerClass(): string { @@ -162,7 +153,7 @@ protected function getProcessedQueueCallback(): callable { $climate = $this->climate(); - $callback = function ($success, $failures, $skipped) use ($climate): void { + return function ($success, $failures, $skipped) use ($climate): void { if (!empty($success)) { $climate->green()->out(sprintf('%s emails were successfully sent.', count($success))); } @@ -175,14 +166,12 @@ protected function getProcessedQueueCallback(): callable $climate->dim()->out(sprintf('%s emails were skipped.', count($skipped))); } }; - - return $callback; } /** * @param Container $container Pimple DI container. - * @return void */ + #[\Override] protected function setDependencies(Container $container): void { parent::setDependencies($container); @@ -191,7 +180,6 @@ protected function setDependencies(Container $container): void /** * @param FactoryInterface $factory The factory to create queue items. - * @return void */ private function setQueueItemFactory(FactoryInterface $factory): void { diff --git a/packages/email/src/Charcoal/Email/ServiceProvider/EmailServiceProvider.php b/packages/email/src/Charcoal/Email/ServiceProvider/EmailServiceProvider.php index bbafb8c37..2c428bb21 100644 --- a/packages/email/src/Charcoal/Email/ServiceProvider/EmailServiceProvider.php +++ b/packages/email/src/Charcoal/Email/ServiceProvider/EmailServiceProvider.php @@ -33,7 +33,6 @@ class EmailServiceProvider implements ServiceProviderInterface { /** * @param Container $container A pimple container instance. - * @return void */ public function register(Container $container): void { @@ -43,65 +42,54 @@ public function register(Container $container): void */ $container['email/config'] = function (Container $container): EmailConfig { $appConfig = $container['config']; - $emailConfig = new EmailConfig($appConfig['email']); - return $emailConfig; + return new EmailConfig($appConfig['email']); }; /** * @param Container $container Pimple DI container. * @return ViewInterface */ - $container['email/view'] = function (Container $container): ViewInterface { - return $container['view']; - }; + $container['email/view'] = (fn(Container $container): ViewInterface => $container['view']); /** * @param Container $container Pimple DI Container. * @return FactoryInterface */ - $container['email/factory'] = function (Container $container): FactoryInterface { - return new GenericFactory([ - 'map' => [ - 'email' => Email::class - ], - 'base_class' => EmailInterface::class, - 'default_class' => Email::class, - 'arguments' => [[ - 'logger' => $container['logger'], - 'config' => $container['email/config'], - 'view' => $container['email/view'], - 'template_factory' => $container['template/factory'], - 'queue_item_factory' => $container['model/factory'], - 'log_factory' => $container['model/factory'], - 'tracker' => $container['email/tracker'] - ]] - ]); - }; + $container['email/factory'] = (fn(Container $container): FactoryInterface => new GenericFactory([ + 'map' => [ + 'email' => Email::class + ], + 'base_class' => EmailInterface::class, + 'default_class' => Email::class, + 'arguments' => [[ + 'logger' => $container['logger'], + 'config' => $container['email/config'], + 'view' => $container['email/view'], + 'template_factory' => $container['template/factory'], + 'queue_item_factory' => $container['model/factory'], + 'log_factory' => $container['model/factory'], + 'tracker' => $container['email/tracker'] + ]] + ])); /** * @return Parser */ - $container['email/parser'] = function (): Parser { - return new Parser(); - }; + $container['email/parser'] = (fn(): Parser => new Parser()); /** * @param Container $container Pimple DI Container. * @return Tracker */ - $container['email/tracker'] = function (Container $container): Tracker { - return new Tracker( - (string)$container['base-url'], - $container['model/factory'] - ); - }; + $container['email/tracker'] = (fn(Container $container): Tracker => new Tracker( + (string)$container['base-url'], + $container['model/factory'] + )); /** * @param Container $container Pimple DI container. * @return \Charcoal\Email\EmailInterface */ - $container['email'] = $container->factory(function (Container $container): EmailInterface { - return $container['email/factory']->create('email'); - }); + $container['email'] = $container->factory(fn(Container $container): EmailInterface => $container['email/factory']->create('email')); } } diff --git a/packages/email/src/Charcoal/Email/Services/Parser.php b/packages/email/src/Charcoal/Email/Services/Parser.php index 841a4bff7..b18d99d3d 100644 --- a/packages/email/src/Charcoal/Email/Services/Parser.php +++ b/packages/email/src/Charcoal/Email/Services/Parser.php @@ -19,7 +19,6 @@ class Parser /** * @param string|array $email An email value (either a string or an array). * @throws InvalidArgumentException If the email is invalid. - * @return string */ public function parse($email): string { @@ -40,7 +39,6 @@ public function parse($email): string * * @param string $var An email array (containing an "email" key and optionally a "name" key). * @throws InvalidArgumentException If the email is invalid. - * @return array */ public function emailToArray(string $var): array { @@ -56,7 +54,6 @@ public function emailToArray(string $var): array * * @param array $arr An email array (containing an "email" key and optionally a "name" key). * @throws InvalidArgumentException If the email array is invalid. - * @return string */ public function emailFromArray(array $arr): string { diff --git a/packages/email/src/Charcoal/Email/Services/Tracker.php b/packages/email/src/Charcoal/Email/Services/Tracker.php index a1c9c265a..83a329afc 100644 --- a/packages/email/src/Charcoal/Email/Services/Tracker.php +++ b/packages/email/src/Charcoal/Email/Services/Tracker.php @@ -22,30 +22,17 @@ */ class Tracker { - /** - * @var string - */ - private $baseUrl; - - /** - * @var FactoryInterface - */ - private $modelFactory; - /** * @param string $baseUrl Base URL. * @param FactoryInterface $modelFactory Model factory to create link and log objects. */ - public function __construct(string $baseUrl, FactoryInterface $modelFactory) + public function __construct(private readonly string $baseUrl, private readonly FactoryInterface $modelFactory) { - $this->baseUrl = $baseUrl; - $this->modelFactory = $modelFactory; } /** * @param Email $email Email object to update. * @param string $emailLogId Email log ID, to generate image link for. - * @return void */ public function addOpenTrackingImage(Email &$email, string $emailLogId): void { @@ -64,7 +51,6 @@ public function addOpenTrackingImage(Email &$email, string $emailLogId): void /** * @param Email $email Email object to update. * @param string $emailLogId Email log ID, to generate links for. - * @return void */ public function replaceLinksWithTracker(Email &$email, string $emailLogId): void { @@ -83,7 +69,6 @@ public function replaceLinksWithTracker(Email &$email, string $emailLogId): void /** * @param string $emailLogId Email log ID, to track. * @param string|null $ip Client IP address. - * @return void */ public function trackOpen(string $emailLogId, ?string $ip): void { @@ -97,7 +82,6 @@ public function trackOpen(string $emailLogId, ?string $ip): void /** * @param string $linkId Link ID, to track. * @param string|null $ip Client IP address. - * @return void */ public function trackLink(string $linkId, ?string $ip): void { @@ -111,7 +95,6 @@ public function trackLink(string $linkId, ?string $ip): void /** * @param string $emailLogId Email log ID, to create link for. * @param string $url URL to redirect to. - * @return string */ private function createLink(string $emailLogId, string $url): string { diff --git a/packages/email/tests/Charcoal/AbstractTestCase.php b/packages/email/tests/Charcoal/AbstractTestCase.php index 59ba12ea0..80f1772c4 100644 --- a/packages/email/tests/Charcoal/AbstractTestCase.php +++ b/packages/email/tests/Charcoal/AbstractTestCase.php @@ -1,5 +1,7 @@ obj = $this->getMockForTrait('\Charcoal\Email\EmailAwareTrait'); + $this->obj = $this->getMockForTrait(\Charcoal\Email\EmailAwareTrait::class); } - public function getMethod($obj, $name) + public function getMethod($obj, $name): \ReflectionMethod { $class = new ReflectionClass($obj); - $method = $class->getMethod($name); - $method->setAccessible(true); - return $method; + return $class->getMethod($name); } - /** - * @dataProvider emailToArrayProvider - */ - public function testEmailToArray($val, $exp) + #[\PHPUnit\Framework\Attributes\DataProvider('emailToArrayProvider')] + public function testEmailToArray(string $val, array $exp): void { $method = $this->getMethod($this->obj, 'emailToArray'); $res = $method->invokeArgs($this->obj, [$val]); $this->assertEquals($res, $exp); } - public function emailToArrayProvider() + public static function emailToArrayProvider(): array { return [ ['mat@locomotive.ca', ['email'=>'mat@locomotive.ca', 'name'=>'']], diff --git a/packages/email/tests/Charcoal/Email/EmailConfigTest.php b/packages/email/tests/Charcoal/Email/EmailConfigTest.php index e4941625b..a979ef820 100644 --- a/packages/email/tests/Charcoal/Email/EmailConfigTest.php +++ b/packages/email/tests/Charcoal/Email/EmailConfigTest.php @@ -18,7 +18,7 @@ protected function setUp(): void { $this->obj = new EmailConfig(); } - public function testSetData() + public function testSetData(): void { $data = [ 'smtp' => true, @@ -43,7 +43,7 @@ public function testSetData() $this->assertEquals(true, $this->obj->defaultTrackLinksEnabled()); } - public function testSetSmtp() + public function testSetSmtp(): void { $ret = $this->obj->setSmtp(true); $this->assertSame($ret, $this->obj); @@ -53,7 +53,7 @@ public function testSetSmtp() $this->assertFalse($this->obj->smtp()); } - public function testSetSmtpHostname() + public function testSetSmtpHostname(): void { $ret = $this->obj->setSmtpHostname('foobar'); $this->assertSame($ret, $this->obj); @@ -63,7 +63,7 @@ public function testSetSmtpHostname() $this->obj->setSmtpHostname([]); } - public function testSetSmtpPort() + public function testSetSmtpPort(): void { $ret = $this->obj->setSmtpPort(42); $this->assertSame($ret, $this->obj); @@ -73,7 +73,7 @@ public function testSetSmtpPort() $this->obj->setSmtpPort('foo'); } - public function testSetSmtpAuth() + public function testSetSmtpAuth(): void { $ret = $this->obj->setSmtpAuth(true); $this->assertSame($ret, $this->obj); @@ -83,7 +83,7 @@ public function testSetSmtpAuth() $this->assertFalse($this->obj->smtpAuth()); } - public function testSetSmtpUsername() + public function testSetSmtpUsername(): void { $ret = $this->obj->setSmtpUsername('foobar'); $this->assertSame($ret, $this->obj); @@ -93,7 +93,7 @@ public function testSetSmtpUsername() $this->obj->setSmtpUsername([]); } - public function testSetSmtpPassword() + public function testSetSmtpPassword(): void { $ret = $this->obj->setSmtpPassword('foobar'); $this->assertSame($ret, $this->obj); @@ -103,7 +103,7 @@ public function testSetSmtpPassword() $this->obj->setSmtpPassword([]); } - public function testSetDefaultFrom() + public function testSetDefaultFrom(): void { $ret = $this->obj->setDefaultFrom('test@example.com'); $this->assertSame($ret, $this->obj); @@ -119,7 +119,7 @@ public function testSetDefaultFrom() $this->obj->setDefaultFrom(123); } - public function testSetDefaultReplyTo() + public function testSetDefaultReplyTo(): void { $ret = $this->obj->setDefaultReplyTo('test@example.com'); $this->assertSame($ret, $this->obj); @@ -135,21 +135,21 @@ public function testSetDefaultReplyTo() $this->obj->setDefaultReplyTo(123); } - public function testSetDefaultLogEnabled() + public function testSetDefaultLogEnabled(): void { $ret = $this->obj->setDefaultLogEnabled(true); $this->assertSame($ret, $this->obj); $this->assertEquals(true, $this->obj->defaultLogEnabled()); } - public function testSetDefaultTrackOpenEnabled() + public function testSetDefaultTrackOpenEnabled(): void { $ret = $this->obj->setDefaultTrackOpenEnabled(true); $this->assertSame($ret, $this->obj); $this->assertEquals(true, $this->obj->defaultTrackOpenEnabled()); } - public function testSetDefaultTrackLinksEnabled() + public function testSetDefaultTrackLinksEnabled(): void { $ret = $this->obj->setDefaultTrackLinksEnabled(true); $this->assertSame($ret, $this->obj); diff --git a/packages/email/tests/Charcoal/Email/EmailQueueItemTest.php b/packages/email/tests/Charcoal/Email/EmailQueueItemTest.php index 173f407ac..4cb7f42e1 100644 --- a/packages/email/tests/Charcoal/Email/EmailQueueItemTest.php +++ b/packages/email/tests/Charcoal/Email/EmailQueueItemTest.php @@ -16,8 +16,6 @@ class EmailQueueItemTest extends AbstractTestCase /** * Set up the test. - * - * @return void */ protected function setUp(): void { @@ -26,20 +24,15 @@ protected function setUp(): void /** * Create tested class. - * - * @return EmailQueueItem */ - public function createObj() + public function createObj(): \Charcoal\Email\EmailQueueItem { return new EmailQueueItem([ 'logger' => new NullLogger() ]); } - /** - * @return void - */ - public function testConstructor() + public function testConstructor(): void { $this->assertInstanceOf(QueueItemInterface::class, $this->obj); } diff --git a/packages/email/tests/Charcoal/Email/EmailQueueManagerTest.php b/packages/email/tests/Charcoal/Email/EmailQueueManagerTest.php index b0b0324dc..747a28e68 100644 --- a/packages/email/tests/Charcoal/Email/EmailQueueManagerTest.php +++ b/packages/email/tests/Charcoal/Email/EmailQueueManagerTest.php @@ -26,7 +26,7 @@ protected function setUp(): void ]); } - public function testProto() + public function testProto(): void { $ret = $this->obj->queueItemProto(); $this->assertInstanceOf(EmailQueueItem::class, $ret); diff --git a/packages/email/tests/Charcoal/Email/EmailTest.php b/packages/email/tests/Charcoal/Email/EmailTest.php index c03d6f48f..71e4ff39e 100644 --- a/packages/email/tests/Charcoal/Email/EmailTest.php +++ b/packages/email/tests/Charcoal/Email/EmailTest.php @@ -32,7 +32,7 @@ protected function setUp(): void ]); } - public function testSetData() + public function testSetData(): void { $obj = $this->obj; $ret = $obj->setData([ @@ -69,7 +69,7 @@ public function testSetData() $this->assertEquals(true, $obj->trackLinksEnabled()); } - public function testSetCampaign() + public function testSetCampaign(): void { $obj = $this->obj; $ret = $obj->setCampaign('foo'); @@ -77,7 +77,7 @@ public function testSetCampaign() $this->assertEquals('foo', $obj->campaign()); } - public function testGenerateCampaign() + public function testGenerateCampaign(): void { $obj = $this->obj; $ret = $obj->campaign(); @@ -92,7 +92,7 @@ public function testGenerateCampaign() * - Resets the "to" value before setting it, at every call. * - Throws an exception if the to argument is not a string. */ - public function testSetTo() + public function testSetTo(): void { $obj = $this->obj; @@ -123,7 +123,7 @@ public function testSetTo() $obj->setTo(false); } - public function testAddTo() + public function testAddTo(): void { $obj = $this->obj; $ret = $obj->addTo('test@example.com'); @@ -137,7 +137,7 @@ public function testAddTo() $obj->addTo(false); } - public function testSetCc() + public function testSetCc(): void { $obj = $this->obj; @@ -160,7 +160,7 @@ public function testSetCc() $obj->SetCc(false); } - public function testAddCc() + public function testAddCc(): void { $obj = $this->obj; $ret = $obj->addCc('test@example.com'); @@ -174,7 +174,7 @@ public function testAddCc() $obj->addCc(false); } - public function testSetBcc() + public function testSetBcc(): void { $obj = $this->obj; @@ -197,7 +197,7 @@ public function testSetBcc() $obj->setBcc(false); } - public function testAddBcc() + public function testAddBcc(): void { $obj = $this->obj; $ret = $obj->addBcc('test@example.com'); @@ -211,7 +211,7 @@ public function testAddBcc() $obj->addBcc(false); } - public function testSetFrom() + public function testSetFrom(): void { $obj = $this->obj; //$config = $obj->config()->setDefaultFrom('default@example.com'); @@ -231,7 +231,7 @@ public function testSetFrom() $obj->setFrom(false); } - public function testSetReplyTo() + public function testSetReplyTo(): void { $obj = $this->obj; //$config = $obj->config()->setDefaultReplyTo('default@example.com'); @@ -251,7 +251,7 @@ public function testSetReplyTo() $obj->setReplyTo(false); } - public function testSetSubject() + public function testSetSubject(): void { $obj = $this->obj; $ret = $obj->setSubject('foo'); @@ -259,7 +259,7 @@ public function testSetSubject() $this->assertEquals('foo', $obj->subject()); } - public function testSetMsgHtml() + public function testSetMsgHtml(): void { $obj = $this->obj; $ret = $obj->setMsgHtml('foo'); @@ -267,7 +267,7 @@ public function testSetMsgHtml() $this->assertEquals('foo', $obj->msgHtml()); } - public function testSetMsgTxt() + public function testSetMsgTxt(): void { $obj = $this->obj; $ret = $obj->setMsgTxt('foo'); @@ -275,7 +275,7 @@ public function testSetMsgTxt() $this->assertEquals('foo', $obj->msgTxt()); } - public function testConvertHtml() + public function testConvertHtml(): void { $obj = $this->obj; $html = file_get_contents(__DIR__.'/../../data/example.html'); @@ -287,7 +287,7 @@ public function testConvertHtml() $this->assertEquals($txt, $obj->msgTxt()); } - public function testSetAttachments() + public function testSetAttachments(): void { $obj = $this->obj; $ret = $obj->setAttachments(['foo']); @@ -295,7 +295,7 @@ public function testSetAttachments() $this->assertEquals(['foo'], $obj->attachments()); } - public function testSetLogEnabled() + public function testSetLogEnabled(): void { $obj = $this->obj; // $this->config()->setDefaultLogEnabled(false); @@ -309,7 +309,7 @@ public function testSetLogEnabled() $this->assertNotTrue($obj->logEnabled()); } - public function testSetTrackOpenEnabled() + public function testSetTrackOpenEnabled(): void { $obj = $this->obj; // $this->config()->setDefaultTrackOpenEnabled(false); @@ -323,7 +323,7 @@ public function testSetTrackOpenEnabled() $this->assertNotTrue($obj->trackOpenEnabled()); } - public function testSetTrackLinksEnabled() + public function testSetTrackLinksEnabled(): void { $obj = $this->obj; // $this->config()->setDefaultTrackLinksEnabled(false); diff --git a/packages/email/tests/Charcoal/Email/Objects/EmailLogTest.php b/packages/email/tests/Charcoal/Email/Objects/EmailLogTest.php index d1ebb3f9c..80f8445de 100644 --- a/packages/email/tests/Charcoal/Email/Objects/EmailLogTest.php +++ b/packages/email/tests/Charcoal/Email/Objects/EmailLogTest.php @@ -22,7 +22,7 @@ protected function setUp(): void ]); } - public function testSetData() + public function testSetData(): void { $ret = $this->obj->setData([ 'queue_id' => 'foo', @@ -44,7 +44,7 @@ public function testSetData() $this->assertEquals(new DateTime('2010-01-02 03:45:00'), $this->obj->sendTs()); } - public function testKey() + public function testKey(): void { $this->assertEquals('id', $this->obj->key()); } diff --git a/packages/email/tests/Charcoal/Email/Services/TrackerTest.php b/packages/email/tests/Charcoal/Email/Services/TrackerTest.php index 51844cfa1..db1e8e58b 100644 --- a/packages/email/tests/Charcoal/Email/Services/TrackerTest.php +++ b/packages/email/tests/Charcoal/Email/Services/TrackerTest.php @@ -15,15 +15,12 @@ */ class TrackerTest extends TestCase { - /** - * @var Tracker - */ - private $obj; + private \Charcoal\Email\Services\Tracker $obj; /** * @var Email */ - private $email; + private mixed $email; /** * @@ -39,7 +36,7 @@ public function setUp(): void /** * */ - public function testAddOpenTrackingImageWithBody() + public function testAddOpenTrackingImageWithBody(): void { $html = '

Hello

'; $this->email->setMsgHtml($html); @@ -52,7 +49,7 @@ public function testAddOpenTrackingImageWithBody() /** * */ - public function testAddOpenTrackingImageWithoutBody() + public function testAddOpenTrackingImageWithoutBody(): void { $html = '

Hello

'; $this->email->setMsgHtml($html); @@ -64,7 +61,7 @@ public function testAddOpenTrackingImageWithoutBody() /** * */ - public function testReplaceLinksWithTracker() + public function testReplaceLinksWithTracker(): void { $html = ''; $id = uniqid(); diff --git a/packages/email/tests/bootstrap.php b/packages/email/tests/bootstrap.php index 5f58b07cf..57358fc7d 100644 --- a/packages/email/tests/bootstrap.php +++ b/packages/email/tests/bootstrap.php @@ -1,5 +1,7 @@ $instance]` format. * Used with the `get()` method only. - * @var array $instances */ - private $instances = []; + private array $instances = []; /** * @var callable $resolver @@ -70,7 +66,7 @@ abstract class AbstractFactory implements FactoryInterface /** * @param array $data Constructor dependencies. */ - public function __construct(array $data = null) + public function __construct(?array $data = null) { if (isset($data['base_class'])) { $this->setBaseClass($data['base_class']); @@ -89,7 +85,7 @@ public function __construct(array $data = null) } if (!isset($data['resolver'])) { - $opts = isset($data['resolver_options']) ? $data['resolver_options'] : null; + $opts = $data['resolver_options'] ?? null; $data['resolver'] = new GenericResolver($opts); } @@ -118,13 +114,13 @@ public function __construct(array $data = null) * @throws InvalidArgumentException If type argument is not a string or is not an available type. * @return mixed The instance / object */ - final public function create($type, array $args = null, callable $cb = null) + final public function create($type, ?array $args = null, ?callable $cb = null) { if (!is_string($type)) { throw new InvalidArgumentException( sprintf( '%s: Type must be a string.', - get_called_class() + static::class ) ); } @@ -133,7 +129,7 @@ final public function create($type, array $args = null, callable $cb = null) $args = $this->arguments(); } - $pool = get_called_class(); + $pool = static::class; if (isset($this->resolved[$pool][$type])) { $className = $this->resolved[$pool][$type]; } else { @@ -147,7 +143,7 @@ final public function create($type, array $args = null, callable $cb = null) throw new InvalidArgumentException( sprintf( '%1$s: Type "%2$s" is not a valid type. (Using default class "%3$s")', - get_called_class(), + static::class, $type, $defaultClass ) @@ -168,7 +164,7 @@ final public function create($type, array $args = null, callable $cb = null) throw new Exception( sprintf( '%1$s: Class "%2$s" must be an instance of "%3$s"', - get_called_class(), + static::class, $className, $baseClass ) @@ -192,7 +188,7 @@ final public function create($type, array $args = null, callable $cb = null) * @throws InvalidArgumentException If type argument is not a string. * @return mixed The instance / object */ - final public function get($type, array $args = null) + final public function get($type, ?array $args = null) { if (!is_string($type)) { throw new InvalidArgumentException( @@ -216,7 +212,7 @@ final public function get($type, array $args = null) */ public function setBaseClass($type) { - if (!is_string($type) || empty($type)) { + if (!is_string($type) || ($type === '' || $type === '0')) { throw new InvalidArgumentException( 'Class name or type must be a non-empty string.' ); @@ -259,7 +255,7 @@ public function baseClass() */ public function setDefaultClass($type) { - if (!is_string($type) || empty($type)) { + if (!is_string($type) || ($type === '' || $type === '0')) { throw new InvalidArgumentException( 'Class name or type must be a non-empty string.' ); @@ -351,8 +347,7 @@ public function resolve($type) } $resolver = $this->resolver(); - $resolved = $resolver($type); - return $resolved; + return $resolver($type); } /** @@ -381,11 +376,7 @@ public function isResolvable($type) $resolver = $this->resolver(); $resolved = $resolver($type); - if (class_exists($resolved)) { - return true; - } - - return false; + return class_exists($resolved); } @@ -411,7 +402,7 @@ protected function createClass($className, $args) if (!is_array($args)) { return new $className($args); } - if (count(array_filter(array_keys($args), 'is_string')) > 0) { + if (count(array_filter(array_keys($args), is_string(...))) > 0) { return new $className($args); } else { /** @@ -463,9 +454,8 @@ protected function addClassToMap($type, $className) /** * @param callable $resolver The class resolver instance to use. - * @return self */ - private function setResolver(callable $resolver) + private function setResolver(callable $resolver): static { $this->resolver = $resolver; return $this; @@ -475,9 +465,8 @@ private function setResolver(callable $resolver) * Add multiple types, in a an array of `type` => `className`. * * @param string[] $map The map (key=>classname) to use. - * @return self */ - private function setMap(array $map) + private function setMap(array $map): static { // Resets (overwrites) map. $this->map = []; @@ -492,9 +481,8 @@ private function setMap(array $map) * * @param mixed $obj The object to pass to callback(s). * @param callable $customCallback An optional additional custom callback. - * @return void */ - private function runCallbacks(&$obj, callable $customCallback = null) + private function runCallbacks(&$obj, ?callable $customCallback = null): void { $factoryCallback = $this->callback(); if (isset($factoryCallback)) { diff --git a/packages/factory/src/Charcoal/Factory/FactoryInterface.php b/packages/factory/src/Charcoal/Factory/FactoryInterface.php index 04e218bd4..f56fb5432 100644 --- a/packages/factory/src/Charcoal/Factory/FactoryInterface.php +++ b/packages/factory/src/Charcoal/Factory/FactoryInterface.php @@ -1,5 +1,7 @@ resolve($type); } @@ -80,7 +80,7 @@ public function __invoke($type) * @throws InvalidArgumentException If the type parameter is not a string. * @return string The resolved class name (FQN). */ - public function resolve($type) + public function resolve($type): string { if (!is_string($type)) { throw new InvalidArgumentException( @@ -91,7 +91,7 @@ public function resolve($type) // Normalize requested type with prefix / suffix, if applicable. $type = $this->prefix . $type . $this->suffix; - $capitalizeNext = function (&$i) { + $capitalizeNext = function (&$i): void { $i = ucfirst($i); }; @@ -107,8 +107,6 @@ public function resolve($type) $type = str_replace($rep, $target, $type); } - $class = '\\' . trim($type, '\\'); - - return $class; + return '\\' . trim($type, '\\'); } } diff --git a/packages/factory/src/Charcoal/Factory/MapFactory.php b/packages/factory/src/Charcoal/Factory/MapFactory.php index 601043fbe..5608570b8 100644 --- a/packages/factory/src/Charcoal/Factory/MapFactory.php +++ b/packages/factory/src/Charcoal/Factory/MapFactory.php @@ -1,5 +1,7 @@ resolverPrefix; } @@ -95,7 +80,7 @@ public function resolverPrefix() * @throws InvalidArgumentException If the suffix argument is not a string. * @return ResolverFactory Chainable */ - public function setResolverSuffix($suffix) + public function setResolverSuffix($suffix): static { if (!is_string($suffix)) { throw new InvalidArgumentException( @@ -106,10 +91,7 @@ public function setResolverSuffix($suffix) return $this; } - /** - * @return string - */ - public function resolverSuffix() + public function resolverSuffix(): string { return $this->resolverSuffix; } @@ -118,16 +100,13 @@ public function resolverSuffix() * @param array $capitals The array of letter to "calitalize-next" (uppercase next letter in the string). * @return ResolverFactory Chainable */ - public function setResolverCapitals(array $capitals) + public function setResolverCapitals(array $capitals): static { $this->resolverCapitals = $capitals; return $this; } - /** - * @return array - */ - public function resolverCapitals() + public function resolverCapitals(): array { return $this->resolverCapitals; } @@ -136,16 +115,13 @@ public function resolverCapitals() * @param array $replacements The array (key=>value) of replacements. * @return ResolverFactory Chainable */ - public function setResolverReplacements(array $replacements) + public function setResolverReplacements(array $replacements): static { $this->resolverReplacements = $replacements; return $this; } - /** - * @return array - */ - public function resolverReplacements() + public function resolverReplacements(): array { return $this->resolverReplacements; } @@ -157,7 +133,8 @@ public function resolverReplacements() * @throws InvalidArgumentException If the type parameter is not a string. * @return string The resolved class name (FQN). */ - public function resolve($type) + #[\Override] + public function resolve($type): string { if (!is_string($type)) { throw new InvalidArgumentException( @@ -165,7 +142,7 @@ public function resolve($type) ); } - $capitalize_next = function (&$i) { + $capitalize_next = function (&$i): void { $i = ucfirst($i); }; @@ -192,9 +169,9 @@ public function resolve($type) /** * @param string $type The "type" of object to resolve (the object ident). * @throws InvalidArgumentException If the type parameter is not a string. - * @return boolean */ - public function isResolvable($type) + #[\Override] + public function isResolvable($type): bool { if (!is_string($type)) { throw new InvalidArgumentException( diff --git a/packages/factory/tests/Charcoal/AbstractTestCase.php b/packages/factory/tests/Charcoal/AbstractTestCase.php index 59ba12ea0..80f1772c4 100644 --- a/packages/factory/tests/Charcoal/AbstractTestCase.php +++ b/packages/factory/tests/Charcoal/AbstractTestCase.php @@ -1,5 +1,7 @@ obj = $this->getMockForAbstractClass(AbstractFactory::class); } - /** - * @return void - */ - public function testConstructorBaseClassAndDefaultClass() + public function testConstructorBaseClassAndDefaultClass(): void { $obj = $this->getMockForAbstractClass(AbstractFactory::class, [[ 'base_class' => DateTimeInterface::class, @@ -40,10 +34,7 @@ public function testConstructorBaseClassAndDefaultClass() $this->assertEquals(DateTime::class, $obj->defaultClass()); } - /** - * @return void - */ - public function testConstructorArguments() + public function testConstructorArguments(): void { $obj = $this->getMockForAbstractClass(AbstractFactory::class, [[ 'arguments' => ['2018-01-01 15:30:00'] @@ -52,10 +43,7 @@ public function testConstructorArguments() $this->assertEquals('2018-01-01 15:30:00', $ret->format('Y-m-d H:i:s')); } - /** - * @return void - */ - public function testConstructorMap() + public function testConstructorMap(): void { $obj = $this->getMockForAbstractClass(AbstractFactory::class, [[ 'map' => [ @@ -67,15 +55,12 @@ public function testConstructorMap() $this->assertInstanceOf(DateTime::class, $ret); $this->expectException(InvalidArgumentException::class); - $obj2 = $this->getMockForAbstractClass(AbstractFactory::class, [[ + $this->getMockForAbstractClass(AbstractFactory::class, [[ 'map' => [DateTime::class] ]]); } - /** - * @return void - */ - public function testConstructorCallback() + public function testConstructorCallback(): void { $obj = $this->getMockForAbstractClass(AbstractFactory::class, [[ 'callback' => function ($obj) { @@ -97,10 +82,8 @@ public function testConstructorCallback() * - Is chainable * - Properly sets the baseClass value. * - Throws an exception if the parameter is not a valid (existing) class - * - * @return void */ - public function testSetBaseClass() + public function testSetBaseClass(): void { $obj = $this->obj; $this->assertSame('', $obj->baseClass()); @@ -113,10 +96,7 @@ public function testSetBaseClass() $obj->setBaseClass('foobar'); } - /** - * @return void - */ - public function testSetBaseClassNotAString() + public function testSetBaseClassNotAString(): void { $this->expectException(InvalidArgumentException::class); $this->obj->setBaseClass(false); @@ -132,10 +112,8 @@ public function testSetBaseClassNotAString() * - Throws an exception if the parameter is not a valid (existing) class * Also asserts that subsequent call to `create()`: * - Create an instance of the default class if an invalid parameters is sent. - * - * @return void */ - public function testSetDefaultClass() + public function testSetDefaultClass(): void { $this->assertSame('', $this->obj->defaultClass()); @@ -150,10 +128,7 @@ public function testSetDefaultClass() $this->obj->setDefaultClass('foobar'); } - /** - * @return void - */ - public function testSetDefaultClassNotAString() + public function testSetDefaultClassNotAString(): void { $this->expectException(InvalidArgumentException::class); $this->obj->setDefaultClass(false); @@ -164,10 +139,8 @@ public function testSetDefaultClassNotAString() * Asserts that the create method: * - Creates an object of the given class. * - Returns a new object on every call. - * - * @return void */ - public function testCreate() + public function testCreate(): void { $ret = $this->obj->create(DateTime::class); $this->assertInstanceOf(DateTime::class, $ret); @@ -176,10 +149,7 @@ public function testCreate() $this->assertNotSame($ret, $ret2); } - /** - * @return void - */ - public function testCreateInvalidArgumentException() + public function testCreateInvalidArgumentException(): void { $this->expectException(InvalidArgumentException::class); $this->obj->create(false); @@ -189,10 +159,8 @@ public function testCreateInvalidArgumentException() * Asserts that the get method: * - Returns an object of the given class. * - Returns the exact same object if called multiple times. - * - * @return void */ - public function testGet() + public function testGet(): void { $ret = $this->obj->get(DateTime::class); $this->assertInstanceOf(DateTime::class, $ret); @@ -201,19 +169,13 @@ public function testGet() $this->assertSame($ret, $ret2); } - /** - * @return void - */ - public function testGetInvalidArgumentException() + public function testGetInvalidArgumentException(): void { $this->expectException(InvalidArgumentException::class); $this->obj->get(false); } - /** - * @return void - */ - public function testDefaultResolver() + public function testDefaultResolver(): void { $ret = $this->obj->create('date-time'); $this->assertInstanceOf(DateTime::class, $ret); diff --git a/packages/factory/tests/Charcoal/Factory/GenericFactoryTest.php b/packages/factory/tests/Charcoal/Factory/GenericFactoryTest.php index b7363df61..ab583f28e 100644 --- a/packages/factory/tests/Charcoal/Factory/GenericFactoryTest.php +++ b/packages/factory/tests/Charcoal/Factory/GenericFactoryTest.php @@ -15,18 +15,12 @@ class GenericFactoryTest extends AbstractTestCase */ public $obj; - /** - * @return void - */ protected function setUp(): void { $this->obj = new GenericFactory(); } - /** - * @return void - */ - public function testIsResolvable() + public function testIsResolvable(): void { $this->assertTrue($this->obj->isResolvable('DateTime')); $this->assertFalse($this->obj->isResolvable('foobaz')); @@ -35,36 +29,28 @@ public function testIsResolvable() $this->obj->isResolvable(false); } - /** - * @return void - */ - public function testCreate() + public function testCreate(): void { $ret = $this->obj->create('\DateTime'); $this->assertInstanceOf('\DateTime', $ret); $this->expectException(\Exception::class); - $ret2 = $this->obj->create('foobar'); + $this->obj->create('foobar'); } /** * Asserts that the AbstractFactory's `create()` method, as GenericFactory: * - Returns the default class when passing an invalid argument, if set * - Throws an exception when passing an invalid argument, if no default class is set - * - * @return void */ - public function testCreateDefaultClass() + public function testCreateDefaultClass(): void { $this->obj->setDefaultClass('\DateTime'); $ret = $this->obj->create('foobar'); $this->assertInstanceOf('\DateTime', $ret); } - /** - * @return void - */ - public function testCreateCreatesNewInstance() + public function testCreateCreatesNewInstance(): void { $ret1 = $this->obj->create('\DateTime'); $ret2 = $this->obj->create('\DateTime'); @@ -72,20 +58,14 @@ public function testCreateCreatesNewInstance() $this->assertNotSame($ret1, $ret2); } - /** - * @return void - */ - public function testCreateCallback() + public function testCreateCallback(): void { - $ret = $this->obj->create('\DateTime', null, function($obj) { + $this->obj->create('\DateTime', null, function($obj): void { $this->assertInstanceOf('\DateTime', $obj); }); } - /** - * @return void - */ - public function testGetReturnsSameInstance() + public function testGetReturnsSameInstance(): void { $ret1 = $this->obj->get('\DateTime'); $ret2 = $this->obj->get('\DateTime'); @@ -93,17 +73,14 @@ public function testGetReturnsSameInstance() $this->assertSame($ret1, $ret2); } - /** - * @return void - */ - public function testCreateBaseClass() + public function testCreateBaseClass(): void { $this->obj->setBaseClass('\DateTimeInterface'); $ret = $this->obj->create('\DateTime'); $this->assertInstanceOf('\DateTime', $ret); $this->expectException(\Exception::class); - $this->obj->setBaseClass('\Charcoal\Factory\FactoryInterface'); + $this->obj->setBaseClass(\Charcoal\Factory\FactoryInterface::class); $this->obj->create('\DateTime'); } } diff --git a/packages/factory/tests/Charcoal/Factory/ResolverFactoryTest.php b/packages/factory/tests/Charcoal/Factory/ResolverFactoryTest.php index 76b46872c..52ed108a6 100644 --- a/packages/factory/tests/Charcoal/Factory/ResolverFactoryTest.php +++ b/packages/factory/tests/Charcoal/Factory/ResolverFactoryTest.php @@ -15,18 +15,12 @@ class ResolverFactoryTest extends AbstractTestCase */ public $obj; - /** - * @return void - */ protected function setUp(): void { $this->obj = new ResolverFactory(); } - /** - * @return void - */ - public function testSetResolverPrefix() + public function testSetResolverPrefix(): void { $this->assertEquals('', $this->obj->resolverPrefix()); $ret = $this->obj->setResolverPrefix('foo'); @@ -37,10 +31,7 @@ public function testSetResolverPrefix() $this->obj->setResolverPrefix(false); } - /** - * @return void - */ - public function testSetResolverSuffix() + public function testSetResolverSuffix(): void { $this->assertEquals('', $this->obj->resolverSuffix()); $ret = $this->obj->setResolverSuffix('foo'); @@ -51,10 +42,7 @@ public function testSetResolverSuffix() $this->obj->setResolverSuffix(false); } - /** - * @return void - */ - public function testSetResolverCapitals() + public function testSetResolverCapitals(): void { $ret = $this->obj->setResolverCapitals(['$']); $this->assertSame($ret, $this->obj); @@ -63,10 +51,7 @@ public function testSetResolverCapitals() $this->assertEquals('\$Abc$De', $this->obj->resolve('$abc$de')); } - /** - * @return void - */ - public function testSetResoverReplacements() + public function testSetResoverReplacements(): void { $ret = $this->obj->setResolverReplacements(['$'=>'_']); $this->assertSame($ret, $this->obj); @@ -76,13 +61,12 @@ public function testSetResoverReplacements() } /** - * @dataProvider providerResolve * * @param string $type Factory key. * @param string $classname Factory class name. - * @return void */ - public function testResolve($type, $classname) + #[\PHPUnit\Framework\Attributes\DataProvider('providerResolve')] + public function testResolve(string $type, string $classname): void { $this->assertEquals($classname, $this->obj->resolve($type)); @@ -91,19 +75,13 @@ public function testResolve($type, $classname) $this->assertEquals($classname.'Test', $this->obj->resolve($type)); } - /** - * @return void - */ - public function testResolveWithoutStringThrowsException() + public function testResolveWithoutStringThrowsException(): void { $this->expectException(\InvalidArgumentException::class); $this->obj->resolve(false); } - /** - * @return void - */ - public function testIsResolvable() + public function testIsResolvable(): void { $this->assertFalse($this->obj->isResolvable('foo')); $this->assertTrue($this->obj->isResolvable('charcoal/factory/map-factory')); @@ -112,19 +90,13 @@ public function testIsResolvable() $this->obj->isResolvable(false); } - /** - * @return void - */ - public function testCreate() + public function testCreate(): void { $ret = $this->obj->create('charcoal/factory/map-factory'); - $this->assertInstanceOf('\Charcoal\Factory\MapFactory', $ret); + $this->assertInstanceOf(\Charcoal\Factory\MapFactory::class, $ret); } - /** - * @return array - */ - public function providerResolve() + public static function providerResolve(): array { return [ ['foo', '\Foo'], @@ -133,7 +105,7 @@ public function providerResolve() ['foo-bar', '\FooBar'], ['foo.bar', '\Foo_Bar'], ['foo.bar\baz_baz-baz/foo\\', '\Foo_Bar\Baz_BazBaz\Foo'], - ['charcoal/factory/map-factory', '\Charcoal\Factory\MapFactory'] + ['charcoal/factory/map-factory', \Charcoal\Factory\MapFactory::class] ]; } } diff --git a/packages/image/composer.json b/packages/image/composer.json index 7e5c83bf0..1b8a2a806 100644 --- a/packages/image/composer.json +++ b/packages/image/composer.json @@ -1,7 +1,15 @@ { "name": "charcoal/image", "description": "PHP Image manipulation library", - "keywords": ["php", "image", "imagick", "gd", "imagemagick", "charcoal", "locomotive"], + "keywords": [ + "php", + "image", + "imagick", + "gd", + "imagemagick", + "charcoal", + "locomotive" + ], "license": "MIT", "authors": [ { @@ -9,24 +17,15 @@ "email": "mat@locomotive.ca" } ], - "extra": { - "branch-alias": { - "dev-main": "5.x-dev" - } - }, "require": { - "php": "^7.4 || ^8.0", + "php": "^8.3", "charcoal/factory": "^5.1" }, "require-dev": { "php-coveralls/php-coveralls": "^2.2", - "phpunit/phpunit": "^9.5", + "phpunit/phpunit": "^12.5", "squizlabs/php_codesniffer": "^3.5" }, - "suggest": { - "ext-imagick": "To use the imagick driver.", - "ext-SimpleXML": "To parse sprite (SVG) property." - }, "autoload": { "psr-4": { "Charcoal\\": "src/Charcoal" @@ -37,8 +36,10 @@ "Charcoal\\Tests\\": "tests/Charcoal" } }, - "replace": { - "locomotivemtl/charcoal-image": "*" + "extra": { + "branch-alias": { + "dev-main": "5.x-dev" + } }, "scripts": { "test": [ @@ -54,6 +55,13 @@ "phpcs": "php vendor/bin/phpcs -ps --colors src/", "phpcbf": "php vendor/bin/phpcbf -ps --colors src/ tests/" }, + "suggest": { + "ext-imagick": "To use the imagick driver.", + "ext-SimpleXML": "To parse sprite (SVG) property." + }, + "replace": { + "locomotivemtl/charcoal-image": "*" + }, "minimum-stability": "dev", "prefer-stable": true } diff --git a/packages/image/src/Charcoal/Image/AbstractEffect.php b/packages/image/src/Charcoal/Image/AbstractEffect.php index 23a0bf7f8..3865ef17e 100644 --- a/packages/image/src/Charcoal/Image/AbstractEffect.php +++ b/packages/image/src/Charcoal/Image/AbstractEffect.php @@ -12,10 +12,7 @@ */ abstract class AbstractEffect implements EffectInterface { - /** - * @var ImageInterface $image - */ - private $image; + private ?\Charcoal\Image\ImageInterface $image = null; /** * @param ImageInterface $image The parent image. @@ -33,7 +30,7 @@ public function setImage(ImageInterface $image) */ public function image() { - if ($this->image === null) { + if (!$this->image instanceof \Charcoal\Image\ImageInterface) { throw new Exception( 'Can not get effect\'s image: Trying to access an unset image' ); @@ -60,7 +57,7 @@ public function setData(array $data) * @param array $data Optional effect data. If null, use the currently set properties. * @return AbstractEffect Chainable */ - abstract public function process(array $data = null); + abstract public function process(?array $data = null); /** * Allow an object to define how the key setter are called. @@ -68,7 +65,7 @@ abstract public function process(array $data = null); * @param string $key The key to get the setter from. * @return string The setter method name, for a given key. */ - protected function setter($key) + protected function setter(string $key) { $setter = 'set_' . $key; return $this->camelize($setter); @@ -82,6 +79,6 @@ protected function setter($key) */ protected function camelize($str) { - return lcfirst(implode('', array_map('ucfirst', explode('_', $str)))); + return lcfirst(implode('', array_map(ucfirst(...), explode('_', $str)))); } } diff --git a/packages/image/src/Charcoal/Image/AbstractImage.php b/packages/image/src/Charcoal/Image/AbstractImage.php index 3e61ce276..7298b836e 100644 --- a/packages/image/src/Charcoal/Image/AbstractImage.php +++ b/packages/image/src/Charcoal/Image/AbstractImage.php @@ -28,10 +28,7 @@ abstract class AbstractImage implements ImageInterface */ protected $effects = []; - /** - * @var EffectFactory $effectFactory - */ - private $effectFactory; + private ?\Charcoal\Image\EffectFactory $effectFactory = null; /** @@ -43,7 +40,7 @@ abstract class AbstractImage implements ImageInterface * @param array $data The effect options. * @return ImageInterface Chainable */ - public function __call($fxType, array $data) + public function __call(string $fxType, array $data) { $data['type'] = $fxType; @@ -61,7 +58,7 @@ public function __call($fxType, array $data) */ protected function effectFactory() { - if ($this->effectFactory === null) { + if (!$this->effectFactory instanceof \Charcoal\Image\EffectFactory) { $this->effectFactory = new EffectFactory(); } return $this->effectFactory; @@ -175,7 +172,7 @@ public function addEffect($effect) * @param array $effects Optional. The effects to process. If null, use in-memory's. * @return ImageInterface Chainable */ - public function process(array $effects = null) + public function process(?array $effects = null) { if ($effects !== null) { $this->setEffects($effects); @@ -255,9 +252,7 @@ public function ratio() 'Ratio can not be calculated. Invalid image dimensions' ); } - - $ratio = ($width / $height); - return $ratio; + return $width / $height; } /** @@ -320,7 +315,7 @@ protected function createEffect($effect) ); } $fxType = $effect['type']; - if (strstr($fxType, '/') === false) { + if (!str_contains((string) $fxType, '/')) { // Core effects do not need to be namespaced $driver = $this->driverType(); $fxType = 'charcoal/image/' . $driver . '/effect/' . $driver . '-' . $fxType . '-effect'; diff --git a/packages/image/src/Charcoal/Image/Effect/AbstractAutoorientationEffect.php b/packages/image/src/Charcoal/Image/Effect/AbstractAutoorientationEffect.php index 0b1e716e7..cde5801c4 100644 --- a/packages/image/src/Charcoal/Image/Effect/AbstractAutoorientationEffect.php +++ b/packages/image/src/Charcoal/Image/Effect/AbstractAutoorientationEffect.php @@ -1,5 +1,7 @@ setData($data); } $mode = $this->mode(); - switch ($mode) { - case 'adaptive': - return $this->processAdaptive(); - - case 'gaussian': - return $this->processGaussian(); - - case 'motion': - return $this->processMotion(); - - case 'radial': - return $this->processRadial(); - - case 'soft': - return $this->processSoft(); - - case 'standard': - default: - return $this->processStandard(); - } + return match ($mode) { + 'adaptive' => $this->processAdaptive(), + 'gaussian' => $this->processGaussian(), + 'motion' => $this->processMotion(), + 'radial' => $this->processRadial(), + 'soft' => $this->processSoft(), + default => $this->processStandard(), + }; } /** diff --git a/packages/image/src/Charcoal/Image/Effect/AbstractCompressionEffect.php b/packages/image/src/Charcoal/Image/Effect/AbstractCompressionEffect.php index b9cf71f5d..bc7070cc0 100644 --- a/packages/image/src/Charcoal/Image/Effect/AbstractCompressionEffect.php +++ b/packages/image/src/Charcoal/Image/Effect/AbstractCompressionEffect.php @@ -1,5 +1,7 @@ repage = !!$repage; + $this->repage = (bool) $repage; return $this; } @@ -220,7 +202,7 @@ public function repage() * @param array $data The effect data. * @return AbstractCropEffect */ - public function process(array $data = null) + public function process(?array $data = null) { if ($data !== null) { $this->setData($data); diff --git a/packages/image/src/Charcoal/Image/Effect/AbstractDitherEffect.php b/packages/image/src/Charcoal/Image/Effect/AbstractDitherEffect.php index 3ef49b447..e38bdffde 100644 --- a/packages/image/src/Charcoal/Image/Effect/AbstractDitherEffect.php +++ b/packages/image/src/Charcoal/Image/Effect/AbstractDitherEffect.php @@ -1,5 +1,7 @@ height = (int)$height; + $this->height = $height; return $this; } @@ -329,7 +299,7 @@ public function backgroundColor() */ public function setAdaptive($adaptive) { - $this->adaptive = !!$adaptive; + $this->adaptive = (bool) $adaptive; return $this; } @@ -355,12 +325,10 @@ public function autoMode() return 'width'; } elseif ($height > 0) { return 'height'; + } elseif ($this->minWidth() || $this->minHeight() || $this->maxWidth() || $this->maxHeight()) { + return 'constraints'; } else { - if ($this->minWidth() || $this->minHeight() || $this->maxWidth() || $this->maxHeight()) { - return 'constraints'; - } else { - return 'none'; - } + return 'none'; } } @@ -369,7 +337,7 @@ public function autoMode() * @throws Exception If the effect data is invalid for its resize mode. * @return self */ - public function process(array $data = null) + public function process(?array $data = null) { if ($data !== null) { $this->setData($data); diff --git a/packages/image/src/Charcoal/Image/Effect/AbstractRotateEffect.php b/packages/image/src/Charcoal/Image/Effect/AbstractRotateEffect.php index 708eccda2..64cf29822 100644 --- a/packages/image/src/Charcoal/Image/Effect/AbstractRotateEffect.php +++ b/packages/image/src/Charcoal/Image/Effect/AbstractRotateEffect.php @@ -1,5 +1,7 @@ setData($data); } $mode = $this->mode(); - switch ($mode) { - case 'adaptive': - return $this->processAdaptive(); - - case 'unsharp': - return $this->processUnsharp(); - - case 'standard': - default: - return $this->processStandard(); - } + return match ($mode) { + 'adaptive' => $this->processAdaptive(), + 'unsharp' => $this->processUnsharp(), + default => $this->processStandard(), + }; } /** diff --git a/packages/image/src/Charcoal/Image/Effect/AbstractThresholdEffect.php b/packages/image/src/Charcoal/Image/Effect/AbstractThresholdEffect.php index 586ccbd88..177d26a0f 100644 --- a/packages/image/src/Charcoal/Image/Effect/AbstractThresholdEffect.php +++ b/packages/image/src/Charcoal/Image/Effect/AbstractThresholdEffect.php @@ -1,5 +1,7 @@ midtone = !!$midtone; + $this->midtone = (bool) $midtone; return $this; } diff --git a/packages/image/src/Charcoal/Image/Effect/AbstractWatermarkEffect.php b/packages/image/src/Charcoal/Image/Effect/AbstractWatermarkEffect.php index 30828bc77..69cc2f862 100644 --- a/packages/image/src/Charcoal/Image/Effect/AbstractWatermarkEffect.php +++ b/packages/image/src/Charcoal/Image/Effect/AbstractWatermarkEffect.php @@ -1,5 +1,7 @@ defaultMap(), $data['map']); - } else { - $data['map'] = $this->defaultMap(); - } + $data['map'] = isset($data['map']) ? array_merge($this->defaultMap(), $data['map']) : $this->defaultMap(); parent::__construct($data); } - /** - * @return array - */ - protected function defaultMap() + protected function defaultMap(): array { return [ - 'imagick' => '\Charcoal\Image\Imagick\ImagickImage', - 'imagemagick' => '\Charcoal\Image\Imagemagick\ImagemagickImage' + 'imagick' => \Charcoal\Image\Imagick\ImagickImage::class, + 'imagemagick' => \Charcoal\Image\Imagemagick\ImagemagickImage::class ]; } } diff --git a/packages/image/src/Charcoal/Image/ImageInterface.php b/packages/image/src/Charcoal/Image/ImageInterface.php index dc080978d..79dfd0dc7 100644 --- a/packages/image/src/Charcoal/Image/ImageInterface.php +++ b/packages/image/src/Charcoal/Image/ImageInterface.php @@ -1,5 +1,7 @@ setData($data); diff --git a/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickBlurEffect.php b/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickBlurEffect.php index b3b6af59d..5b3ab5e21 100644 --- a/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickBlurEffect.php +++ b/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickBlurEffect.php @@ -9,10 +9,7 @@ */ class ImagemagickBlurEffect extends AbstractBlurEffect { - /** - * @return self - */ - public function processAdaptive() + public function processAdaptive(): static { $channel = $this->image()->convertChannel($this->channel()); $cmd = '-channel ' . $channel . ' -adaptive-blur ' . $this->radius() . 'x' . $this->sigma(); @@ -20,10 +17,7 @@ public function processAdaptive() return $this; } - /** - * @return self - */ - public function processGaussian() + public function processGaussian(): static { $channel = $this->image()->convertChannel($this->channel()); $cmd = '-channel ' . $channel . ' -gaussian-blur ' . $this->radius() . 'x' . $this->sigma(); @@ -31,10 +25,7 @@ public function processGaussian() return $this; } - /** - * @return self - */ - public function processMotion() + public function processMotion(): static { $channel = $this->image()->convertChannel($this->channel()); $cmd = '-channel ' . $channel . ' -motion-blur ' . $this->radius() . 'x' . $this->sigma() . '+' . $this->angle(); @@ -42,10 +33,7 @@ public function processMotion() return $this; } - /** - * @return self - */ - public function processRadial() + public function processRadial(): static { $channel = $this->image()->convertChannel($this->channel()); $cmd = '-channel ' . $channel . ' -rotational-blur ' . $this->angle(); @@ -53,20 +41,14 @@ public function processRadial() return $this; } - /** - * @return self - */ - public function processSoft() + public function processSoft(): static { $cmd = '-define convolve:scale=60,40% -morphology Convolve \'Gaussian:' . $this->radius() . 'x' . $this->sigma() . '\''; $this->image()->applyCmd($cmd); return $this; } - /** - * @return self - */ - public function processStandard() + public function processStandard(): static { $channel = $this->image()->convertChannel($this->channel()); $cmd = '-channel ' . $channel . ' -blur ' . $this->radius() . 'x' . $this->sigma(); diff --git a/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickCompressionEffect.php b/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickCompressionEffect.php index d439f3221..f53d9efe6 100644 --- a/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickCompressionEffect.php +++ b/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickCompressionEffect.php @@ -11,9 +11,8 @@ class ImagemagickCompressionEffect extends AbstractCompressionEffect { /** * @param array $data The effect data, if available. - * @return self */ - public function process(array $data = null) + public function process(?array $data = null): static { if ($data !== null) { $this->setData($data); diff --git a/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickDitherEffect.php b/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickDitherEffect.php index 22875805b..639097ff5 100644 --- a/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickDitherEffect.php +++ b/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickDitherEffect.php @@ -1,5 +1,7 @@ setData($data); diff --git a/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickFormatEffect.php b/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickFormatEffect.php index 06bdc75c1..dc6daf5c4 100644 --- a/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickFormatEffect.php +++ b/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickFormatEffect.php @@ -1,5 +1,7 @@ setData($data); diff --git a/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickGrayscaleEffect.php b/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickGrayscaleEffect.php index 06b8e7725..bb610be0b 100644 --- a/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickGrayscaleEffect.php +++ b/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickGrayscaleEffect.php @@ -13,7 +13,7 @@ class ImagemagickGrayscaleEffect extends AbstractGrayscaleEffect * @param array $data The effect data, if available. * @return self */ - public function process(array $data = null) + public function process(?array $data = null) { if ($data !== null) { $this->setData($data); diff --git a/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickMaskEffect.php b/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickMaskEffect.php index fba4e6138..70e965306 100644 --- a/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickMaskEffect.php +++ b/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickMaskEffect.php @@ -1,5 +1,7 @@ setData($data); diff --git a/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickMirrorEffect.php b/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickMirrorEffect.php index c56aa1b51..11799efb0 100644 --- a/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickMirrorEffect.php +++ b/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickMirrorEffect.php @@ -11,20 +11,15 @@ class ImagemagickMirrorEffect extends AbstractMirrorEffect { /** * @param array $data The effect data, if available. - * @return self */ - public function process(array $data = null) + public function process(?array $data = null): static { if ($data !== null) { $this->setData($data); } $axis = $this->axis(); - if ($axis == 'x') { - $cmd = '-flip'; - } else { - $cmd = '-flop'; - } + $cmd = $axis == 'x' ? '-flip' : '-flop'; $this->image()->applyCmd($cmd); return $this; } diff --git a/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickModulateEffect.php b/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickModulateEffect.php index 43c3f7318..7068da2b8 100644 --- a/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickModulateEffect.php +++ b/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickModulateEffect.php @@ -11,9 +11,8 @@ class ImagemagickModulateEffect extends AbstractModulateEffect { /** * @param array $data The effect data, if available. - * @return self */ - public function process(array $data = null) + public function process(?array $data = null): static { if ($data !== null) { $this->setData($data); diff --git a/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickResizeEffect.php b/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickResizeEffect.php index f4e325329..ed12b3cae 100644 --- a/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickResizeEffect.php +++ b/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickResizeEffect.php @@ -20,11 +20,7 @@ class ImagemagickResizeEffect extends AbstractResizeEffect */ protected function doResize($width, $height, $bestFit = false) { - if ($this->adaptive()) { - $option = '-adaptive-resize'; - } else { - $option = '-resize'; - } + $option = $this->adaptive() ? '-adaptive-resize' : '-resize'; $size = $this->size(); if ($size) { diff --git a/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickRevertEffect.php b/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickRevertEffect.php index 9109f6aca..7e5a2891c 100644 --- a/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickRevertEffect.php +++ b/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickRevertEffect.php @@ -11,9 +11,8 @@ class ImagemagickRevertEffect extends AbstractRevertEffect { /** * @param array $data The effect data, if available. - * @return self */ - public function process(array $data = null) + public function process(?array $data = null): static { if ($data !== null) { $this->setData($data); diff --git a/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickRotateEffect.php b/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickRotateEffect.php index 39f20f4d4..2fc48e7db 100644 --- a/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickRotateEffect.php +++ b/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickRotateEffect.php @@ -11,9 +11,8 @@ class ImagemagickRotateEffect extends AbstractRotateEffect { /** * @param array $data The effect data, if available. - * @return self */ - public function process(array $data = null) + public function process(?array $data = null): static { if ($data !== null) { $this->setData($data); diff --git a/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickSepiaEffect.php b/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickSepiaEffect.php index 5f5f699b7..41aeab7be 100644 --- a/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickSepiaEffect.php +++ b/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickSepiaEffect.php @@ -11,9 +11,8 @@ class ImagemagickSepiaEffect extends AbstractSepiaEffect { /** * @param array $data The effect data, if available. - * @return self */ - public function process(array $data = null) + public function process(?array $data = null): static { if ($data !== null) { $this->setData($data); diff --git a/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickSharpenEffect.php b/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickSharpenEffect.php index 336b29610..45d6e8b66 100644 --- a/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickSharpenEffect.php +++ b/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickSharpenEffect.php @@ -9,10 +9,7 @@ */ class ImagemagickSharpenEffect extends AbstractSharpenEffect { - /** - * @return self - */ - public function processAdaptive() + public function processAdaptive(): static { $radius = $this->radius(); $sigma = $this->sigma(); @@ -22,10 +19,7 @@ public function processAdaptive() return $this; } - /** - * @return self - */ - public function processUnsharp() + public function processUnsharp(): static { $radius = $this->radius(); $sigma = $this->sigma(); @@ -38,10 +32,7 @@ public function processUnsharp() return $this; } - /** - * @return self - */ - public function processStandard() + public function processStandard(): static { $radius = $this->radius(); $sigma = $this->sigma(); diff --git a/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickThresholdEffect.php b/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickThresholdEffect.php index d9e78e534..1a4b57aa0 100644 --- a/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickThresholdEffect.php +++ b/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickThresholdEffect.php @@ -11,9 +11,8 @@ class ImagemagickThresholdEffect extends AbstractThresholdEffect { /** * @param array $data The effect data, if available. - * @return self */ - public function process(array $data = null) + public function process(?array $data = null): static { if ($data !== null) { $this->setData($data); diff --git a/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickTintEffect.php b/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickTintEffect.php index 94c59c73f..a6b7e2ca2 100644 --- a/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickTintEffect.php +++ b/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickTintEffect.php @@ -11,19 +11,14 @@ class ImagemagickTintEffect extends AbstractTintEffect { /** * @param array $data The effect data, if available. - * @return self */ - public function process(array $data = null) + public function process(?array $data = null): static { if ($data !== null) { $this->setData($data); } - if ($this->midtone() === true) { - $tintCmd = '-tint'; - } else { - $tintCmd = '-colorize'; - } + $tintCmd = $this->midtone() === true ? '-tint' : '-colorize'; $color = $this->color(); $value = ($this->opacity() * 100) . '%'; $cmd = '-fill "' . $color . '" ' . $tintCmd . ' ' . $value; diff --git a/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickWatermarkEffect.php b/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickWatermarkEffect.php index f4f3caffa..f06888e2e 100644 --- a/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickWatermarkEffect.php +++ b/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickWatermarkEffect.php @@ -12,9 +12,8 @@ class ImagemagickWatermarkEffect extends AbstractWatermarkEffect { /** * @param array $data The effect data, if available. - * @return self */ - public function process(array $data = null) + public function process(?array $data = null): static { if ($data !== null) { $this->setData($data); @@ -28,7 +27,7 @@ public function process(array $data = null) $watermark = $out; } else { $watermark = $this->watermark(); - $c = get_class($this->image()); + $c = $this->image()::class; $w = new $c(); $w->open($watermark); $width = $w->width(); diff --git a/packages/image/src/Charcoal/Image/Imagemagick/ImagemagickImage.php b/packages/image/src/Charcoal/Image/Imagemagick/ImagemagickImage.php index f4e6ca967..777254ac4 100644 --- a/packages/image/src/Charcoal/Image/Imagemagick/ImagemagickImage.php +++ b/packages/image/src/Charcoal/Image/Imagemagick/ImagemagickImage.php @@ -17,9 +17,8 @@ class ImagemagickImage extends AbstractImage { /** * The temporary file location - * @var string|null $tmpFile */ - private $tmpFile; + private ?string $tmpFile = null; /** * @var string $mogrifyCmd @@ -59,10 +58,7 @@ public function __destruct() $this->resetTmp(); } - /** - * @return string - */ - public function driverType() + public function driverType(): string { return 'imagemagick'; } @@ -74,9 +70,8 @@ public function driverType() * @param integer $height Image height, in pixels. * @param string $color Default to transparent. * @throws InvalidArgumentException If the size arguments are not valid. - * @return self */ - public function create($width, $height, $color = 'rgb(100%, 100%, 100%, 0)') + public function create($width, $height, $color = 'rgb(100%, 100%, 100%, 0)'): static { if (!is_numeric($width) || $width < 1) { throw new InvalidArgumentException( @@ -101,9 +96,8 @@ public function create($width, $height, $color = 'rgb(100%, 100%, 100%, 0)') * @param string $source The source path / filename. * @throws Exception If the source file does not exist. * @throws InvalidArgumentException If the source argument is not a string. - * @return self */ - public function open($source = null) + public function open($source = null): static { if ($source !== null && !is_string($source)) { throw new InvalidArgumentException( @@ -111,14 +105,12 @@ public function open($source = null) ); } - $source = ($source) ? $source : $this->source(); + $source = $source ?: $this->source(); $this->resetTmp(); - if (!file_exists($source)) { - if (null === parse_url($source, PHP_URL_HOST)) { - throw new Exception( - sprintf('File "%s" does not exist', $source) - ); - } + if (!file_exists($source) && null === parse_url($source, PHP_URL_HOST)) { + throw new Exception( + sprintf('File "%s" does not exist', $source) + ); } copy($source, $this->tmp()); @@ -133,9 +125,8 @@ public function open($source = null) * @param string $target The target path / filename. * @throws Exception If the target file does not exist or is not writeable. * @throws InvalidArgumentException If the target argument is not a string. - * @return self */ - public function save($target = null) + public function save($target = null): static { if ($target !== null && !is_string($target)) { throw new InvalidArgumentException( @@ -143,7 +134,7 @@ public function save($target = null) ); } - $target = ($target) ? $target : $this->target(); + $target = $target ?: $this->target(); if (!is_writable(dirname($target))) { throw new Exception( sprintf('Target "%s" is not writable', $target) @@ -157,10 +148,8 @@ public function save($target = null) /** * Get the image's width, in pixels - * - * @return integer */ - public function width() + public function width(): int { if (!file_exists($this->tmp())) { return 0; @@ -171,10 +160,8 @@ public function width() /** * Get the image's height, in pixels - * - * @return integer */ - public function height() + public function height(): int { if (!file_exists($this->tmp())) { return 0; @@ -185,9 +172,8 @@ public function height() /** * @param string $channel The channel name to convert. - * @return string */ - public function convertChannel($channel) + public function convertChannel($channel): string { return ucfirst($channel); } @@ -211,13 +197,13 @@ protected function findCmd($cmdName) if (!is_string($cmdName)) { throw new InvalidArgumentException(sprintf( 'Target image must be a string, received %s', - (is_object($cmdName) ? get_class($cmdName) : gettype($cmdName)) + (get_debug_type($cmdName)) )); } if (!in_array($cmdName, $this->availableCommands())) { if (!is_string($cmdName)) { - $cmdName = (is_object($cmdName) ? get_class($cmdName) : gettype($cmdName)); + $cmdName = (get_debug_type($cmdName)); } throw new OutOfBoundsException(sprintf( 'Unsupported command "%s" provided', @@ -228,7 +214,7 @@ protected function findCmd($cmdName) $cmd = exec('type -p ' . $cmdName); $cmd = str_replace($cmdName . ' is ', '', $cmd); - if (!$cmd) { + if ($cmd === '' || $cmd === '0') { $cmd = exec('where ' . $cmdName); } @@ -248,10 +234,8 @@ protected function findCmd($cmdName) /** * Retrieve the list of available commands. - * - * @return array */ - public function availableCommands() + public function availableCommands(): array { return [ 'mogrify', 'convert', 'composite', 'identify' ]; } @@ -269,7 +253,7 @@ public function cmd($name) if (!is_string($name)) { throw new InvalidArgumentException(sprintf( 'Command name must be a string, received %s', - (is_object($name) ? get_class($name) : gettype($name)) + (get_debug_type($name)) )); } @@ -288,7 +272,7 @@ public function cmd($name) default: if (!is_string($name)) { - $name = (is_object($name) ? get_class($name) : gettype($name)); + $name = (get_debug_type($name)); } throw new OutOfBoundsException(sprintf( 'Unsupported command "%s" provided', @@ -347,10 +331,8 @@ public function identifyCmd() /** * Generate a temporary file, to apply effects on. - * - * @return string */ - public function tmp() + public function tmp(): string { if ($this->tmpFile !== null) { return $this->tmpFile; @@ -363,7 +345,7 @@ public function tmp() /** * @return ImagemagickImage Chainable */ - public function resetTmp() + public function resetTmp(): static { if (file_exists($this->tmpFile)) { unlink($this->tmpFile); @@ -383,7 +365,7 @@ public function resetTmp() * @throws Exception If the command fails. * @return string */ - public function exec($cmd) + public function exec(string $cmd): string|false|null { if (function_exists('proc_open')) { $proc = proc_open( @@ -408,9 +390,7 @@ public function exec($cmd) return $out; } else { - $ret = shell_exec($cmd); - - return $ret; + return shell_exec($cmd); } } @@ -420,13 +400,9 @@ public function exec($cmd) * @throws Exception If the tmp file was not properly set. * @return ImagemagickImage Chainable */ - public function applyCmd($params, $cmd = null) + public function applyCmd(string $params, $cmd = null): static { - if ($cmd === null) { - $cmd = $this->mogrifyCmd(); - } else { - $cmd = $this->cmd($cmd); - } + $cmd = $cmd === null ? $this->mogrifyCmd() : $this->cmd($cmd); if (!file_exists($this->tmp())) { throw new Exception( @@ -444,9 +420,8 @@ public function applyCmd($params, $cmd = null) * * @param string $gravity The standard gravity name. * @throws InvalidArgumentException If the gravity argument is not a valid gravity. - * @return integer */ - public function imagemagickGravity($gravity) + public function imagemagickGravity($gravity): string { $gravityMap = [ 'center' => 'center', diff --git a/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickAutoorientationEffect.php b/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickAutoorientationEffect.php index 630f0bd23..3f4517b41 100644 --- a/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickAutoorientationEffect.php +++ b/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickAutoorientationEffect.php @@ -14,7 +14,7 @@ class ImagickAutoorientationEffect extends AbstractAutoorientationEffect * @param array $data The effect data, if available. * @return ImagickAutoorientationEffect Chainable */ - public function process(array $data = null) + public function process(?array $data = null): static { if ($data !== null) { $this->setData($data); diff --git a/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickBlurEffect.php b/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickBlurEffect.php index 2431759e1..0541a2ac0 100644 --- a/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickBlurEffect.php +++ b/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickBlurEffect.php @@ -13,7 +13,7 @@ class ImagickBlurEffect extends AbstractBlurEffect /** * @return ImagickBlurEffect Chainable */ - public function processAdaptive() + public function processAdaptive(): static { $channel = $this->image()->imagickChannel($this->channel()); $this->image()->imagick()->adaptiveBlurImage($this->radius(), $this->sigma(), $channel); @@ -23,7 +23,7 @@ public function processAdaptive() /** * @return ImagickBlurEffect Chainable */ - public function processGaussian() + public function processGaussian(): static { $channel = $this->image()->imagickChannel($this->channel()); $this->image()->imagick()->gaussianBlurImage($this->radius(), $this->sigma(), $channel); @@ -33,7 +33,7 @@ public function processGaussian() /** * @return ImagickBlurEffect Chainable */ - public function processMotion() + public function processMotion(): static { $channel = $this->image()->imagickChannel($this->channel()); $this->image()->imagick()->motionBlurImage($this->radius(), $this->sigma(), $this->angle(), $channel); @@ -43,7 +43,7 @@ public function processMotion() /** * @return ImagickBlurEffect Chainable */ - public function processRadial() + public function processRadial(): static { $angle = $this->angle(); $channel = $this->image()->imagickChannel($this->channel()); @@ -53,9 +53,8 @@ public function processRadial() /** * @throws Exception This method is not yet supported on Imagick. - * @return void */ - public function processSoft() + public function processSoft(): never { throw new Exception( 'Soft blur is not (yet) supported with imagick driver.' @@ -65,7 +64,7 @@ public function processSoft() /** * @return ImagickBlurEffect Chainable */ - public function processStandard() + public function processStandard(): static { $channel = $this->image()->imagickChannel($this->channel()); $this->image()->imagick()->blurImage($this->radius(), $this->sigma(), $channel); diff --git a/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickCompressionEffect.php b/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickCompressionEffect.php index cf31ad4bc..a5c6808e5 100644 --- a/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickCompressionEffect.php +++ b/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickCompressionEffect.php @@ -11,16 +11,15 @@ class ImagickCompressionEffect extends AbstractCompressionEffect { /** * @param array $data The effect data, if available. - * @return self */ - public function process(array $data = null) + public function process(?array $data = null): static { if ($data !== null) { $this->setData($data); } - $target = $this->image()->target(); - $extension = strtolower($this->image()->imagick()->getImageFormat()); + $this->image()->target(); + $extension = strtolower((string) $this->image()->imagick()->getImageFormat()); $invalidExtensions = [ 'gif', 'bmp' ]; diff --git a/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickDitherEffect.php b/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickDitherEffect.php index 8c054afe7..f7bbacff1 100644 --- a/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickDitherEffect.php +++ b/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickDitherEffect.php @@ -14,7 +14,7 @@ class ImagickDitherEffect extends AbstractDitherEffect * @param array $data The effect data, if available. * @return ImagickDitherEffect Chainable */ - public function process(array $data = null) + public function process(?array $data = null): static { if ($data !== null) { $this->setData($data); diff --git a/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickFormatEffect.php b/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickFormatEffect.php index bd7cc6c02..d41d4d8d9 100644 --- a/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickFormatEffect.php +++ b/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickFormatEffect.php @@ -14,7 +14,7 @@ class ImagickFormatEffect extends AbstractFormatEffect * @param array $data The effect data, if available. * @return ImageFormatEffect Chainable */ - public function process(array $data = null) + public function process(?array $data = null): static { if ($this->format()) { $this->image()->imagick()->setimageFormat($this->format()); diff --git a/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickGrayscaleEffect.php b/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickGrayscaleEffect.php index f9637ae29..b5b4fc593 100644 --- a/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickGrayscaleEffect.php +++ b/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickGrayscaleEffect.php @@ -14,7 +14,7 @@ class ImagickGrayscaleEffect extends AbstractGrayscaleEffect * @param array $data The effect data, if available. * @return ImagickGrayscaleEffect Chainable */ - public function process(array $data = null) + public function process(?array $data = null): static { if ($data !== null) { $this->setData($data); diff --git a/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickMaskEffect.php b/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickMaskEffect.php index d52307a33..1673d50c9 100644 --- a/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickMaskEffect.php +++ b/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickMaskEffect.php @@ -1,5 +1,7 @@ setData($data); diff --git a/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickMirrorEffect.php b/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickMirrorEffect.php index 8e2a7d022..169754920 100644 --- a/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickMirrorEffect.php +++ b/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickMirrorEffect.php @@ -11,9 +11,8 @@ class ImagickMirrorEffect extends AbstractMirrorEffect { /** * @param array $data The effect data, if available. - * @return self */ - public function process(array $data = null) + public function process(?array $data = null): static { if ($data !== null) { $this->setData($data); diff --git a/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickModulateEffect.php b/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickModulateEffect.php index 626d60609..d65acbe74 100644 --- a/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickModulateEffect.php +++ b/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickModulateEffect.php @@ -13,7 +13,7 @@ class ImagickModulateEffect extends AbstractModulateEffect * @param array $data The effect data, if available. * @return AbstractModulateEffect Chainable */ - public function process(array $data = null) + public function process(?array $data = null): static { if ($data !== null) { $this->setData($data); diff --git a/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickRevertEffect.php b/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickRevertEffect.php index 0892a1b88..f2767ad85 100644 --- a/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickRevertEffect.php +++ b/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickRevertEffect.php @@ -13,7 +13,7 @@ class ImagickRevertEffect extends AbstractRevertEffect * @param array $data The effect data, if available. * @return ImagickRevertEffect Chainable */ - public function process(array $data = null) + public function process(?array $data = null): static { if ($data !== null) { $this->setData($data); diff --git a/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickRotateEffect.php b/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickRotateEffect.php index 46ac9b131..cf4cc4a00 100644 --- a/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickRotateEffect.php +++ b/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickRotateEffect.php @@ -13,7 +13,7 @@ class ImagickRotateEffect extends AbstractRotateEffect * @param array $data The effect data, if available. * @return ImagickRotateEffect Chainable */ - public function process(array $data = null) + public function process(?array $data = null): static { if ($data !== null) { $this->setData($data); diff --git a/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickSepiaEffect.php b/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickSepiaEffect.php index 7171b3660..671cf0505 100644 --- a/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickSepiaEffect.php +++ b/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickSepiaEffect.php @@ -13,7 +13,7 @@ class ImagickSepiaEffect extends AbstractSepiaEffect * @param array $data The effect data, if available. * @return ImagickSepiaEffect Chainable */ - public function process(array $data = null) + public function process(?array $data = null): static { if ($data !== null) { $this->setData($data); diff --git a/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickSharpenEffect.php b/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickSharpenEffect.php index 1af3ab40a..d9f05b759 100644 --- a/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickSharpenEffect.php +++ b/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickSharpenEffect.php @@ -12,7 +12,7 @@ class ImagickSharpenEffect extends AbstractSharpenEffect /** * @return ImagickSharpenEffect Chainable */ - public function processAdaptive() + public function processAdaptive(): static { $channel = $this->image()->imagickChannel($this->channel()); $this->image()->imagick()->adaptiveAbstractSharpenEffectImage($this->radius(), $this->sigma(), $channel); @@ -22,7 +22,7 @@ public function processAdaptive() /** * @return ImagickSharpenEffect Chainable */ - public function processUnsharp() + public function processUnsharp(): static { $radius = $this->radius(); $sigma = $this->sigma(); @@ -38,7 +38,7 @@ public function processUnsharp() /** * @return ImagickSharpenEffect Chainable */ - public function processStandard() + public function processStandard(): static { $channel = $this->image()->imagickChannel($this->channel()); $this->image()->imagick()->sharpenImage($this->radius(), $this->sigma(), $channel); diff --git a/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickThresholdEffect.php b/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickThresholdEffect.php index cf9630af7..915b55e5a 100644 --- a/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickThresholdEffect.php +++ b/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickThresholdEffect.php @@ -11,9 +11,8 @@ class ImagickThresholdEffect extends AbstractThresholdEffect { /** * @param array $data The effect data, if available. - * @return self */ - public function process(array $data = null) + public function process(?array $data = null): static { if ($data !== null) { $this->setData($data); diff --git a/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickTintEffect.php b/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickTintEffect.php index ce45437c8..666a84475 100644 --- a/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickTintEffect.php +++ b/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickTintEffect.php @@ -12,9 +12,8 @@ class ImagickTintEffect extends AbstractTintEffect { /** * @param array $data The effect data, if available. - * @return self */ - public function process(array $data = null) + public function process(?array $data = null): static { if ($data !== null) { $this->setData($data); diff --git a/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickWatermarkEffect.php b/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickWatermarkEffect.php index ff9661a36..7db0fd884 100644 --- a/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickWatermarkEffect.php +++ b/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickWatermarkEffect.php @@ -15,9 +15,8 @@ class ImagickWatermarkEffect extends AbstractWatermarkEffect /** * @param array $data The effect data, if available. * @throws Exception If the image data is invalid. - * @return self */ - public function process(array $data = null) + public function process(?array $data = null): static { if ($data !== null) { $this->setData($data); @@ -30,7 +29,7 @@ public function process(array $data = null) if ($this->watermark() instanceof ImageInterface) { $watermark = $this->watermark(); } else { - $imgClass = get_class($img); + $imgClass = $img::class; $watermark = new $imgClass(); $watermark->open($this->watermark()); } diff --git a/packages/image/src/Charcoal/Image/Imagick/ImagickImage.php b/packages/image/src/Charcoal/Image/Imagick/ImagickImage.php index 1c8480275..73256b5a2 100644 --- a/packages/image/src/Charcoal/Image/Imagick/ImagickImage.php +++ b/packages/image/src/Charcoal/Image/Imagick/ImagickImage.php @@ -12,10 +12,7 @@ */ class ImagickImage extends AbstractImage { - /** - * @var Imagick $imagick - */ - private $imagick; + private readonly \Imagick $imagick; /** * @throws Exception If imagick driver can not be loaded. @@ -30,18 +27,12 @@ public function __construct() $this->imagick = new Imagick(); } - /** - * @return string - */ - public function driverType() + public function driverType(): string { return 'imagick'; } - /** - * @return Imagick - */ - public function imagick() + public function imagick(): \Imagick { return $this->imagick; } @@ -53,9 +44,8 @@ public function imagick() * @param integer $height Image height, in pixels. * @param string $color Default to transparent. * @throws InvalidArgumentException If the size arguments are not valid, positive integers. - * @return self */ - public function create($width, $height, $color = 'rgb(100%, 100%, 100%, 0)') + public function create($width, $height, $color = 'rgb(100%, 100%, 100%, 0)'): static { if (!is_numeric($width) || $width < 1) { throw new InvalidArgumentException( @@ -76,9 +66,8 @@ public function create($width, $height, $color = 'rgb(100%, 100%, 100%, 0)') * * @param string $source The source path / filename. * @throws InvalidArgumentException If the source argument is not a string. - * @return self */ - public function open($source = null) + public function open($source = null): static { if ($source !== null && !is_string($source)) { throw new InvalidArgumentException( @@ -86,7 +75,7 @@ public function open($source = null) ); } - $source = ($source) ? $source : $this->source(); + $source = $source ?: $this->source(); if (parse_url($source, PHP_URL_HOST)) { $handle = fopen($source, 'rb'); $this->imagick()->readImageFile($handle); @@ -103,9 +92,8 @@ public function open($source = null) * * @param string $target The target path / filename. * @throws InvalidArgumentException If the target argument is not a string. - * @return self */ - public function save($target = null) + public function save($target = null): static { if ($target !== null && !is_string($target)) { throw new InvalidArgumentException( @@ -113,7 +101,7 @@ public function save($target = null) ); } - $target = ($target) ? $target : $this->target(); + $target = $target ?: $this->target(); $fileExt = pathinfo($target, PATHINFO_EXTENSION); $this->imagick()->setImageFormat($fileExt); @@ -124,20 +112,16 @@ public function save($target = null) /** * Get the image's width, in pixels - * - * @return integer */ - public function width() + public function width(): int { return $this->imagick()->getImageWidth(); } /** * Get the image's height, in pixels - * - * @return integer */ - public function height() + public function height(): int { return $this->imagick()->getImageHeight(); } @@ -147,9 +131,8 @@ public function height() * * @param string $channel The standard "channel" string. * @throws InvalidArgumentException If the channel argument is not a valid channel. - * @return integer */ - public function imagickChannel($channel) + public function imagickChannel($channel): int { $channelMap = [ // RGB @@ -180,9 +163,8 @@ public function imagickChannel($channel) * * @param string $gravity The standard gravity name. * @throws InvalidArgumentException If the gravity argument is not a valid gravity type. - * @return integer */ - public function imagickGravity($gravity) + public function imagickGravity($gravity): int { $gravityMap = [ 'center' => Imagick::GRAVITY_CENTER, diff --git a/packages/image/tests/Charcoal/Image/AbstractImageTest.php b/packages/image/tests/Charcoal/Image/AbstractImageTest.php index 7c0efee28..140d5f612 100644 --- a/packages/image/tests/Charcoal/Image/AbstractImageTest.php +++ b/packages/image/tests/Charcoal/Image/AbstractImageTest.php @@ -7,9 +7,9 @@ class AbstractImageTest extends \PHPUnit\Framework\TestCase { - public function testSetData() + public function testSetData(): void { - $obj = $this->getMockForAbstractClass('\Charcoal\Image\AbstractImage'); + $obj = $this->getMockForAbstractClass(\Charcoal\Image\AbstractImage::class); $ret = $obj->setData( [ 'source'=>__DIR__.'/test.png', @@ -25,9 +25,9 @@ public function testSetData() $this->assertEquals('/tmp/phpunit.png', $obj->target()); } - public function testSetSource() + public function testSetSource(): void { - $obj = $this->getMockForAbstractClass('\Charcoal\Image\AbstractImage'); + $obj = $this->getMockForAbstractClass(\Charcoal\Image\AbstractImage::class); $ret = $obj->setSource('test.png'); $this->assertSame($ret, $obj); $this->assertEquals('test.png', $obj->source()); @@ -36,9 +36,9 @@ public function testSetSource() $obj->setSource(false); } - public function testSetTarget() + public function testSetTarget(): void { - $obj = $this->getMockForAbstractClass('\Charcoal\Image\AbstractImage'); + $obj = $this->getMockForAbstractClass(\Charcoal\Image\AbstractImage::class); $ret = $obj->setTarget('test.png'); $this->assertSame($ret, $obj); $this->assertEquals('test.png', $obj->target()); diff --git a/packages/image/tests/Charcoal/Image/Effect/AbstractBlurEffectTest.php b/packages/image/tests/Charcoal/Image/Effect/AbstractBlurEffectTest.php index 9ae2e4a6c..9e4bf3235 100644 --- a/packages/image/tests/Charcoal/Image/Effect/AbstractBlurEffectTest.php +++ b/packages/image/tests/Charcoal/Image/Effect/AbstractBlurEffectTest.php @@ -8,13 +8,13 @@ class AbstractBlurEffectTest extends \PHPUnit\Framework\TestCase protected function setUp(): void { - $img = $this->getMockForAbstractClass('\Charcoal\Image\AbstractImage'); + $img = $this->getMockForAbstractClass(\Charcoal\Image\AbstractImage::class); $img->method('driverType')->willReturn('imagick'); - $this->obj = $this->getMockForAbstractClass('\Charcoal\Image\Effect\AbstractBlurEffect'); + $this->obj = $this->getMockForAbstractClass(\Charcoal\Image\Effect\AbstractBlurEffect::class); $this->obj->setImage($img); } - public function testDefaults() + public function testDefaults(): void { $obj = $this->obj; @@ -25,7 +25,7 @@ public function testDefaults() $this->assertEquals(0, $obj->angle()); } - public function testSetData() + public function testSetData(): void { $obj = $this->obj; $ret = $obj->setData( @@ -46,7 +46,7 @@ public function testSetData() $this->assertEquals(40, $obj->angle()); } - public function testSetRadius() + public function testSetRadius(): void { $obj = $this->obj; @@ -58,14 +58,14 @@ public function testSetRadius() $obj->setRadius(false); } - public function testSetRadiusNegativeThrowsException() + public function testSetRadiusNegativeThrowsException(): void { $this->expectException('\InvalidArgumentException'); $obj = $this->obj; $obj->setRadius(-1); } - public function testSetSigma() + public function testSetSigma(): void { $obj = $this->obj; @@ -77,14 +77,14 @@ public function testSetSigma() $obj->setSigma(false); } - public function testSetSigmaNegativeThrowsException() + public function testSetSigmaNegativeThrowsException(): void { $this->expectException('\InvalidArgumentException'); $obj = $this->obj; $obj->setSigma(-1); } - public function testSetMode() + public function testSetMode(): void { $obj = $this->obj; $ret = $obj->setMode('radial'); @@ -95,7 +95,7 @@ public function testSetMode() $obj->setMode('foobar'); } - public function testSetChannel() + public function testSetChannel(): void { $obj = $this->obj; $ret = $obj->setChannel('alpha'); @@ -106,7 +106,7 @@ public function testSetChannel() $obj->setChannel('foobar'); } - public function testSetAngle() + public function testSetAngle(): void { $obj = $this->obj; $ret = $obj->setAngle(45); diff --git a/packages/image/tests/Charcoal/Image/Effect/AbstractDitherEffectTest.php b/packages/image/tests/Charcoal/Image/Effect/AbstractDitherEffectTest.php index 938fc672d..5cf1f90cf 100644 --- a/packages/image/tests/Charcoal/Image/Effect/AbstractDitherEffectTest.php +++ b/packages/image/tests/Charcoal/Image/Effect/AbstractDitherEffectTest.php @@ -8,13 +8,13 @@ class AbstractDitherEffectTest extends \PHPUnit\Framework\TestCase protected function setUp(): void { - $img = $this->getMockForAbstractClass('\Charcoal\Image\AbstractImage'); + $img = $this->getMockForAbstractClass(\Charcoal\Image\AbstractImage::class); $img->method('driverType')->willReturn('imagick'); - $this->obj = $this->getMockForAbstractClass('\Charcoal\Image\Effect\AbstractDitherEffect'); + $this->obj = $this->getMockForAbstractClass(\Charcoal\Image\Effect\AbstractDitherEffect::class); $this->obj->setImage($img); } - public function testDefaults() + public function testDefaults(): void { $obj = $this->obj; @@ -22,7 +22,7 @@ public function testDefaults() $this->assertEquals('', $obj->mode()); } - public function testSetData() + public function testSetData(): void { $obj = $this->obj; $ret = $obj->setData( @@ -37,7 +37,7 @@ public function testSetData() $this->assertEquals('h6x6a', $obj->mode()); } - public function testSetColors() + public function testSetColors(): void { $obj = $this->obj; $ret = $obj->setColors(6); @@ -48,7 +48,7 @@ public function testSetColors() $obj->setColors(false); } - public function testSetMode() + public function testSetMode(): void { $obj = $this->obj; $ret = $obj->setMode('checks'); diff --git a/packages/image/tests/Charcoal/Image/Effect/AbstractGrayscaleEffectTest.php b/packages/image/tests/Charcoal/Image/Effect/AbstractGrayscaleEffectTest.php index b63d76372..828c0acda 100644 --- a/packages/image/tests/Charcoal/Image/Effect/AbstractGrayscaleEffectTest.php +++ b/packages/image/tests/Charcoal/Image/Effect/AbstractGrayscaleEffectTest.php @@ -8,13 +8,13 @@ class AbstractGrayscaleEffectTest extends \PHPUnit\Framework\TestCase protected function setUp(): void { - $img = $this->getMockForAbstractClass('\Charcoal\Image\AbstractImage'); + $img = $this->getMockForAbstractClass(\Charcoal\Image\AbstractImage::class); $img->method('driverType')->willReturn('imagick'); - $this->obj = $this->getMockForAbstractClass('\Charcoal\Image\Effect\AbstractGrayscaleEffect'); + $this->obj = $this->getMockForAbstractClass(\Charcoal\Image\Effect\AbstractGrayscaleEffect::class); $this->obj->setImage($img); } - public function testSetData() + public function testSetData(): void { $obj = $this->obj; $ret = $obj->setData([]); diff --git a/packages/image/tests/Charcoal/Image/Effect/AbstractMaskEffectTest.php b/packages/image/tests/Charcoal/Image/Effect/AbstractMaskEffectTest.php index 4134b759f..e86b3da49 100644 --- a/packages/image/tests/Charcoal/Image/Effect/AbstractMaskEffectTest.php +++ b/packages/image/tests/Charcoal/Image/Effect/AbstractMaskEffectTest.php @@ -8,13 +8,13 @@ class AbstractMaskEffectTest extends \PHPUnit\Framework\TestCase protected function setUp(): void { - $img = $this->getMockForAbstractClass('\Charcoal\Image\AbstractImage'); + $img = $this->getMockForAbstractClass(\Charcoal\Image\AbstractImage::class); $img->method('driverType')->willReturn('imagick'); - $this->obj = $this->getMockForAbstractClass('\Charcoal\Image\Effect\AbstractMaskEffect'); + $this->obj = $this->getMockForAbstractClass(\Charcoal\Image\Effect\AbstractMaskEffect::class); $this->obj->setImage($img); } - public function testDefaults() + public function testDefaults(): void { $obj = $this->obj; @@ -24,7 +24,7 @@ public function testDefaults() $this->assertEquals(0, $obj->y()); } - public function testSetData() + public function testSetData(): void { $obj = $this->obj; $ret = $obj->setData( @@ -45,7 +45,7 @@ public function testSetData() $this->assertEquals(20, $obj->y()); } - public function testSetMask() + public function testSetMask(): void { $obj = $this->obj; $ret = $obj->setMask('bar/baz.png'); @@ -56,7 +56,7 @@ public function testSetMask() $obj->setMask(false); } - public function testSetOpacity() + public function testSetOpacity(): void { $obj = $this->obj; $ret = $obj->setOpacity(0.42); @@ -67,7 +67,7 @@ public function testSetOpacity() $obj->setOpacity(false); } - public function testSetGravity() + public function testSetGravity(): void { $obj = $this->obj; $ret = $obj->setGravity('se'); @@ -78,7 +78,7 @@ public function testSetGravity() $obj->setGravity('foobar'); } - public function testSetX() + public function testSetX(): void { $obj = $this->obj; $ret = $obj->setX(15); @@ -89,7 +89,7 @@ public function testSetX() $obj->setX(false); } - public function testSetY() + public function testSetY(): void { $obj = $this->obj; $ret = $obj->setY(15); diff --git a/packages/image/tests/Charcoal/Image/Effect/AbstractMirrorEffectTest.php b/packages/image/tests/Charcoal/Image/Effect/AbstractMirrorEffectTest.php index ad319ced4..b4e65b454 100644 --- a/packages/image/tests/Charcoal/Image/Effect/AbstractMirrorEffectTest.php +++ b/packages/image/tests/Charcoal/Image/Effect/AbstractMirrorEffectTest.php @@ -8,13 +8,13 @@ class AbstractMirrorEffectTest extends \PHPUnit\Framework\TestCase protected function setUp(): void { - $img = $this->getMockForAbstractClass('\Charcoal\Image\AbstractImage'); + $img = $this->getMockForAbstractClass(\Charcoal\Image\AbstractImage::class); $img->method('driverType')->willReturn('imagick'); - $this->obj = $this->getMockForAbstractClass('\Charcoal\Image\Effect\AbstractMirrorEffect'); + $this->obj = $this->getMockForAbstractClass(\Charcoal\Image\Effect\AbstractMirrorEffect::class); $this->obj->setImage($img); } - public function testDefaults() + public function testDefaults(): void { $obj = $this->obj; @@ -24,7 +24,7 @@ public function testDefaults() ); } - public function testSetData() + public function testSetData(): void { $obj = $this->obj; $ret = $obj->setData( @@ -37,7 +37,7 @@ public function testSetData() $this->assertEquals('x', $obj->axis()); } - public function testSetAxis() + public function testSetAxis(): void { $obj = $this->obj; diff --git a/packages/image/tests/Charcoal/Image/Effect/AbstractModulateEffectTest.php b/packages/image/tests/Charcoal/Image/Effect/AbstractModulateEffectTest.php index 4c5893fef..12459750b 100644 --- a/packages/image/tests/Charcoal/Image/Effect/AbstractModulateEffectTest.php +++ b/packages/image/tests/Charcoal/Image/Effect/AbstractModulateEffectTest.php @@ -8,13 +8,13 @@ class AbstractModulateEffectTest extends \PHPUnit\Framework\TestCase protected function setUp(): void { - $img = $this->getMockForAbstractClass('\Charcoal\Image\AbstractImage'); + $img = $this->getMockForAbstractClass(\Charcoal\Image\AbstractImage::class); $img->method('driverType')->willReturn('imagick'); - $this->obj = $this->getMockForAbstractClass('\Charcoal\Image\Effect\AbstractModulateEffect'); + $this->obj = $this->getMockForAbstractClass(\Charcoal\Image\Effect\AbstractModulateEffect::class); $this->obj->setImage($img); } - public function testDefaults() + public function testDefaults(): void { $obj = $this->obj; @@ -23,7 +23,7 @@ public function testDefaults() $this->assertEquals(0, $obj->luminance()); } - public function testSetData() + public function testSetData(): void { $obj = $this->obj; $ret = $obj->setData( @@ -40,7 +40,7 @@ public function testSetData() $this->assertEquals(-75, $obj->luminance()); } - public function testSetHue() + public function testSetHue(): void { $obj = $this->obj; @@ -52,7 +52,7 @@ public function testSetHue() $obj->setHue(false); } - public function testSetHueMaxExeption() + public function testSetHueMaxExeption(): void { $this->expectException('\InvalidArgumentException'); @@ -60,7 +60,7 @@ public function testSetHueMaxExeption() $obj->setHue(101); } - public function testSetHueMinExeption() + public function testSetHueMinExeption(): void { $this->expectException('\InvalidArgumentException'); @@ -68,7 +68,7 @@ public function testSetHueMinExeption() $obj->setHue(-101); } - public function testSetSaturation() + public function testSetSaturation(): void { $obj = $this->obj; @@ -80,7 +80,7 @@ public function testSetSaturation() $obj->setSaturation(false); } - public function testSetSaturationMaxExeption() + public function testSetSaturationMaxExeption(): void { $this->expectException('\InvalidArgumentException'); @@ -88,7 +88,7 @@ public function testSetSaturationMaxExeption() $obj->setSaturation(101); } - public function testSetSaturationMinExeption() + public function testSetSaturationMinExeption(): void { $this->expectException('\InvalidArgumentException'); @@ -96,7 +96,7 @@ public function testSetSaturationMinExeption() $obj->setSaturation(-101); } - public function testSetLuminance() + public function testSetLuminance(): void { $obj = $this->obj; @@ -108,7 +108,7 @@ public function testSetLuminance() $obj->setLuminance(false); } - public function testSetLuminanceMaxExeption() + public function testSetLuminanceMaxExeption(): void { $this->expectException('\InvalidArgumentException'); @@ -116,7 +116,7 @@ public function testSetLuminanceMaxExeption() $obj->setLuminance(101); } - public function testSetLuminanceMinExeption() + public function testSetLuminanceMinExeption(): void { $this->expectException('\InvalidArgumentException'); diff --git a/packages/image/tests/Charcoal/Image/Effect/AbstractResizeEffectTest.php b/packages/image/tests/Charcoal/Image/Effect/AbstractResizeEffectTest.php index f0a4f9f12..0770667e8 100644 --- a/packages/image/tests/Charcoal/Image/Effect/AbstractResizeEffectTest.php +++ b/packages/image/tests/Charcoal/Image/Effect/AbstractResizeEffectTest.php @@ -8,13 +8,13 @@ class AbstractResizeEffectTest extends \PHPUnit\Framework\TestCase protected function setUp(): void { - $img = $this->getMockForAbstractClass('\Charcoal\Image\AbstractImage'); + $img = $this->getMockForAbstractClass(\Charcoal\Image\AbstractImage::class); $img->method('driverType')->willReturn('imagick'); - $this->obj = $this->getMockForAbstractClass('\Charcoal\Image\Effect\AbstractResizeEffect'); + $this->obj = $this->getMockForAbstractClass(\Charcoal\Image\Effect\AbstractResizeEffect::class); $this->obj->setImage($img); } - public function testDefaults() + public function testDefaults(): void { $obj = $this->obj; @@ -27,7 +27,7 @@ public function testDefaults() $this->assertFalse($obj->adaptive()); } - public function testSetData() + public function testSetData(): void { $obj = $this->obj; $ret = $obj->setData([ @@ -50,7 +50,7 @@ public function testSetData() $this->assertTrue($obj->adaptive()); } - public function testSetMode() + public function testSetMode(): void { $obj = $this->obj; $ret = $obj->setMode('width'); @@ -61,7 +61,7 @@ public function testSetMode() $obj->setMode('foobar'); } - public function testSetSize() + public function testSetSize(): void { $obj = $this->obj; @@ -70,14 +70,14 @@ public function testSetSize() $this->assertEquals('50%', $obj->size()); - $ret = $obj->setSize(400); + $obj->setSize(400); $this->assertEquals(400, $obj->size()); - $ret = $obj->setSize(null); + $obj->setSize(null); $this->assertEquals(null, $obj->size()); } - public function testSetSizeException() + public function testSetSizeException(): void { $obj = $this->obj; @@ -88,7 +88,7 @@ public function testSetSizeException() $obj->setSize([ 'foo', 'bar' ]); } - public function testSetWidth() + public function testSetWidth(): void { $obj = $this->obj; $ret = $obj->setWidth(400); @@ -96,14 +96,14 @@ public function testSetWidth() $this->assertEquals(400, $obj->width()); } - public function testSetWidthNegativeException() + public function testSetWidthNegativeException(): void { $obj = $this->obj; $this->expectException('\InvalidArgumentException'); $obj->setWidth(-1); } - public function testSetHeight() + public function testSetHeight(): void { $obj = $this->obj; $ret = $obj->setHeight(400); @@ -111,14 +111,14 @@ public function testSetHeight() $this->assertEquals(400, $obj->height()); } - public function testSetHeightNegativeException() + public function testSetHeightNegativeException(): void { $obj = $this->obj; $this->expectException('\InvalidArgumentException'); $obj->setHeight(-1); } - public function testSetGravity() + public function testSetGravity(): void { $obj = $this->obj; $ret = $obj->setGravity('nw'); @@ -129,7 +129,7 @@ public function testSetGravity() $obj->setGravity('foobar'); } - public function testSetBackgroundColor() + public function testSetBackgroundColor(): void { $obj = $this->obj; $ret = $obj->setBackgroundColor('red'); @@ -140,7 +140,7 @@ public function testSetBackgroundColor() $obj->setBackgroundColor(false); } - public function testSetAdaptive() + public function testSetAdaptive(): void { $obj = $this->obj; $ret = $obj->setAdaptive(true); @@ -148,7 +148,7 @@ public function testSetAdaptive() $this->assertTrue($obj->adaptive()); } - public function testAutoMode() + public function testAutoMode(): void { $obj = $this->obj; $obj->setMode('auto'); @@ -170,7 +170,7 @@ public function testAutoMode() $this->assertEquals('none', $obj->autoMode()); } - public function testProcessExactParametersException() + public function testProcessExactParametersException(): void { $obj = $this->obj; $obj->setMode('exact'); @@ -178,7 +178,7 @@ public function testProcessExactParametersException() $obj->process(); } - public function testProcessWidthParameterException() + public function testProcessWidthParameterException(): void { $obj = $this->obj; $obj->setMode('width'); @@ -186,7 +186,7 @@ public function testProcessWidthParameterException() $obj->process(); } - public function testProcessHeightParameterException() + public function testProcessHeightParameterException(): void { $obj = $this->obj; $obj->setMode('height'); @@ -194,7 +194,7 @@ public function testProcessHeightParameterException() $obj->process(); } - public function testProcessBestFitParameterException() + public function testProcessBestFitParameterException(): void { $obj = $this->obj; $obj->setMode('best_fit'); @@ -202,7 +202,7 @@ public function testProcessBestFitParameterException() $obj->process(); } - public function testProcessCropException() + public function testProcessCropException(): void { $obj = $this->obj; $obj->setMode('crop'); @@ -210,7 +210,7 @@ public function testProcessCropException() $obj->process(); } - public function testProcessFillException() + public function testProcessFillException(): void { $obj = $this->obj; $obj->setMode('fill'); diff --git a/packages/image/tests/Charcoal/Image/Effect/AbstractRevertEffectTest.php b/packages/image/tests/Charcoal/Image/Effect/AbstractRevertEffectTest.php index 97d790e69..a64f3b760 100644 --- a/packages/image/tests/Charcoal/Image/Effect/AbstractRevertEffectTest.php +++ b/packages/image/tests/Charcoal/Image/Effect/AbstractRevertEffectTest.php @@ -8,19 +8,19 @@ class AbstractRevertEffectTest extends \PHPUnit\Framework\TestCase protected function setUp(): void { - $img = $this->getMockForAbstractClass('\Charcoal\Image\AbstractImage'); - $this->obj = $this->getMockForAbstractClass('\Charcoal\Image\Effect\AbstractRevertEffect'); + $img = $this->getMockForAbstractClass(\Charcoal\Image\AbstractImage::class); + $this->obj = $this->getMockForAbstractClass(\Charcoal\Image\Effect\AbstractRevertEffect::class); $this->obj->setImage($img); } - public function testDefaults() + public function testDefaults(): void { $obj = $this->obj; $this->assertEquals('all', $obj->channel()); } - public function testSetData() + public function testSetData(): void { $obj = $this->obj; $ret = $obj->setData( @@ -33,7 +33,7 @@ public function testSetData() $this->assertEquals('green', $obj->channel()); } - public function testSetChannel() + public function testSetChannel(): void { $obj = $this->obj; $ret = $obj->setChannel('gray'); diff --git a/packages/image/tests/Charcoal/Image/Effect/AbstractRotateEffectTest.php b/packages/image/tests/Charcoal/Image/Effect/AbstractRotateEffectTest.php index 5bd045324..165f89901 100644 --- a/packages/image/tests/Charcoal/Image/Effect/AbstractRotateEffectTest.php +++ b/packages/image/tests/Charcoal/Image/Effect/AbstractRotateEffectTest.php @@ -8,12 +8,12 @@ class AbstractRotateEffectTest extends \PHPUnit\Framework\TestCase protected function setUp(): void { - $img = $this->getMockForAbstractClass('\Charcoal\Image\AbstractImage'); - $this->obj = $this->getMockForAbstractClass('\Charcoal\Image\Effect\AbstractRotateEffect'); + $img = $this->getMockForAbstractClass(\Charcoal\Image\AbstractImage::class); + $this->obj = $this->getMockForAbstractClass(\Charcoal\Image\Effect\AbstractRotateEffect::class); $this->obj->setImage($img); } - public function testDefaults() + public function testDefaults(): void { $obj = $this->obj; @@ -21,7 +21,7 @@ public function testDefaults() $this->assertEquals('rgb(100%, 100%, 100%, 0)', $obj->backgroundColor()); } - public function testSetData() + public function testSetData(): void { $obj = $this->obj; $ret = $obj->setData( @@ -36,7 +36,7 @@ public function testSetData() $this->assertEquals('blue', $obj->backgroundColor()); } - public function testSetAngle() + public function testSetAngle(): void { $obj = $this->obj; $ret = $obj->setAngle(135); @@ -47,7 +47,7 @@ public function testSetAngle() $obj->setAngle('foobar'); } - public function testSetBackgroundColor() + public function testSetBackgroundColor(): void { $obj = $this->obj; $ret = $obj->setBackgroundColor('red'); diff --git a/packages/image/tests/Charcoal/Image/Effect/AbstractSepiaEffectTest.php b/packages/image/tests/Charcoal/Image/Effect/AbstractSepiaEffectTest.php index e282359da..9a4efdab8 100644 --- a/packages/image/tests/Charcoal/Image/Effect/AbstractSepiaEffectTest.php +++ b/packages/image/tests/Charcoal/Image/Effect/AbstractSepiaEffectTest.php @@ -8,19 +8,19 @@ class AbstractSepiaEffectTest extends \PHPUnit\Framework\TestCase protected function setUp(): void { - $img = $this->getMockForAbstractClass('\Charcoal\Image\AbstractImage'); - $this->obj = $this->getMockForAbstractClass('\Charcoal\Image\Effect\AbstractSepiaEffect'); + $img = $this->getMockForAbstractClass(\Charcoal\Image\AbstractImage::class); + $this->obj = $this->getMockForAbstractClass(\Charcoal\Image\Effect\AbstractSepiaEffect::class); $this->obj->setImage($img); } - public function testDefaults() + public function testDefaults(): void { $obj = $this->obj; $this->assertEquals(75, $obj->threshold()); } - public function testSetData() + public function testSetData(): void { $obj = $this->obj; $ret = $obj->setData( @@ -33,7 +33,7 @@ public function testSetData() $this->assertEquals(100, $obj->threshold()); } - public function testSetThreshold() + public function testSetThreshold(): void { $obj = $this->obj; @@ -45,13 +45,13 @@ public function testSetThreshold() $obj->setThreshold('foobar'); } - public function testSetThresholdMinException() + public function testSetThresholdMinException(): void { $this->expectException('\InvalidArgumentException'); $obj = $this->obj; $obj->setThreshold(-1); } - public function testSetThresholdMaxException() + public function testSetThresholdMaxException(): void { $this->expectException('\InvalidArgumentException'); $obj = $this->obj; diff --git a/packages/image/tests/Charcoal/Image/Effect/AbstractSharpenEffectTest.php b/packages/image/tests/Charcoal/Image/Effect/AbstractSharpenEffectTest.php index 67e2547a0..c82334563 100644 --- a/packages/image/tests/Charcoal/Image/Effect/AbstractSharpenEffectTest.php +++ b/packages/image/tests/Charcoal/Image/Effect/AbstractSharpenEffectTest.php @@ -8,12 +8,12 @@ class AbstractSharpenEffectTest extends \PHPUnit\Framework\TestCase protected function setUp(): void { - $img = $this->getMockForAbstractClass('\Charcoal\Image\AbstractImage'); - $this->obj = $this->getMockForAbstractClass('\Charcoal\Image\Effect\AbstractSharpenEffect'); + $img = $this->getMockForAbstractClass(\Charcoal\Image\AbstractImage::class); + $this->obj = $this->getMockForAbstractClass(\Charcoal\Image\Effect\AbstractSharpenEffect::class); $this->obj->setImage($img); } - public function testDefaults() + public function testDefaults(): void { $obj = $this->obj; @@ -25,7 +25,7 @@ public function testDefaults() $this->assertEquals('all', $obj->channel()); } - public function testSetData() + public function testSetData(): void { $obj = $this->obj; $ret = $obj->setData( @@ -48,7 +48,7 @@ public function testSetData() $this->assertEquals('blue', $obj->channel()); } - public function testSetRadius() + public function testSetRadius(): void { $obj = $this->obj; @@ -60,14 +60,14 @@ public function testSetRadius() $obj->setRadius(false); } - public function testSetRadiusNegativeThrowsException() + public function testSetRadiusNegativeThrowsException(): void { $this->expectException('\InvalidArgumentException'); $obj = $this->obj; $obj->setRadius(-1); } - public function testSetSigma() + public function testSetSigma(): void { $obj = $this->obj; @@ -79,14 +79,14 @@ public function testSetSigma() $obj->setSigma(false); } - public function testSetSigmaNegativeThrowsException() + public function testSetSigmaNegativeThrowsException(): void { $this->expectException('\InvalidArgumentException'); $obj = $this->obj; $obj->setSigma(-1); } - public function testSetAmount() + public function testSetAmount(): void { $obj = $this->obj; @@ -98,14 +98,14 @@ public function testSetAmount() $obj->setAmount('foobar'); } - public function testSetAmountNegativeThrowsException() + public function testSetAmountNegativeThrowsException(): void { $this->expectException('\InvalidArgumentException'); $obj = $this->obj; $obj->setAmount(-1); } - public function testSetThreshold() + public function testSetThreshold(): void { $obj = $this->obj; @@ -117,7 +117,7 @@ public function testSetThreshold() $obj->setThreshold('foobar'); } - public function testSetMode() + public function testSetMode(): void { $obj = $this->obj; $ret = $obj->setMode('unsharp'); @@ -128,7 +128,7 @@ public function testSetMode() $obj->setMode('foobar'); } - public function testSetChannel() + public function testSetChannel(): void { $obj = $this->obj; $ret = $obj->setChannel('alpha'); diff --git a/packages/image/tests/Charcoal/Image/Effect/AbstractThresholdEffectTest.php b/packages/image/tests/Charcoal/Image/Effect/AbstractThresholdEffectTest.php index bc6e4bc6f..9ba11ec9f 100644 --- a/packages/image/tests/Charcoal/Image/Effect/AbstractThresholdEffectTest.php +++ b/packages/image/tests/Charcoal/Image/Effect/AbstractThresholdEffectTest.php @@ -8,19 +8,19 @@ class AbstractThresholdEffectTest extends \PHPUnit\Framework\TestCase protected function setUp(): void { - $img = $this->getMockForAbstractClass('\Charcoal\Image\AbstractImage'); - $this->obj = $this->getMockForAbstractClass('\Charcoal\Image\Effect\AbstractThresholdEffect'); + $img = $this->getMockForAbstractClass(\Charcoal\Image\AbstractImage::class); + $this->obj = $this->getMockForAbstractClass(\Charcoal\Image\Effect\AbstractThresholdEffect::class); $this->obj->setImage($img); } - public function testDefaults() + public function testDefaults(): void { $obj = $this->obj; $this->assertEquals(0.5, $obj->threshold()); } - public function testSetData() + public function testSetData(): void { $obj = $this->obj; $ret = $obj->setData( @@ -33,7 +33,7 @@ public function testSetData() $this->assertEquals(0.1, $obj->threshold()); } - public function testSetThreshold() + public function testSetThreshold(): void { $obj = $this->obj; @@ -45,14 +45,14 @@ public function testSetThreshold() $obj->setThreshold('foobar'); } - public function testSetThresholdMinException() + public function testSetThresholdMinException(): void { $this->expectException('\InvalidArgumentException'); $obj = $this->obj; $obj->setThreshold(-1); } - public function testSetThresholdMaxException() + public function testSetThresholdMaxException(): void { $this->expectException('\InvalidArgumentException'); $obj = $this->obj; diff --git a/packages/image/tests/Charcoal/Image/Effect/AbstractTintEffectTest.php b/packages/image/tests/Charcoal/Image/Effect/AbstractTintEffectTest.php index e544cb4d2..0e4e84314 100644 --- a/packages/image/tests/Charcoal/Image/Effect/AbstractTintEffectTest.php +++ b/packages/image/tests/Charcoal/Image/Effect/AbstractTintEffectTest.php @@ -8,12 +8,12 @@ class AbstractTintEffectTest extends \PHPUnit\Framework\TestCase protected function setUp(): void { - $img = $this->getMockForAbstractClass('\Charcoal\Image\AbstractImage'); - $this->obj = $this->getMockForAbstractClass('\Charcoal\Image\Effect\AbstractTintEffect'); + $img = $this->getMockForAbstractClass(\Charcoal\Image\AbstractImage::class); + $this->obj = $this->getMockForAbstractClass(\Charcoal\Image\Effect\AbstractTintEffect::class); $this->obj->setImage($img); } - public function testDefaults() + public function testDefaults(): void { $obj = $this->obj; @@ -22,7 +22,7 @@ public function testDefaults() $this->assertTrue($obj->midtone()); } - public function testSetData() + public function testSetData(): void { $obj = $this->obj; $ret = $obj->setData( @@ -39,7 +39,7 @@ public function testSetData() $this->assertFalse($obj->midtone()); } - public function testSetColor() + public function testSetColor(): void { $obj = $this->obj; $ret = $obj->setColor('#ff00ff'); @@ -50,7 +50,7 @@ public function testSetColor() $obj->setColor(false); } - public function testSetOpacity() + public function testSetOpacity(): void { $obj = $this->obj; $ret = $obj->setOpacity(0.42); @@ -61,7 +61,7 @@ public function testSetOpacity() $obj->setOpacity(false); } - public function testSetMidtone() + public function testSetMidtone(): void { $obj = $this->obj; $ret = $obj->setMidtone(false); diff --git a/packages/image/tests/Charcoal/Image/Effect/AbstractWatermarkEffectTest.php b/packages/image/tests/Charcoal/Image/Effect/AbstractWatermarkEffectTest.php index 1f259e200..d51faa08e 100644 --- a/packages/image/tests/Charcoal/Image/Effect/AbstractWatermarkEffectTest.php +++ b/packages/image/tests/Charcoal/Image/Effect/AbstractWatermarkEffectTest.php @@ -8,12 +8,12 @@ class AbstractWatermarkEffectTest extends \PHPUnit\Framework\TestCase protected function setUp(): void { - $img = $this->getMockForAbstractClass('\Charcoal\Image\AbstractImage'); - $this->obj = $this->getMockForAbstractClass('\Charcoal\Image\Effect\AbstractWatermarkEffect'); + $img = $this->getMockForAbstractClass(\Charcoal\Image\AbstractImage::class); + $this->obj = $this->getMockForAbstractClass(\Charcoal\Image\Effect\AbstractWatermarkEffect::class); $this->obj->setImage($img); } - public function testDefaults() + public function testDefaults(): void { $obj = $this->obj; @@ -23,7 +23,7 @@ public function testDefaults() $this->assertEquals(0, $obj->y()); } - public function testSetData() + public function testSetData(): void { $obj = $this->obj; $ret = $obj->setData( @@ -44,7 +44,7 @@ public function testSetData() $this->assertEquals(20, $obj->y()); } - public function testSetWatermark() + public function testSetWatermark(): void { $obj = $this->obj; $ret = $obj->setWatermark('bar/baz.png'); @@ -55,7 +55,7 @@ public function testSetWatermark() $obj->setWatermark(false); } - public function testSetOpacity() + public function testSetOpacity(): void { $obj = $this->obj; $ret = $obj->setOpacity(0.42); @@ -66,7 +66,7 @@ public function testSetOpacity() $obj->setOpacity(false); } - public function testSetGravity() + public function testSetGravity(): void { $obj = $this->obj; $ret = $obj->setGravity('se'); @@ -77,7 +77,7 @@ public function testSetGravity() $obj->setGravity('foobar'); } - public function testSetX() + public function testSetX(): void { $obj = $this->obj; $ret = $obj->setX(15); @@ -88,7 +88,7 @@ public function testSetX() $obj->setX(false); } - public function testSetY() + public function testSetY(): void { $obj = $this->obj; $ret = $obj->setY(15); diff --git a/packages/image/tests/Charcoal/Image/Imagemagick/ImagemagickImageTest.php b/packages/image/tests/Charcoal/Image/Imagemagick/ImagemagickImageTest.php index 8cde190fa..f08367003 100644 --- a/packages/image/tests/Charcoal/Image/Imagemagick/ImagemagickImageTest.php +++ b/packages/image/tests/Charcoal/Image/Imagemagick/ImagemagickImageTest.php @@ -9,11 +9,11 @@ class ImagemagickImageTest extends \PHPUnit\Framework\TestCase { - private $factory; + private ?\Charcoal\Image\ImageFactory $factory = null; - public function imageFactory() + public function imageFactory(): \Charcoal\Image\ImageFactory { - if ($this->factory === null) { + if (!$this->factory instanceof \Charcoal\Image\ImageFactory) { $this->factory = new ImageFactory(); } @@ -25,7 +25,7 @@ public function createImage() return $this->imageFactory()->create('imagemagick'); } - public function testFromFactory() + public function testFromFactory(): void { $obj = $this->createImage(); $this->assertInstanceOf(Image::class, $obj); @@ -41,21 +41,21 @@ public function testFromFactory() // $obj->create('foo', 'bar'); // } - public function testCreateMinWidth() + public function testCreateMinWidth(): void { $obj = $this->createImage(); $this->expectException(InvalidArgumentException::class); $obj->create(400, 0); } - public function testCreateMinHeigth() + public function testCreateMinHeigth(): void { $obj = $this->createImage(); $this->expectException(InvalidArgumentException::class); $obj->create(0, 400); } - public function testOpen() + public function testOpen(): void { $obj = $this->createImage(); $ret = $obj->open(EXAMPLES_DIR.'/test01.jpg'); @@ -65,7 +65,7 @@ public function testOpen() $obj->open(false); } - public function testOpenInvalidFile() + public function testOpenInvalidFile(): void { $obj = $this->createImage(); $this->expectException('\Exception'); @@ -88,7 +88,7 @@ public function testOpenInvalidFile() // //$this->assertEquals($id1, $id2); // } - public function testWidth() + public function testWidth(): void { $obj = $this->createImage(); $obj->open(EXAMPLES_DIR.'/test01.jpg'); @@ -97,7 +97,7 @@ public function testWidth() $this->assertEquals(3456, $width); } - public function testHeight() + public function testHeight(): void { $obj = $this->createImage(); $obj->open(EXAMPLES_DIR.'/test01.jpg'); @@ -106,10 +106,8 @@ public function testHeight() $this->assertEquals(2304, $height); } - /** - * @dataProvider effectProvider - */ - public function testEffects($effect, $filename) + #[\PHPUnit\Framework\Attributes\DataProvider('effectProvider')] + public function testEffects(array $effect, string $filename): void { $obj = $this->createImage(); $obj->open(EXAMPLES_DIR.'/test02.png'); @@ -120,10 +118,8 @@ public function testEffects($effect, $filename) $this->assertTrue(file_exists(OUTPUT_DIR.'/'.$filename)); } - /** - * @dataProvider invalidEffectProvider - */ - public function testInvalidEffext($effect) + #[\PHPUnit\Framework\Attributes\DataProvider('invalidEffectProvider')] + public function testInvalidEffext(array $effect): void { $obj = $this->createImage(); $obj->open(EXAMPLES_DIR.'/test02.png'); @@ -132,7 +128,7 @@ public function testInvalidEffext($effect) $obj->processsEffect($effect); } - public function effectProvider() + public static function effectProvider(): array { return [ # Blur @@ -186,7 +182,7 @@ public function effectProvider() ]; } - public function invalidEffectProvider() + public static function invalidEffectProvider(): array { return [ # Dither diff --git a/packages/image/tests/Charcoal/Image/Imagick/ImagickImageTest.php b/packages/image/tests/Charcoal/Image/Imagick/ImagickImageTest.php index 0bb7fc05f..91992fb5d 100644 --- a/packages/image/tests/Charcoal/Image/Imagick/ImagickImageTest.php +++ b/packages/image/tests/Charcoal/Image/Imagick/ImagickImageTest.php @@ -11,11 +11,11 @@ class ImagickImageTest extends \PHPUnit\Framework\TestCase { - private $factory; + private ?\Charcoal\Image\ImageFactory $factory = null; - public function imageFactory() + public function imageFactory(): \Charcoal\Image\ImageFactory { - if ($this->factory === null) { + if (!$this->factory instanceof \Charcoal\Image\ImageFactory) { $this->factory = new ImageFactory(); } @@ -27,13 +27,13 @@ public function createImage() return $this->imageFactory()->create('imagick'); } - public function testFromFactory() + public function testFromFactory(): void { $obj = $this->createImage(); $this->assertInstanceOf(Image::class, $obj); } - public function testCreate() + public function testCreate(): void { $obj = $this->createImage(); $ret = $obj->create(1, 1); @@ -43,21 +43,21 @@ public function testCreate() $obj->create('foo', 'bar'); } - public function testCreateMinWidth() + public function testCreateMinWidth(): void { $obj = $this->createImage(); $this->expectException('\InvalidArgumentException'); $obj->create(400, 0); } - public function testCreateMinHeigth() + public function testCreateMinHeigth(): void { $obj = $this->createImage(); $this->expectException('\InvalidArgumentException'); $obj->create(0, 400); } - public function testOpen() + public function testOpen(): void { $obj = $this->createImage(); $ret = $obj->open(EXAMPLES_DIR.'/test01.jpg'); @@ -67,14 +67,14 @@ public function testOpen() $obj->open(false); } - public function testOpenInvalidFile() + public function testOpenInvalidFile(): void { $obj = $this->createImage(); $this->expectException('\Exception'); $obj->open('foo/bar/baz.png'); } - public function testOpenWithoutParamUseSource() + public function testOpenWithoutParamUseSource(): void { $obj1 = $this->createImage(); $obj1->open(EXAMPLES_DIR.'/test01.jpg'); @@ -90,25 +90,25 @@ public function testOpenWithoutParamUseSource() $this->assertEquals($id1, $id2); } - public function testWidth() + public function testWidth(): void { $obj = $this->createImage(); - $ret = $obj->open(EXAMPLES_DIR.'/test01.jpg'); + $obj->open(EXAMPLES_DIR.'/test01.jpg'); $width = $obj->width(); $this->assertEquals(3456, $width); } - public function testHeight() + public function testHeight(): void { $obj = $this->createImage(); - $ret = $obj->open(EXAMPLES_DIR.'/test01.jpg'); + $obj->open(EXAMPLES_DIR.'/test01.jpg'); $height = $obj->height(); $this->assertEquals(2304, $height); } - public function testImagickChannel() + public function testImagickChannel(): void { $obj = $this->createImage(); $ret = $obj->imagickChannel('red'); @@ -118,7 +118,7 @@ public function testImagickChannel() $obj->imagickChannel('foobar'); } - public function testImagickGravity() + public function testImagickGravity(): void { $obj = $this->createImage(); $ret = $obj->imagickGravity('ne'); @@ -128,10 +128,8 @@ public function testImagickGravity() $obj->imagickGravity('foobar'); } - /** - * @dataProvider effectProvider - */ - public function testEffects($effect, $filename) + #[\PHPUnit\Framework\Attributes\DataProvider('effectProvider')] + public function testEffects(array $effect, string $filename): void { $obj = $this->createImage(); $obj->open(EXAMPLES_DIR.'/test02.png'); @@ -142,10 +140,8 @@ public function testEffects($effect, $filename) $this->assertTrue(file_exists(OUTPUT_DIR.'/'.$filename)); } - /** - * @dataProvider invalidEffectProvider - */ - public function testInvalidEffect($effect) + #[\PHPUnit\Framework\Attributes\DataProvider('invalidEffectProvider')] + public function testInvalidEffect(array $effect): void { $obj = $this->createImage(); $obj->open(EXAMPLES_DIR.'/test02.png'); @@ -154,7 +150,7 @@ public function testInvalidEffect($effect) $obj->processsEffect($effect); } - public function effectProvider() + public static function effectProvider(): array { return [ # Blur @@ -220,7 +216,7 @@ public function effectProvider() ]; } - public function invalidEffectProvider() + public static function invalidEffectProvider(): array { return [ # Blur diff --git a/packages/image/tests/bootstrap.php b/packages/image/tests/bootstrap.php index be2a3d483..64b6efd7c 100644 --- a/packages/image/tests/bootstrap.php +++ b/packages/image/tests/bootstrap.php @@ -1,6 +1,8 @@ numCategoryItems === null) { $items = $this->getCategoryItems(); - if (is_array($items) || ($items instanceof \Countable)) { - $count = count($items); - } else { - $count = 0; - } + $count = is_countable($items) ? count($items) : 0; $this->numCategoryItems = $count; } @@ -98,10 +94,8 @@ public function numCategoryItems() /** * Gets wether the category has any items, directly within it. - * - * @return boolean */ - public function hasCategoryItems() + public function hasCategoryItems(): bool { $numItems = $this->getNumCategoryItems(); return ($numItems > 0); diff --git a/packages/object/src/Charcoal/Object/Content.php b/packages/object/src/Charcoal/Object/Content.php index 32cf80941..bf2fc0001 100644 --- a/packages/object/src/Charcoal/Object/Content.php +++ b/packages/object/src/Charcoal/Object/Content.php @@ -36,32 +36,28 @@ class Content extends AbstractModel implements /** * Objects are active by default - * @var boolean */ - private $active = true; + private bool $active = true; /** * The position is used for ordering lists - * @var integer */ - private $position = 0; + private ?int $position = 0; - /** - * @var FactoryInterface - */ - private $modelFactory; + private ?\Charcoal\Factory\FactoryInterface $modelFactory = null; /** * @var string[] */ - private $requiredAclPermissions = []; + private array $requiredAclPermissions = []; /** * Dependencies * @param Container $container DI Container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -74,7 +70,7 @@ protected function setDependencies(Container $container) * @param FactoryInterface $factory The factory used to create models. * @return Content Chainable */ - protected function setModelFactory(FactoryInterface $factory) + protected function setModelFactory(FactoryInterface $factory): static { $this->modelFactory = $factory; return $this; @@ -83,7 +79,7 @@ protected function setModelFactory(FactoryInterface $factory) /** * @return FactoryInterface The model factory. */ - protected function modelFactory() + protected function modelFactory(): ?\Charcoal\Factory\FactoryInterface { return $this->modelFactory; } @@ -92,16 +88,13 @@ protected function modelFactory() * @param boolean $active The active flag. * @return Content Chainable */ - public function setActive($active) + public function setActive($active): static { - $this->active = !!$active; + $this->active = (bool) $active; return $this; } - /** - * @return boolean - */ - public function getActive() + public function getActive(): bool { return $this->active; } @@ -111,7 +104,7 @@ public function getActive() * @throws InvalidArgumentException If the position is not an integer (or numeric integer string). * @return Content Chainable */ - public function setPosition($position) + public function setPosition($position): static { if ($position === null) { $this->position = null; @@ -121,7 +114,7 @@ public function setPosition($position) if (!is_numeric($position)) { throw new InvalidArgumentException(sprintf( 'Position must be an integer, received %s', - is_object($position) ? get_class($position) : gettype($position) + get_debug_type($position) )); } @@ -132,7 +125,7 @@ public function setPosition($position) /** * @return integer */ - public function getPosition() + public function getPosition(): ?int { return $this->position; } @@ -143,7 +136,7 @@ public function getPosition() * @param string|string[] $permissions The required ACL permissions. * @return Content Chainable */ - public function setRequiredAclPermissions($permissions) + public function setRequiredAclPermissions($permissions): static { if ($permissions === null || !$permissions) { $this->requiredAclPermissions = []; @@ -151,7 +144,7 @@ public function setRequiredAclPermissions($permissions) } if (is_string($permissions)) { $permissions = explode(',', $permissions); - $permissions = array_map('trim', $permissions); + $permissions = array_map(trim(...), $permissions); } if (!is_array($permissions)) { throw new InvalidArgumentException( @@ -165,7 +158,7 @@ public function setRequiredAclPermissions($permissions) /** * @return string[] */ - public function getRequiredAclPermissions() + public function getRequiredAclPermissions(): array { return $this->requiredAclPermissions; } @@ -173,9 +166,9 @@ public function getRequiredAclPermissions() /** * StorableTrait > preSave(): Called automatically before saving the object to source. * For content object, set the `created` and `lastModified` properties automatically - * @return boolean */ - protected function preSave() + #[\Override] + protected function preSave(): bool { parent::preSave(); @@ -191,9 +184,9 @@ protected function preSave() * For content object, set the `lastModified` property automatically. * * @param array $properties The properties (ident) set for update. - * @return boolean */ - protected function preUpdate(array $properties = null) + #[\Override] + protected function preUpdate(?array $properties = null): bool { parent::preUpdate($properties); diff --git a/packages/object/src/Charcoal/Object/ContentInterface.php b/packages/object/src/Charcoal/Object/ContentInterface.php index ad6380702..86bd50bcc 100644 --- a/packages/object/src/Charcoal/Object/ContentInterface.php +++ b/packages/object/src/Charcoal/Object/ContentInterface.php @@ -1,5 +1,7 @@ $children) { + if ($rootObjects === [] && $childObjects !== []) { + foreach ($childObjects as $children) { $parentObj = $children[0]->getMasterObject(); $parentObj->auxiliary = true; @@ -111,14 +108,12 @@ public function sortTree() } // Display orphaned descendants. - if ($childObjects) { - foreach ($childObjects as $orphans) { - foreach ($orphans as $descendants) { - $descendants->level = 0; - $sortedObjects[$descendants->id()] = $descendants; + foreach ($childObjects as $orphans) { + foreach ($orphans as $descendants) { + $descendants->level = 0; + $sortedObjects[$descendants->id()] = $descendants; - $count++; - } + $count++; } } } else { @@ -184,15 +179,14 @@ public function sortTree() * @param integer $level The level directly below the $parentObj. * @param HierarchicalInterface[] $sortedObjects The list of objects to be displayed. * Passed by reference. - * @return void */ private function sortDescendantObjects( HierarchicalInterface $parentObj, array &$childObjects, - &$count, - $level, + int|float &$count, + int|float $level, array &$sortedObjects - ) { + ): void { $pageNum = $this->getPage(); $perPage = $this->getNumPerPage(); @@ -293,7 +287,7 @@ private function sortDescendantObjects( * @throws InvalidArgumentException If the parameter is not numeric or < 0. * @return Pagination (Chainable) */ - public function setPage($page) + public function setPage($page): static { if (!is_numeric($page)) { throw new InvalidArgumentException( @@ -326,7 +320,7 @@ public function getPage() * @throws InvalidArgumentException If the parameter is not numeric or < 0. * @return Pagination (Chainable) */ - public function setNumPerPage($num) + public function setNumPerPage($num): static { if (!is_numeric($num)) { throw new InvalidArgumentException( @@ -359,9 +353,9 @@ public function getNumPerPage() * Determine if the given value is acceptable for the collection. * * @param mixed $value The value being vetted. - * @return boolean */ - public function isAcceptable($value) + #[\Override] + public function isAcceptable($value): bool { return ($value instanceof HierarchicalInterface); } diff --git a/packages/object/src/Charcoal/Object/HierarchicalInterface.php b/packages/object/src/Charcoal/Object/HierarchicalInterface.php index c83a65cea..9a20080c4 100644 --- a/packages/object/src/Charcoal/Object/HierarchicalInterface.php +++ b/packages/object/src/Charcoal/Object/HierarchicalInterface.php @@ -1,5 +1,7 @@ masterObject && $this->hasMaster()) { $master = $this->objFromIdent($this->getMaster()); - if ($master instanceof ModelInterface) { - if ($master->id() === $this->id()) { - throw new UnexpectedValueException(sprintf( - 'Can not be ones own parent: %s', - $master->id() - )); - } + if ($master instanceof ModelInterface && $master->id() === $this->id()) { + throw new UnexpectedValueException(sprintf( + 'Can not be ones own parent: %s', + $master->id() + )); } $this->masterObject = $master; @@ -129,20 +127,16 @@ public function getMasterObject() /** * Determine if this object's immediate parent exists. - * - * @return boolean */ - public function hasMasterObject() + public function hasMasterObject(): bool { return (bool)$this->getMasterObject(); } /** * Determine if this object has a direct parent. - * - * @return boolean */ - public function hasMaster() + public function hasMaster(): bool { return (bool)$this->getMaster(); } @@ -151,10 +145,8 @@ public function hasMaster() * Determine if this object is the head (top-level) of its hierarchy. * * Top-level objects do not have a parent (master). - * - * @return boolean */ - public function isTopLevel() + public function isTopLevel(): bool { return !$this->getMaster(); } @@ -163,10 +155,8 @@ public function isTopLevel() * Determine if this object is the tail (last-level) of its hierarchy. * * Last-level objects do not have a children. - * - * @return boolean */ - public function isLastLevel() + public function isLastLevel(): bool { return !$this->hasChildren(); } @@ -177,15 +167,12 @@ public function isLastLevel() * Starts at "1" (top-level). * * The level is calculated by loading all ancestors with {@see self::hierarchy()}. - * - * @return integer */ - public function hierarchyLevel() + public function hierarchyLevel(): int { $hierarchy = $this->hierarchy(); - $level = (count($hierarchy) + 1); - return $level; + return count($hierarchy) + 1; } /** @@ -205,10 +192,8 @@ public function toplevelMaster() /** * Determine if this object has any ancestors. - * - * @return boolean */ - public function hasParents() + public function hasParents(): bool { return count($this->hierarchy()) > 0; } @@ -232,7 +217,7 @@ public function hierarchy() * * @return HierarchicalInterface[] */ - public function loadHierarchy() + public function loadHierarchy(): array { $hierarchy = []; $master = $this->getMasterObject(); @@ -249,7 +234,7 @@ public function loadHierarchy() * * @return HierarchicalInterface[] */ - public function invertedHierarchy() + public function invertedHierarchy(): array { $hierarchy = $this->hierarchy(); @@ -260,9 +245,8 @@ public function invertedHierarchy() * Determine if the object is the parent of the given object. * * @param mixed $child The child (or ID) to match against. - * @return boolean */ - public function isMasterOf($child) + public function isMasterOf($child): bool { $child = $this->objFromIdent($child); @@ -273,21 +257,19 @@ public function isMasterOf($child) * Determine if the object is a parent/ancestor of the given object. * * @param mixed $child The child (or ID) to match against. - * @return boolean * @todo Implementation needed. */ - public function recursiveIsMasterOf($child) + public function recursiveIsMasterOf($child): bool { - $child = $this->objFromIdent($child); + $this->objFromIdent($child); return false; } /** * Get wether the object has any children at all - * @return boolean */ - public function hasChildren() + public function hasChildren(): bool { $numChildren = $this->numChildren(); @@ -296,9 +278,8 @@ public function hasChildren() /** * Get the number of children directly under this object. - * @return integer */ - public function numChildren() + public function numChildren(): int { $children = $this->children(); @@ -308,10 +289,9 @@ public function numChildren() /** * Get the total number of children in the entire hierarchy. * This method counts all children and sub-children, unlike `numChildren()` which only count 1 level. - * @return integer * @todo Implementation needed. */ - public function recursiveNumChildren() + public function recursiveNumChildren(): int { return 0; } @@ -339,13 +319,11 @@ public function addChild($child) { $child = $this->objFromIdent($child); - if ($child instanceof ModelInterface) { - if ($child->id() === $this->id()) { - throw new UnexpectedValueException(sprintf( - 'Can not be ones own child: %s', - $child->id() - )); - } + if ($child instanceof ModelInterface && $child->id() === $this->id()) { + throw new UnexpectedValueException(sprintf( + 'Can not be ones own child: %s', + $child->id() + )); } $this->children[] = $child; @@ -375,9 +353,8 @@ abstract public function loadChildren(); /** * @param mixed $master The master object (or ident) to check against. - * @return boolean */ - public function isChildOf($master) + public function isChildOf($master): bool { $master = $this->objFromIdent($master); @@ -401,20 +378,14 @@ public function recursiveIsChildOf($master) return false; } - /** - * @return boolean - */ - public function hasSiblings() + public function hasSiblings(): bool { $numSiblings = $this->numSiblings(); return ($numSiblings > 1); } - /** - * @return integer - */ - public function numSiblings() + public function numSiblings(): int { $siblings = $this->siblings(); @@ -453,9 +424,8 @@ public function loadSiblings() /** * @param mixed $sibling The sibling to check. - * @return boolean */ - public function isSiblingOf($sibling) + public function isSiblingOf($sibling): bool { $sibling = $this->objFromIdent($sibling); @@ -473,9 +443,9 @@ private function objFromIdent($ident) return null; } - $class = get_called_class(); + $class = static::class; - if (is_object($ident) && ($ident instanceof $class)) { + if ($ident instanceof $class) { return $ident; } @@ -530,11 +500,8 @@ private function loadObjectFromSource($id) private function loadObjectFromCache($id) { $objType = $this->objType(); - if (isset(static::$objectCache[$objType][$id])) { - return static::$objectCache[$objType][$id]; - } - return null; + return static::$objectCache[$objType][$id] ?? null; } /** diff --git a/packages/object/src/Charcoal/Object/ObjectRevision.php b/packages/object/src/Charcoal/Object/ObjectRevision.php index 4f847c572..979f9ed68 100644 --- a/packages/object/src/Charcoal/Object/ObjectRevision.php +++ b/packages/object/src/Charcoal/Object/ObjectRevision.php @@ -33,9 +33,8 @@ class ObjectRevision extends AbstractModel implements ObjectRevisionInterface /** * Object type of this revision (required) - * @var string $targetType */ - private $targetType; + private ?string $targetType = null; /** * Object ID of this revision (required) @@ -45,21 +44,18 @@ class ObjectRevision extends AbstractModel implements ObjectRevisionInterface /** * Revision number. Sequential integer for each object's ID. (required) - * @var integer $revNum */ - private $revNum; + private ?int $revNum = null; /** * Timestamp; when this revision was created - * @var DateTimeInterface|null $revTs */ - private $revTs; + private ?\DateTimeInterface $revTs = null; /** * The (admin) user that was - * @var string|null $revUser */ - private $revUser; + private ?string $revUser = null; /** * @var array $dataPrev @@ -80,6 +76,7 @@ class ObjectRevision extends AbstractModel implements ObjectRevisionInterface * @param Container $container DI Container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -92,7 +89,7 @@ protected function setDependencies(Container $container) * @throws InvalidArgumentException If the obj type parameter is not a string. * @return ObjectRevision Chainable */ - public function setTargetType($targetType) + public function setTargetType($targetType): static { if (!is_string($targetType)) { throw new InvalidArgumentException( @@ -106,7 +103,7 @@ public function setTargetType($targetType) /** * @return string */ - public function getTargetType() + public function getTargetType(): ?string { return $this->targetType; } @@ -115,7 +112,7 @@ public function getTargetType() * @param mixed $targetId The object ID. * @return ObjectRevision Chainable */ - public function setTargetId($targetId) + public function setTargetId($targetId): static { $this->targetId = $targetId; return $this; @@ -134,7 +131,7 @@ public function getTargetId() * @throws InvalidArgumentException If the revision number argument is not numerical. * @return ObjectRevision Chainable */ - public function setRevNum($revNum) + public function setRevNum($revNum): static { if (!is_numeric($revNum)) { throw new InvalidArgumentException( @@ -148,7 +145,7 @@ public function setRevNum($revNum) /** * @return integer */ - public function getRevNum() + public function getRevNum(): ?int { return $this->revNum; } @@ -158,7 +155,7 @@ public function getRevNum() * @throws InvalidArgumentException If the timestamp is invalid. * @return ObjectRevision Chainable */ - public function setRevTs($revTs) + public function setRevTs($revTs): static { if ($revTs === null) { $this->revTs = null; @@ -176,10 +173,7 @@ public function setRevTs($revTs) return $this; } - /** - * @return DateTimeInterface|null - */ - public function getRevTs() + public function getRevTs(): ?\DateTimeInterface { return $this->revTs; } @@ -189,7 +183,7 @@ public function getRevTs() * @throws InvalidArgumentException If the revision user parameter is not a string. * @return ObjectRevision Chainable */ - public function setRevUser($revUser) + public function setRevUser($revUser): static { if ($revUser === null) { $this->revUser = null; @@ -207,7 +201,7 @@ public function setRevUser($revUser) /** * @return string */ - public function getRevUser() + public function getRevUser(): ?string { return $this->revUser; } @@ -216,10 +210,10 @@ public function getRevUser() * @param string|array|null $data The previous revision data. * @return ObjectRevision Chainable */ - public function setDataPrev($data) + public function setDataPrev($data): static { if (!is_array($data)) { - $data = json_decode($data, true); + $data = json_decode((string) $data, true); } if ($data === null) { $data = []; @@ -240,10 +234,10 @@ public function getDataPrev() * @param array|string|null $data The current revision (object) data. * @return ObjectRevision Chainable */ - public function setDataObj($data) + public function setDataObj($data): static { if (!is_array($data)) { - $data = json_decode($data, true); + $data = json_decode((string) $data, true); } if ($data === null) { $data = []; @@ -262,9 +256,8 @@ public function getDataObj() /** * @param array|string $data The data diff. - * @return ObjectRevision */ - public function setDataDiff($data) + public function setDataDiff($data): static { if (!is_array($data)) { $data = json_decode($data, true); @@ -294,7 +287,7 @@ public function getDataDiff() * @param RevisionableInterface $obj The object to create the revision from. * @return ObjectRevision Chainable */ - public function createFromObject(RevisionableInterface $obj) + public function createFromObject(RevisionableInterface $obj): static { $prevRev = $this->lastObjectRevision($obj); @@ -321,7 +314,7 @@ public function createFromObject(RevisionableInterface $obj) * @param array $dataObj Optional. Current revision (object) data. * @return array The diff data */ - public function createDiff(array $dataPrev = null, array $dataObj = null) + public function createDiff(?array $dataPrev = null, ?array $dataObj = null): array { if ($dataPrev === null) { $dataPrev = $this->getDataPrev(); @@ -329,8 +322,7 @@ public function createDiff(array $dataPrev = null, array $dataObj = null) if ($dataObj === null) { $dataObj = $this->getDataObj(); } - $dataDiff = $this->recursiveDiff($dataPrev, $dataObj); - return $dataDiff; + return $this->recursiveDiff($dataPrev, $dataObj); } /** @@ -340,7 +332,7 @@ public function createDiff(array $dataPrev = null, array $dataObj = null) * @param array $array2 Second Array. * @return array The array diff. */ - public function recursiveDiff(array $array1, array $array2) + public function recursiveDiff(array $array1, array $array2): array { $diff = []; @@ -354,13 +346,11 @@ public function recursiveDiff(array $array1, array $array2) $diff[1][$key] = $array2[$key]; } else { $new = $this->recursiveDiff($value, $array2[$key]); - if ($new !== false) { - if (isset($new[0])) { - $diff[0][$key] = $new[0]; - } - if (isset($new[1])) { - $diff[1][$key] = $new[1]; - } + if (isset($new[0])) { + $diff[0][$key] = $new[0]; + } + if (isset($new[1])) { + $diff[1][$key] = $new[1]; } } } elseif ($array2[$key] !== $value) { diff --git a/packages/object/src/Charcoal/Object/ObjectRevisionInterface.php b/packages/object/src/Charcoal/Object/ObjectRevisionInterface.php index 9206ee66c..3f1649607 100644 --- a/packages/object/src/Charcoal/Object/ObjectRevisionInterface.php +++ b/packages/object/src/Charcoal/Object/ObjectRevisionInterface.php @@ -1,5 +1,7 @@ generateUniqueSlug(); @@ -159,7 +159,8 @@ protected function preSave() * @param array $properties Optional. The list of properties to update. * @return boolean */ - protected function preUpdate(array $properties = null) + #[\Override] + protected function preUpdate(?array $properties = null) { $this->setCreationDate('now'); $this->setLastModificationDate('now'); @@ -169,10 +170,8 @@ protected function preUpdate(array $properties = null) /** * Determine if the current slug is unique. - * - * @return boolean */ - public function isSlugUnique() + public function isSlugUnique(): bool { $proto = $this->modelFactory()->get(static::class); $loader = $this->collectionLoader(); @@ -235,9 +234,8 @@ public function generateUniqueSlug() * * @param string|null $slug The route. * @throws InvalidArgumentException If the slug argument is not a string. - * @return self */ - public function setSlug($slug) + public function setSlug($slug): static { if ($slug === null) { $this->slug = null; @@ -258,9 +256,8 @@ public function setSlug($slug) * Set the locale of the object route. * * @param string $lang The route's locale. - * @return self */ - public function setLang($lang) + public function setLang($lang): static { $this->lang = $lang; @@ -272,9 +269,8 @@ public function setLang($lang) * * @param string|DateTimeInterface|null $time The date/time value. * @throws InvalidArgumentException If the date/time value is invalid. - * @return self */ - public function setCreationDate($time) + public function setCreationDate($time): static { if (empty($time) && !is_numeric($time)) { $this->creationDate = null; @@ -309,9 +305,8 @@ public function setCreationDate($time) * * @param string|DateTimeInterface|null $time The date/time value. * @throws InvalidArgumentException If the date/time value is invalid. - * @return self */ - public function setLastModificationDate($time) + public function setLastModificationDate($time): static { if (empty($time) && !is_numeric($time)) { $this->lastModificationDate = null; @@ -345,9 +340,8 @@ public function setLastModificationDate($time) * Set the foreign object type related to this route. * * @param string $type The object type. - * @return self */ - public function setRouteObjType($type) + public function setRouteObjType($type): static { $this->routeObjType = $type; @@ -358,9 +352,8 @@ public function setRouteObjType($type) * Set the foreign object ID related to this route. * * @param string $id The object ID. - * @return self */ - public function setRouteObjId($id) + public function setRouteObjId($id): static { $this->routeObjId = $id; @@ -371,9 +364,8 @@ public function setRouteObjId($id) * Set the foreign object's template identifier. * * @param string $template The template identifier. - * @return self */ - public function setRouteTemplate($template) + public function setRouteTemplate($template): static { $this->routeTemplate = $template; @@ -384,9 +376,8 @@ public function setRouteTemplate($template) * Customize the template's options. * * @param mixed $options Template options. - * @return self */ - public function setRouteOptions($options) + public function setRouteOptions($options): static { if (is_string($options)) { $options = json_decode($options, true); @@ -399,9 +390,8 @@ public function setRouteOptions($options) /** * @param string $routeOptionsIdent Template options ident. - * @return self */ - public function setRouteOptionsIdent($routeOptionsIdent) + public function setRouteOptionsIdent($routeOptionsIdent): static { $this->routeOptionsIdent = $routeOptionsIdent; @@ -498,9 +488,8 @@ public function getRouteOptionsIdent() /** * Alias of {@see self::slug()}. - * - * @return string */ + #[\Override] public function __toString(): string { return (string)$this->getSlug(); diff --git a/packages/object/src/Charcoal/Object/ObjectRouteInterface.php b/packages/object/src/Charcoal/Object/ObjectRouteInterface.php index 97b1263ab..3aba191b8 100644 --- a/packages/object/src/Charcoal/Object/ObjectRouteInterface.php +++ b/packages/object/src/Charcoal/Object/ObjectRouteInterface.php @@ -1,5 +1,7 @@ modelFactory = $factory; @@ -92,14 +82,13 @@ public function setModelFactory(FactoryInterface $factory) * Retrieve the object model factory. * * @throws RuntimeException If the model factory was not previously set. - * @return FactoryInterface */ - protected function modelFactory() + protected function modelFactory(): \Charcoal\Factory\FactoryInterface { - if (!isset($this->modelFactory)) { + if (!$this->modelFactory instanceof \Charcoal\Factory\FactoryInterface) { throw new RuntimeException(sprintf( 'Model Factory is not defined for "%s"', - get_class($this) + static::class )); } @@ -113,7 +102,7 @@ protected function modelFactory() * @throws InvalidArgumentException If the object type parameter is not a string. * @return ObjectScheduleInterface Chainable */ - public function setTargetType($targetType) + public function setTargetType($targetType): static { if (!is_string($targetType)) { throw new InvalidArgumentException( @@ -131,7 +120,7 @@ public function setTargetType($targetType) * * @return string */ - public function getTargetType() + public function getTargetType(): ?string { return $this->targetType; } @@ -142,7 +131,7 @@ public function getTargetType() * @param mixed $targetId The object ID. * @return ObjectScheduleInterface Chainable */ - public function setTargetId($targetId) + public function setTargetId($targetId): static { $this->targetId = $targetId; @@ -163,7 +152,7 @@ public function getTargetId() * @param array|string $data The data diff. * @return ObjectRevision */ - public function setDataDiff($data) + public function setDataDiff($data): static { if (!is_array($data)) { $data = json_decode($data, true); @@ -189,19 +178,17 @@ public function getDataDiff() * @param boolean $processed Whether the schedule has been processed. * @return ObjectScheduleInterface Chainable */ - public function setProcessed($processed) + public function setProcessed($processed): static { - $this->processed = !!$processed; + $this->processed = (bool) $processed; return $this; } /** * Determine if the schedule has been processed. - * - * @return boolean */ - public function getProcessed() + public function getProcessed(): bool { return $this->processed; } @@ -213,7 +200,7 @@ public function getProcessed() * @throws InvalidArgumentException If the date/time is invalid. * @return ObjectScheduleInterface Chainable */ - public function setScheduledDate($ts) + public function setScheduledDate($ts): static { if ($ts === null) { $this->scheduledDate = null; @@ -245,10 +232,8 @@ public function setScheduledDate($ts) /** * Retrieve the date/time the item should be processed at. - * - * @return null|DateTimeInterface */ - public function getScheduledDate() + public function getScheduledDate(): ?\DateTimeInterface { return $this->scheduledDate; } @@ -260,7 +245,7 @@ public function getScheduledDate() * @throws InvalidArgumentException If the date/time is invalid. * @return ObjectScheduleInterface Chainable */ - public function setProcessedDate($ts) + public function setProcessedDate($ts): static { if ($ts === null) { $this->processedDate = null; @@ -292,10 +277,8 @@ public function setProcessedDate($ts) /** * Retrieve the date/time the item was processed at. - * - * @return null|DateTimeInterface */ - public function getProcessedDate() + public function getProcessedDate(): ?\DateTimeInterface { return $this->processedDate; } @@ -304,10 +287,9 @@ public function getProcessedDate() * Hook called before saving the item. * * Presets the item as _to-be_ processed and queued now. - * - * @return boolean */ - protected function preSave() + #[\Override] + protected function preSave(): bool { parent::preSave(); @@ -325,12 +307,12 @@ protected function preSave() * @return boolean|null Success / Failure, or null in case of a skipped item. */ public function process( - callable $callback = null, - callable $successCallback = null, - callable $failureCallback = null + ?callable $callback = null, + ?callable $successCallback = null, + ?callable $failureCallback = null ) { - if ($this->getProcessed() === true) { + if ($this->getProcessed()) { // Do not process twice, ever. return null; } @@ -369,14 +351,11 @@ public function process( $this->setProcessed(true); $this->setProcessedDate('now'); $this->update([ 'processed', 'processed_date' ]); - if ($successCallback !== null) { $successCallback($this); } - } else { - if ($failureCallback !== null) { - $failureCallback($this); - } + } elseif ($failureCallback !== null) { + $failureCallback($this); } if ($callback !== null) { diff --git a/packages/object/src/Charcoal/Object/ObjectScheduleInterface.php b/packages/object/src/Charcoal/Object/ObjectScheduleInterface.php index 6ae5ae05a..bd271a1bc 100644 --- a/packages/object/src/Charcoal/Object/ObjectScheduleInterface.php +++ b/packages/object/src/Charcoal/Object/ObjectScheduleInterface.php @@ -1,5 +1,7 @@ publishDateStatus() === static::STATUS_PUBLISHED); } diff --git a/packages/object/src/Charcoal/Object/RevisionableInterface.php b/packages/object/src/Charcoal/Object/RevisionableInterface.php index 6b60497ed..b8be26ec7 100644 --- a/packages/object/src/Charcoal/Object/RevisionableInterface.php +++ b/packages/object/src/Charcoal/Object/RevisionableInterface.php @@ -1,5 +1,7 @@ revisionEnabled = !!$enabled; + $this->revisionEnabled = (bool) $enabled; return $this; } @@ -49,18 +49,14 @@ public function getRevisionEnabled() /** * Create a revision collection loader. - * - * @return CollectionLoader */ - public function createRevisionObjectCollectionLoader() + public function createRevisionObjectCollectionLoader(): \Charcoal\Loader\CollectionLoader { - $loader = new CollectionLoader([ + return new CollectionLoader([ 'logger' => $this->logger, 'factory' => $this->modelFactory(), 'model' => $this->getRevisionObjectPrototype(), ]); - - return $loader; } /** @@ -70,9 +66,7 @@ public function createRevisionObjectCollectionLoader() */ public function createRevisionObject() { - $rev = $this->modelFactory()->create($this->getObjectRevisionClass()); - - return $rev; + return $this->modelFactory()->create($this->getObjectRevisionClass()); } /** @@ -82,9 +76,7 @@ public function createRevisionObject() */ public function getRevisionObjectPrototype() { - $proto = $this->modelFactory()->get($this->getObjectRevisionClass()); - - return $proto; + return $this->modelFactory()->get($this->getObjectRevisionClass()); } /** @@ -149,9 +141,8 @@ public function generateRevision() public function latestRevision() { $rev = $this->createRevisionObject(); - $rev = $rev->lastObjectRevision($this); - return $rev; + return $rev->lastObjectRevision($this); } /** @@ -165,9 +156,8 @@ public function latestRevision() public function revisionNum($revNum) { $rev = $this->createRevisionObject(); - $rev = $rev->objectRevisionNum($this, intval($revNum)); - return $rev; + return $rev->objectRevisionNum($this, intval($revNum)); } /** @@ -176,7 +166,7 @@ public function revisionNum($revNum) * @param callable $callback Optional object callback. * @return array */ - public function allRevisions(callable $callback = null) + public function allRevisions(?callable $callback = null) { $loader = $this->createRevisionObjectCollectionLoader(); $loader @@ -205,7 +195,7 @@ public function allRevisions(callable $callback = null) * @throws InvalidArgumentException If revision number is invalid. * @return boolean Success / Failure. */ - public function revertToRevision($revNum) + public function revertToRevision($revNum): bool { if (!$revNum) { throw new InvalidArgumentException( diff --git a/packages/object/src/Charcoal/Object/RoutableInterface.php b/packages/object/src/Charcoal/Object/RoutableInterface.php index 96dfbccc5..c4296c560 100644 --- a/packages/object/src/Charcoal/Object/RoutableInterface.php +++ b/packages/object/src/Charcoal/Object/RoutableInterface.php @@ -1,5 +1,7 @@ isSlugEditable === null) { $metadata = $this->metadata(); - if (isset($metadata['routable']['editable'])) { - $this->isSlugEditable = !!$metadata['routable']['editable']; - } else { - $this->isSlugEditable = false; - } + $this->isSlugEditable = isset($metadata['routable']['editable']) && (bool) $metadata['routable']['editable']; } return $this->isSlugEditable; @@ -191,18 +187,15 @@ public function setSlug($slug) $slug = $this->translator()->translation($slug); if ($slug !== null) { $this->slug = $slug; - $values = $this->slug->data(); foreach ($values as $lang => $val) { $this->slug[$lang] = $this->slugify($val); } - } else { + } elseif (isset($_POST['slug'])) { /** @todo Hack used for regenerating route */ - if (isset($_POST['slug'])) { - $this->slug = []; - } else { - $this->slug = null; - } + $this->slug = []; + } else { + $this->slug = null; } return $this; @@ -240,7 +233,7 @@ public function generateSlug() $newSlug[$lang] = $curSlug[$lang]; } else { $newSlug[$lang] = $this->generateRoutePattern($pattern); - if (!strlen($newSlug[$lang])) { + if ((string) $newSlug[$lang] === '') { throw new UnexpectedValueException(sprintf( 'The slug is empty. The pattern is "%s"', $pattern @@ -274,9 +267,9 @@ public function generateSlug() * @param string $pattern The slug pattern. * @return string Returns the generated route. */ - protected function generateRoutePattern($pattern) + protected function generateRoutePattern(string $pattern) { - if ($this instanceof ViewableInterface && $this->view() !== null) { + if ($this instanceof ViewableInterface && $this->view() instanceof \Charcoal\View\ViewInterface) { $route = $this->view()->renderTemplate($pattern, $this->viewController()); } else { $route = preg_replace_callback('~\{\{\s*(.*?)\s*\}\}~i', [ $this, 'parseRouteToken' ], $pattern); @@ -294,14 +287,14 @@ protected function generateRoutePattern($pattern) * @throws InvalidArgumentException If a route token is not a string. * @return string */ - protected function parseRouteToken($token) + protected function parseRouteToken($token): string|float|int { // Processes matches from a regular expression operation if (is_array($token) && isset($token[1])) { $token = $token[1]; } - $token = trim($token); + $token = trim((string) $token); $method = [ $this, $token ]; if (is_callable($method)) { @@ -318,8 +311,8 @@ protected function parseRouteToken($token) throw new InvalidArgumentException(sprintf( 'Route token "%1$s" must be a string with %2$s; received %3$s', $token, - get_called_class(), - (is_object($value) ? get_class($value) : gettype($value)) + static::class, + (get_debug_type($value)) )); } @@ -374,9 +367,9 @@ protected function generateObjectRoute($slug = null, array $data = []) } else { throw new InvalidArgumentException(sprintf( '[%s] slug parameter must be an instance of %s, received %s', - get_called_class() . '::' . __FUNCTION__, + static::class . '::' . __FUNCTION__, Translation::class, - is_object($slug) ? get_class($slug) : gettype($slug) + get_debug_type($slug) )); } @@ -453,7 +446,7 @@ protected function getLatestObjectRoute($lang = null) } elseif (!in_array($lang, $this->translator()->availableLocales())) { throw new InvalidArgumentException(sprintf( 'Invalid language, received %s', - (is_object($lang) ? get_class($lang) : gettype($lang)) + (get_debug_type($lang)) )); } @@ -491,7 +484,7 @@ protected function getLatestObjectRoute($lang = null) $collection = $loader->load()->objects(); - if (!count($collection)) { + if (count($collection) === 0) { return $this->createRouteObject(); } @@ -515,9 +508,7 @@ public function url($lang = null) if ($slug) { return $slug; } - - $url = (string)$this->getLatestObjectRoute($lang)->getSlug(); - return $url; + return (string)$this->getLatestObjectRoute($lang)->getSlug(); } /** @@ -535,7 +526,7 @@ public function slugify($str) } $metadata = $this->metadata(); - $separator = isset($metadata['routable']['separator']) ? $metadata['routable']['separator'] : '-'; + $separator = $metadata['routable']['separator'] ?? '-'; $delimiters = '-_|'; $pregDelim = preg_quote($delimiters); $directories = '\\/'; @@ -545,41 +536,41 @@ public function slugify($str) $slug = preg_replace('![^(\p{L}|\p{N})(\s|\/)]!u', $separator, $str); if (!isset($metadata['routable']['lowercase']) || $metadata['routable']['lowercase'] === false) { - $slug = mb_strtolower($slug, 'UTF-8'); + $slug = mb_strtolower((string) $slug, 'UTF-8'); } // Strip HTML - $slug = strip_tags($slug); + $slug = strip_tags((string) $slug); // Remove diacritics $slug = htmlentities($slug, ENT_COMPAT, 'UTF-8'); $slug = preg_replace('!&([a-zA-Z])(uml|acute|grave|circ|tilde|cedil|ring);!', '$1', $slug); // Simplify ligatures - $slug = preg_replace('!&([a-zA-Z]{2})(lig);!', '$1', $slug); + $slug = preg_replace('!&([a-zA-Z]{2})(lig);!', '$1', (string) $slug); // Remove unescaped HTML characters $unescaped = '!&(raquo|laquo|rsaquo|lsaquo|rdquo|ldquo|rsquo|lsquo|hellip|amp|nbsp|quot|ordf|ordm);!'; - $slug = preg_replace($unescaped, '', $slug); + $slug = preg_replace($unescaped, '', (string) $slug); // Unify all dashes/underscores as one separator character $flip = ($separator === '-') ? '_' : '-'; - $slug = preg_replace('![' . preg_quote($flip) . ']+!u', $separator, $slug); + $slug = preg_replace('![' . preg_quote($flip) . ']+!u', $separator, (string) $slug); // Remove all whitespace and normalize delimiters - $slug = preg_replace('![_\|\s|\(\)]+!', $separator, $slug); + $slug = preg_replace('![_\|\s|\(\)]+!', $separator, (string) $slug); // Squeeze multiple delimiters and whitespace with a single separator - $slug = preg_replace('![' . $pregDelim . '\s]{2,}!', $separator, $slug); + $slug = preg_replace('![' . $pregDelim . '\s]{2,}!', $separator, (string) $slug); // Squeeze multiple URI path delimiters - $slug = preg_replace('![' . $pregDir . ']{2,}!', $separator, $slug); + $slug = preg_replace('![' . $pregDir . ']{2,}!', $separator, (string) $slug); // Remove delimiters surrouding URI path delimiters - $slug = preg_replace('!(?<=[' . $pregDir . '])[' . $pregDelim . ']|[' . $pregDelim . '](?=[' . $pregDir . '])!', '', $slug); + $slug = preg_replace('!(?<=[' . $pregDir . '])[' . $pregDelim . ']|[' . $pregDelim . '](?=[' . $pregDir . '])!', '', (string) $slug); // Strip leading and trailing dashes or underscores - $slug = trim($slug, $delimiters); + $slug = trim((string) $slug, $delimiters); // Cache the slugified string $sluggedArray[$str] = $slug; @@ -594,9 +585,8 @@ public function slugify($str) * * @param string $slug A slug. * @throws UnexpectedValueException If the slug affixes are invalid. - * @return string */ - protected function finalizeSlug($slug) + protected function finalizeSlug($slug): string { $prefix = $this->slugPrefix(); if ($prefix) { @@ -604,7 +594,7 @@ protected function finalizeSlug($slug) if ($slug === $prefix) { throw new UnexpectedValueException('The slug is the same as the prefix.'); } - $slug = $prefix . preg_replace('!^' . preg_quote($prefix) . '\b!', '', $slug); + $slug = $prefix . preg_replace('!^' . preg_quote((string) $prefix) . '\b!', '', $slug); } $suffix = $this->slugSuffix(); @@ -613,12 +603,10 @@ protected function finalizeSlug($slug) if ($slug === $suffix) { throw new UnexpectedValueException('The slug is the same as the suffix.'); } - $slug = preg_replace('!\b' . preg_quote($suffix) . '$!', '', $slug) . $suffix; + $slug = preg_replace('!\b' . preg_quote((string) $suffix) . '$!', '', $slug) . $suffix; } - $slug = rtrim($slug, '/'); - - return $slug; + return rtrim($slug, '/'); } /** @@ -628,7 +616,7 @@ protected function finalizeSlug($slug) * * @return boolean Success or failure. */ - protected function deleteObjectRoutes() + protected function deleteObjectRoutes(): bool { if (!$this->objType()) { return false; @@ -661,18 +649,14 @@ protected function deleteObjectRoutes() /** * Create a route collection loader. - * - * @return CollectionLoader */ - public function createRouteObjectCollectionLoader() + public function createRouteObjectCollectionLoader(): \Charcoal\Loader\CollectionLoader { - $loader = new CollectionLoader([ + return new CollectionLoader([ 'logger' => $this->logger, 'factory' => $this->modelFactory(), 'model' => $this->getRouteObjectPrototype(), ]); - - return $loader; } /** @@ -682,9 +666,7 @@ public function createRouteObjectCollectionLoader() */ public function createRouteObject() { - $route = $this->modelFactory()->create($this->getObjectRouteClass()); - - return $route; + return $this->modelFactory()->create($this->getObjectRouteClass()); } /** @@ -694,9 +676,7 @@ public function createRouteObject() */ public function getRouteObjectPrototype() { - $proto = $this->modelFactory()->get($this->getObjectRouteClass()); - - return $proto; + return $this->modelFactory()->get($this->getObjectRouteClass()); } /** @@ -799,7 +779,7 @@ public function routeOptionsIdent() public function isActiveRoute() { if (isset($this['active'])) { - return !!$this['active']; + return (bool) $this['active']; } else { return true; } diff --git a/packages/object/src/Charcoal/Object/TimestampableInterface.php b/packages/object/src/Charcoal/Object/TimestampableInterface.php index 5ce206bcc..08daadb38 100644 --- a/packages/object/src/Charcoal/Object/TimestampableInterface.php +++ b/packages/object/src/Charcoal/Object/TimestampableInterface.php @@ -1,5 +1,7 @@ ip = null; @@ -93,10 +89,8 @@ public function setIp($ip) /** * Retrieve the client IP address. - * - * @return integer|null */ - public function getIp() + public function getIp(): ?int { return $this->ip; } @@ -106,16 +100,13 @@ public function getIp() * * @param string $lang The language code. * @throws InvalidArgumentException If the argument is not a string. - * @return self */ - public function setLang($lang) + public function setLang($lang): static { - if ($lang !== null) { - if (!is_string($lang)) { - throw new InvalidArgumentException( - 'Language must be a string' - ); - } + if ($lang !== null && !is_string($lang)) { + throw new InvalidArgumentException( + 'Language must be a string' + ); } $this->lang = $lang; @@ -138,16 +129,13 @@ public function getLang() * * @param string $origin The source URL or identifier of the submission. * @throws InvalidArgumentException If the argument is not a string. - * @return self */ - public function setOrigin($origin) + public function setOrigin($origin): static { - if ($origin !== null) { - if (!is_string($origin)) { - throw new InvalidArgumentException( - 'Origin must be a string.' - ); - } + if ($origin !== null && !is_string($origin)) { + throw new InvalidArgumentException( + 'Origin must be a string.' + ); } $this->origin = $origin; @@ -173,9 +161,8 @@ public function resolveOrigin() $uri .= '://' . $host; } - $uri .= getenv('REQUEST_URI'); - return $uri; + return $uri . getenv('REQUEST_URI'); } /** @@ -195,9 +182,8 @@ public function getOrigin() * NULL is accepted and instances of DateTimeInterface are recommended; * any other value will be converted (if possible) into one. * @throws InvalidArgumentException If the timestamp is invalid. - * @return self */ - public function setTs($timestamp) + public function setTs($timestamp): static { if ($timestamp === null) { $this->ts = null; @@ -228,10 +214,8 @@ public function setTs($timestamp) /** * Retrieve the creation timestamp. - * - * @return DateTimeInterface|null */ - public function getTs() + public function getTs(): ?\DateTimeInterface { return $this->ts; } @@ -242,6 +226,7 @@ public function getTs() * @see Charcoal\Source\StorableTrait::preSave() For the "create" Event. * @return boolean */ + #[\Override] protected function preSave() { $result = parent::preSave(); @@ -252,7 +237,7 @@ protected function preSave() $this->setIp(getenv('REMOTE_ADDR')); } - if (!isset($this->origin)) { + if ($this->origin === null) { $this->setOrigin($this->resolveOrigin()); } diff --git a/packages/object/src/Charcoal/Object/UserDataInterface.php b/packages/object/src/Charcoal/Object/UserDataInterface.php index 71b0958a6..d75ed56b3 100644 --- a/packages/object/src/Charcoal/Object/UserDataInterface.php +++ b/packages/object/src/Charcoal/Object/UserDataInterface.php @@ -1,5 +1,7 @@ obj = $this->getMockForTrait(ArchivableTrait::class); } - /** - * @return void - */ - public function testConstructor() + public function testConstructor(): void { $this->assertTrue(true); } diff --git a/packages/object/tests/Charcoal/Object/CategorizableTraitTest.php b/packages/object/tests/Charcoal/Object/CategorizableTraitTest.php index 80cb10288..28b033d50 100644 --- a/packages/object/tests/Charcoal/Object/CategorizableTraitTest.php +++ b/packages/object/tests/Charcoal/Object/CategorizableTraitTest.php @@ -14,25 +14,18 @@ class CategorizableTraitTest extends AbstractTestCase { /** * Tested Class. - * - * @var CategorizableTrait */ - private $obj; + private \Charcoal\Object\CategorizableTrait $obj; /** * Set up the test. - * - * @return void */ public function setUp(): void { $this->obj = $this->getMockForTrait(CategorizableTrait::class); } - /** - * @return void - */ - public function testSetCategoryType() + public function testSetCategoryType(): void { $obj = $this->obj; $this->assertNull($obj->getCategoryType()); diff --git a/packages/object/tests/Charcoal/Object/CategoryTraitTest.php b/packages/object/tests/Charcoal/Object/CategoryTraitTest.php index 205f53bf7..fedac268b 100644 --- a/packages/object/tests/Charcoal/Object/CategoryTraitTest.php +++ b/packages/object/tests/Charcoal/Object/CategoryTraitTest.php @@ -22,10 +22,7 @@ public function createTrait() return $this->getMockForTrait(CategoryTrait::class); } - /** - * @return void - */ - public function testUnsetCategoryItemTypeThrowsException() + public function testUnsetCategoryItemTypeThrowsException(): void { $mock = $this->createTrait(); @@ -33,10 +30,7 @@ public function testUnsetCategoryItemTypeThrowsException() $mock->getCategoryItemType(); } - /** - * @return void - */ - public function testSetCategoryItemType() + public function testSetCategoryItemType(): void { $mock = $this->createTrait(); @@ -48,10 +42,7 @@ public function testSetCategoryItemType() $mock->setCategoryItemType(false); } - /** - * @return void - */ - public function testNumCategoryItems() + public function testNumCategoryItems(): void { $mock = $this->createTrait(); $mock->expects($this->any()) @@ -68,10 +59,7 @@ public function testNumCategoryItems() $this->assertEquals(1, $mock->getNumCategoryItems()); } - /** - * @return void - */ - public function testHasCategoryItems() + public function testHasCategoryItems(): void { $mock = $this->createTrait(); $mock->expects($this->any()) diff --git a/packages/object/tests/Charcoal/Object/ContainerProvider.php b/packages/object/tests/Charcoal/Object/ContainerProvider.php index b930fa1a8..a718e72ab 100644 --- a/packages/object/tests/Charcoal/Object/ContainerProvider.php +++ b/packages/object/tests/Charcoal/Object/ContainerProvider.php @@ -34,9 +34,8 @@ class ContainerProvider * Register the unit tests required services. * * @param Container $container A DI container. - * @return void */ - public function registerBaseServices(Container $container) + public function registerBaseServices(Container $container): void { $this->registerDatabase($container); $this->registerLogger($container); @@ -50,11 +49,10 @@ public function registerBaseServices(Container $container) * Note: Uses SQLite to create a database in memory. * * @param Container $container A DI container. - * @return void */ - public function registerDatabase(Container $container) + public function registerDatabase(Container $container): void { - $container['database'] = function () { + $container['database'] = function (): \PDO { $pdo = new PDO('sqlite::memory:'); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); return $pdo; @@ -65,167 +63,141 @@ public function registerDatabase(Container $container) * Setup the application's logging interface. * * @param Container $container A DI container. - * @return void */ - public function registerLogger(Container $container) + public function registerLogger(Container $container): void { - $container['logger'] = function () { - return new NullLogger(); - }; + $container['logger'] = (fn(): \Psr\Log\NullLogger => new NullLogger()); } /** * Setup the application's caching interface. * * @param Container $container A DI container. - * @return void */ - public function registerCache(Container $container) + public function registerCache(Container $container): void { - $container['cache'] = function () { - return new Pool(); - }; + $container['cache'] = (fn(): \Stash\Pool => new Pool()); } /** * Setup the framework's metadata loader interface. * * @param Container $container A DI container. - * @return void */ - public function registerMetadataLoader(Container $container) + public function registerMetadataLoader(Container $container): void { - $container['metadata/loader'] = function (Container $container) { - return new MetadataLoader([ - 'cache' => $container['cache'], - 'logger' => $container['logger'], - 'base_path' => realpath(__DIR__ . '/../../../'), - 'paths' => [ - 'metadata', - // Standalone repo - 'vendor/charcoal/property/metadata', - // Monorepo - '/../property/metadata' - ] - ]); - }; + $container['metadata/loader'] = (fn(Container $container): \Charcoal\Model\Service\MetadataLoader => new MetadataLoader([ + 'cache' => $container['cache'], + 'logger' => $container['logger'], + 'base_path' => realpath(__DIR__ . '/../../../'), + 'paths' => [ + 'metadata', + // Standalone repo + 'vendor/charcoal/property/metadata', + // Monorepo + '/../property/metadata' + ] + ])); } /** * Setup the framework's data source factory. * * @param Container $container A DI container. - * @return void */ - public function registerSourceFactory(Container $container) + public function registerSourceFactory(Container $container): void { $this->registerLogger($container); $this->registerCache($container); $this->registerDatabase($container); - $container['source/factory'] = function ($container) { - return new Factory([ - 'map' => [ - 'database' => DatabaseSource::class - ], - 'arguments' => [[ - 'logger' => $container['logger'], - 'cache' => $container['cache'], - 'pdo' => $container['database'] - ]] - ]); - }; + $container['source/factory'] = (fn($container): \Charcoal\Factory\GenericFactory => new Factory([ + 'map' => [ + 'database' => DatabaseSource::class + ], + 'arguments' => [[ + 'logger' => $container['logger'], + 'cache' => $container['cache'], + 'pdo' => $container['database'] + ]] + ])); } /** * Setup the framework's model factory. * * @param Container $container A DI container. - * @return void */ - public function registerModelFactory(Container $container) + public function registerModelFactory(Container $container): void { $this->registerSourceFactory($container); $this->registerMetadataLoader($container); $this->registerPropertyFactory($container); - $container['model/factory'] = function ($container) { - return new Factory([ - 'arguments' => [[ - 'container' => $container, - 'logger' => $container['logger'], - 'metadata_loader' => $container['metadata/loader'], - 'source_factory' => $container['source/factory'], - 'property_factory' => $container['property/factory'] - ]] - ]); - }; + $container['model/factory'] = (fn($container): \Charcoal\Factory\GenericFactory => new Factory([ + 'arguments' => [[ + 'container' => $container, + 'logger' => $container['logger'], + 'metadata_loader' => $container['metadata/loader'], + 'source_factory' => $container['source/factory'], + 'property_factory' => $container['property/factory'] + ]] + ])); } /** * Setup the framework's property factory. * * @param Container $container A DI container. - * @return void */ - public function registerPropertyFactory(Container $container) + public function registerPropertyFactory(Container $container): void { $this->registerLogger($container); $this->registerDatabase($container); $this->registerTranslator($container); - $container['property/factory'] = function (Container $container) { - return new Factory([ - 'resolver_options' => [ - 'prefix' => '\\Charcoal\\Property\\', - 'suffix' => 'Property' - ], - 'arguments' => [[ - 'container' => $container, - 'database' => $container['database'], - 'logger' => $container['logger'], - 'translator' => $container['translator'] - ]] - ]); - }; + $container['property/factory'] = (fn(Container $container): \Charcoal\Factory\GenericFactory => new Factory([ + 'resolver_options' => [ + 'prefix' => '\\Charcoal\\Property\\', + 'suffix' => 'Property' + ], + 'arguments' => [[ + 'container' => $container, + 'database' => $container['database'], + 'logger' => $container['logger'], + 'translator' => $container['translator'] + ]] + ])); } /** * Setup the framework's collection loader interface. * * @param Container $container A DI container. - * @return void */ - public function registerModelCollectionLoader(Container $container) + public function registerModelCollectionLoader(Container $container): void { - $container['model/collection/loader'] = function (Container $container) { - return new CollectionLoader([ - 'logger' => $container['logger'], - 'cache' => $container['cache'] - ]); - }; + $container['model/collection/loader'] = (fn(Container $container): \Charcoal\Loader\CollectionLoader => new CollectionLoader([ + 'logger' => $container['logger'], + 'cache' => $container['cache'] + ])); } /** * Setup the framework's Translator. * * @param Container $container A DI container. - * @return void */ - public function registerTranslator(Container $container) + public function registerTranslator(Container $container): void { - $container['locales/manager'] = function () { - return new LocalesManager([ - 'locales' => [ - 'en' => [ 'locale' => 'en-US' ] - ] - ]); - }; - - $container['translator'] = function (Container $container) { - return new Translator([ - 'manager' => $container['locales/manager'] - ]); - }; + $container['locales/manager'] = (fn(): \Charcoal\Translator\LocalesManager => new LocalesManager([ + 'locales' => [ + 'en' => [ 'locale' => 'en-US' ] + ] + ])); + + $container['translator'] = (fn(Container $container): \Charcoal\Translator\Translator => new Translator([ + 'manager' => $container['locales/manager'] + ])); } } diff --git a/packages/object/tests/Charcoal/Object/ContentTest.php b/packages/object/tests/Charcoal/Object/ContentTest.php index d9f3e68c5..0b1adb6f6 100644 --- a/packages/object/tests/Charcoal/Object/ContentTest.php +++ b/packages/object/tests/Charcoal/Object/ContentTest.php @@ -19,22 +19,16 @@ class ContentTest extends AbstractTestCase { /** * Tested Class. - * - * @var Content */ - private $obj; + private \Charcoal\Object\Content $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ public function setUp(): void { @@ -43,10 +37,7 @@ public function setUp(): void $this->obj = $container['model/factory']->create(Content::class); } - /** - * @return void - */ - public function testDefaults() + public function testDefaults(): void { $this->assertTrue($this->obj['active']); $this->assertEquals(0, $this->obj['position']); @@ -64,10 +55,7 @@ public function testDefaults() $this->assertTrue($this->obj['revisionEnabled']); } - /** - * @return void - */ - public function testSetData() + public function testSetData(): void { $ret = $this->obj->setData([ 'active' => false, @@ -90,10 +78,7 @@ public function testSetData() $this->assertEquals(['foo', 'bar'], $this->obj['requiredAclPermissions']); } - /** - * @return void - */ - public function testSetActive() + public function testSetActive(): void { $this->assertTrue($this->obj['active']); $ret = $this->obj->setActive(false); @@ -110,12 +95,8 @@ public function testSetActive() $this->assertTrue($this->obj['active']); } - /** - * @return void - */ - public function testSetPosition() + public function testSetPosition(): void { - $this->obj = $this->obj; $this->assertEquals(0, $this->obj['position']); $ret = $this->obj->setPosition(42); $this->assertSame($ret, $this->obj); @@ -134,10 +115,7 @@ public function testSetPosition() $this->obj->setPosition('foo'); } - /** - * @return void - */ - public function testSetCreated() + public function testSetCreated(): void { $ret = $this->obj->setCreated('2015-01-01 13:05:45'); $this->assertSame($ret, $this->obj); @@ -156,19 +134,13 @@ public function testSetCreated() $this->obj->setCreated(false); } - /** - * @return void - */ - public function testSetCreatedInvalidDate() + public function testSetCreatedInvalidDate(): void { $this->expectException('\Exception'); $this->obj->setCreated('foo.bar'); } - /** - * @return void - */ - public function testSetCreatedBy() + public function testSetCreatedBy(): void { $ret = $this->obj->setCreatedBy('Me'); $this->assertSame($ret, $this->obj); @@ -178,10 +150,7 @@ public function testSetCreatedBy() //$this->obj->setCreatedBy(false); } - /** - * @return void - */ - public function testSetLastModified() + public function testSetLastModified(): void { $ret = $this->obj->setLastModified('2015-01-01 13:05:45'); $this->assertSame($ret, $this->obj); @@ -200,19 +169,13 @@ public function testSetLastModified() $this->obj->setLastModified(false); } - /** - * @return void - */ - public function testSetLastModifiedInvalidDate() + public function testSetLastModifiedInvalidDate(): void { $this->expectException('\Exception'); $this->obj->setLastModified('foo.bar'); } - /** - * @return void - */ - public function testSetLastModifiedBy() + public function testSetLastModifiedBy(): void { $ret = $this->obj->setLastModifiedBy('Me'); $this->assertSame($ret, $this->obj); @@ -222,10 +185,7 @@ public function testSetLastModifiedBy() //$this->obj->setLastModifiedBy(false); } - /** - * @return void - */ - public function testSetRequiredAclPermissions() + public function testSetRequiredAclPermissions(): void { $ret = $this->obj->setRequiredAclPermissions(['a', 'b', 'c']); $this->assertSame($ret, $this->obj); @@ -247,12 +207,10 @@ public function testSetRequiredAclPermissions() /** * Set up the service container. - * - * @return Container */ - private function container() + private function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerBaseServices($container); diff --git a/packages/object/tests/Charcoal/Object/HierarchicalTraitTest.php b/packages/object/tests/Charcoal/Object/HierarchicalTraitTest.php index 69f38fbed..b8bffa36e 100644 --- a/packages/object/tests/Charcoal/Object/HierarchicalTraitTest.php +++ b/packages/object/tests/Charcoal/Object/HierarchicalTraitTest.php @@ -18,22 +18,17 @@ class HierarchicalTraitTest extends AbstractTestCase * * @var Hierarchical */ - private $obj; + private \Charcoal\Tests\Object\Mocks\HierarchicalClass $obj; /** * Set up the test. - * - * @return void */ public function setUp(): void { $this->obj = new HierarchicalObject(); } - /** - * @return void - */ - public function testSetMaster() + public function testSetMaster(): void { $obj = $this->obj; // $master = $this->createMock(get_class($obj)); @@ -43,49 +38,37 @@ public function testSetMaster() $this->assertSame($master, $obj->getMaster()); } - /** - * @return void - */ - public function testHasMaster() + public function testHasMaster(): void { $obj = $this->obj; $this->assertFalse($obj->hasMaster()); - $master = $this->createMock(get_class($obj)); + $master = $this->createStub($obj::class); $obj->setMaster($master); $this->assertTrue($obj->hasMaster()); } - /** - * @return void - */ - public function testIsTopLevel() + public function testIsTopLevel(): void { $obj = $this->obj; $this->assertTrue($obj->isTopLevel()); - $master = $this->createMock(get_class($obj)); + $master = $this->createStub($obj::class); $obj->setMaster($master); $this->assertFalse($obj->isTopLevel()); } - /** - * @return void - */ - public function testIsLastLevel() + public function testIsLastLevel(): void { $obj = $this->obj; $this->assertTrue($obj->isLastLevel()); - $children = array_fill(0, 4, $this->createMock(get_class($obj))); + $children = array_fill(0, 4, $this->createMock($obj::class)); $obj->setChildren($children); $this->assertFalse($obj->isLastLevel()); } - /** - * @return void - */ - public function testHierarchyLevel() + public function testHierarchyLevel(): void { $obj = $this->obj; @@ -93,7 +76,7 @@ public function testHierarchyLevel() $master = clone $obj; $master2 = clone $obj; - $children = array_fill(0, 4, $this->createMock(get_class($obj))); + $children = array_fill(0, 4, $this->createMock($obj::class)); $obj->setMaster($master); $obj->setChildren($children); @@ -107,17 +90,14 @@ public function testHierarchyLevel() $this->assertEquals(3, $obj->hierarchyLevel()); } - /** - * @return void - */ - public function testToplevelMaster() + public function testToplevelMaster(): void { $obj = $this->obj; $this->assertSame(null, $obj->toplevelMaster()); - $master1 = $this->createMock(get_class($obj)); - $master2 = $this->createMock(get_class($obj)); + $master1 = $this->createMock($obj::class); + $master2 = $this->createMock($obj::class); $obj->setMaster($master1->id()); // No longer easily testable because of modelLoader. @@ -129,16 +109,13 @@ public function testToplevelMaster() // $this->assertSame($master2, $obj->toplevelMaster()); } - /** - * @return void - */ - public function testHierarchy() + public function testHierarchy(): void { - $obj = $this->createPartialMock(get_class($this->obj), ['getMasterObject']); + $obj = $this->createPartialMock($this->obj::class, ['getMasterObject']); $this->assertEquals([], $obj->hierarchy()); - $master1 = $this->createPartialMock(get_class($this->obj), ['getMasterObject']); - $master2 = $this->createTestProxy(get_class($this->obj)); + $master1 = $this->createPartialMock($this->obj::class, ['getMasterObject']); + $master2 = $this->createTestProxy($this->obj::class); $obj->setMaster($master1->getId()); $obj->method('getMasterObject')->willReturn($master1); @@ -152,10 +129,7 @@ public function testHierarchy() $this->assertSame([$master1, $master2], $obj->hierarchy()); } - /** - * @return void - */ - public function testInvertedHierarchy() + public function testInvertedHierarchy(): void { $obj = $this->obj; @@ -172,13 +146,10 @@ public function testInvertedHierarchy() $this->assertSame([$master2, $master1], $obj->invertedHierarchy()); } - /** - * @return void - */ - public function testIsMasterOf() + public function testIsMasterOf(): void { $obj = $this->obj; - $master = $this->createTestProxy(get_class($obj)); + $master = $this->createTestProxy($obj::class); $this->assertFalse($master->isMasterOf($obj)); $obj->setMaster($master->getId()); @@ -186,57 +157,45 @@ public function testIsMasterOf() $this->assertFalse($obj->isMasterOf($master)); } - /** - * @return void - */ - public function testHasChildren() + public function testHasChildren(): void { $obj = $this->obj; $this->assertFalse($obj->hasChildren()); - $children = array_fill(0, 4, $this->createMock(get_class($obj))); + $children = array_fill(0, 4, $this->createMock($obj::class)); $obj->setChildren($children); $this->assertTrue($obj->hasChildren()); } - /** - * @return void - */ - public function testNumChildren() + public function testNumChildren(): void { $obj = $this->obj; $this->assertEquals(0, $obj->numChildren()); - $children = array_fill(0, 4, $this->createMock(get_class($obj))); + $children = array_fill(0, 4, $this->createMock($obj::class)); $obj->setChildren($children); $this->assertEquals(4, $obj->numChildren()); - $child5 = $this->createMock(get_class($obj)); + $child5 = $this->createStub($obj::class); $obj->addChild($child5); $this->assertEquals(5, $obj->numChildren()); } - /** - * @return void - */ - public function testIsChildOf() + public function testIsChildOf(): void { $obj = $this->obj; - $master = $this->createTestProxy(get_class($obj)); + $master = $this->createTestProxy($obj::class); $this->assertFalse($obj->isChildOf($master)); $obj->setMaster($master->getId()); $this->assertTrue($obj->isChildOf($master)); } - /** - * @return void - */ - public function testRecurisveIsChildOf() + public function testRecurisveIsChildOf(): void { $obj = $this->obj; - $master = $this->createTestProxy(get_class($obj)); + $master = $this->createTestProxy($obj::class); $this->assertFalse($obj->isChildOf($master)); $obj->setMaster($master->getId()); diff --git a/packages/object/tests/Charcoal/Object/Mocks/AbstractModel.php b/packages/object/tests/Charcoal/Object/Mocks/AbstractModel.php index 3455f735b..b7d68e94e 100644 --- a/packages/object/tests/Charcoal/Object/Mocks/AbstractModel.php +++ b/packages/object/tests/Charcoal/Object/Mocks/AbstractModel.php @@ -39,7 +39,7 @@ abstract public static function objType(); /** * @param array|null $data The model dependencies. */ - public function __construct(array $data = null) + public function __construct(?array $data = null) { if (isset($data['factory'])) { $this->setModelFactory($data['factory']); @@ -155,7 +155,7 @@ public function setData(array $data) * @param array|null $filters Retrieve a subset. * @return array */ - public function data(array $filters = null) + public function data(?array $filters = null) { return null; } @@ -188,9 +188,8 @@ public function defaultData() /** * @param array $propertyIdents Optional. List of property identifiers * for retrieving a subset of property objects. - * @return null */ - public function properties(array $propertyIdents = null) + public function properties(?array $propertyIdents = null) { return null; } diff --git a/packages/object/tests/Charcoal/Object/Mocks/HierarchicalClass.php b/packages/object/tests/Charcoal/Object/Mocks/HierarchicalClass.php index 4429d3cbf..e81f940e2 100644 --- a/packages/object/tests/Charcoal/Object/Mocks/HierarchicalClass.php +++ b/packages/object/tests/Charcoal/Object/Mocks/HierarchicalClass.php @@ -1,5 +1,7 @@ obj = $container['model/factory']->create(ObjectRevision::class); } - /** - * @return void - */ - public function testSetObjType() + public function testSetObjType(): void { $this->assertNull($this->obj['targetType']); $ret = $this->obj->setTargetType('foobar'); @@ -57,10 +48,7 @@ public function testSetObjType() $this->obj->setTargetType(false); } - /** - * @return void - */ - public function testSetObjId() + public function testSetObjId(): void { $this->assertNull($this->obj['targetId']); $ret = $this->obj->setTargetId(42); @@ -68,10 +56,7 @@ public function testSetObjId() $this->assertEquals(42, $this->obj['targetId']); } - /** - * @return void - */ - public function testSetRevNum() + public function testSetRevNum(): void { $this->assertNull($this->obj['revNum']); $ret = $this->obj->setRevNum(66); @@ -85,10 +70,7 @@ public function testSetRevNum() $this->obj->setRevNum([]); } - /** - * @return void - */ - public function testSetRevTs() + public function testSetRevTs(): void { $obj = $this->obj; $this->assertNull($obj['revTs']); @@ -104,10 +86,7 @@ public function testSetRevTs() $obj->setRevTs(false); } - /** - * @return void - */ - public function testSetRevUser() + public function testSetRevUser(): void { $this->assertNull($this->obj['revUser']); $ret = $this->obj->setRevUser('me'); @@ -121,10 +100,7 @@ public function testSetRevUser() $this->obj->setRevUser(false); } - /** - * @return void - */ - public function testSetDataPrev() + public function testSetDataPrev(): void { $this->assertNull($this->obj['dataPrev']); $ret = $this->obj->setDataPrev(['foo'=>1]); @@ -135,10 +111,7 @@ public function testSetDataPrev() $this->assertEquals([], $this->obj->setDataPrev(null)['dataPrev']); } - /** - * @return void - */ - public function testSetDataObj() + public function testSetDataObj(): void { $this->assertNull($this->obj['dataObj']); $ret = $this->obj->setDataObj(['foo'=>1]); @@ -149,10 +122,7 @@ public function testSetDataObj() $this->assertEquals([], $this->obj->setDataObj(null)['dataObj']); } - /** - * @return void - */ - public function testSetDataDiff() + public function testSetDataDiff(): void { $this->assertNull($this->obj['dataDiff']); $ret = $this->obj->setDataDiff(['foo'=>1]); @@ -163,10 +133,7 @@ public function testSetDataDiff() $this->assertEquals([], $this->obj->setDataDiff(null)['dataDiff']); } - /** - * @return void - */ - public function testCreateDiff() + public function testCreateDiff(): void { $this->assertEquals([], $this->obj->createDiff([], [])); $ret = $this->obj->createDiff(['foo'=>1], ['foo'=>2]); @@ -185,12 +152,10 @@ public function testCreateDiff() /** * Set up the service container. - * - * @return Container */ - private function container() + private function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerBaseServices($container); diff --git a/packages/object/tests/Charcoal/Object/ObjectRouteTest.php b/packages/object/tests/Charcoal/Object/ObjectRouteTest.php index 9451fe128..724bb4dcc 100644 --- a/packages/object/tests/Charcoal/Object/ObjectRouteTest.php +++ b/packages/object/tests/Charcoal/Object/ObjectRouteTest.php @@ -19,22 +19,16 @@ class ObjectRouteTest extends AbstractTestCase { /** * Tested Class. - * - * @var ObjectRoute */ - private $obj; + private \Charcoal\Object\ObjectRoute $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ public function setUp(): void { @@ -43,18 +37,12 @@ public function setUp(): void $this->obj = $container['model/factory']->create(ObjectRoute::class); } - /** - * @return void - */ - public function testDefaults() + public function testDefaults(): void { $this->assertNull($this->obj['id']); } - /** - * @return void - */ - public function testSetData() + public function testSetData(): void { $ret = $this->obj->setData([ 'id' => 42, @@ -82,10 +70,7 @@ public function testSetData() $this->assertEquals('baz', $this->obj->getRouteTemplate()); } - /** - * @return void - */ - public function testSetId() + public function testSetId(): void { $ret = $this->obj->setId(3); $this->assertSame($ret, $this->obj); @@ -98,18 +83,12 @@ public function testSetId() $this->assertEquals(10, $this->obj['id']); } - /** - * @return void - */ - public function testSetCreationDate() + public function testSetCreationDate(): void { $this->assertNull($this->obj->getCreationDate()); } - /** - * @return void - */ - public function testLastModificationDate() + public function testLastModificationDate(): void { $date = $this->obj->getLastModificationDate(); $this->obj->update(); @@ -118,10 +97,7 @@ public function testLastModificationDate() $this->assertIsBool($date2 > $date); } - /** - * @return void - */ - public function testLang() + public function testLang(): void { $ret = $this->obj->setLang('en'); $this->assertSame($ret, $this->obj); @@ -134,10 +110,7 @@ public function testLang() $this->assertEquals('jp', $this->obj['lang']); } - /** - * @return void - */ - public function testSetSlug() + public function testSetSlug(): void { $this->assertNull($this->obj['slug']); $ret = $this->obj->setSlug('foo'); @@ -159,12 +132,10 @@ public function testSetSlug() /** * Set up the service container. - * - * @return Container */ - private function container() + private function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerBaseServices($container); diff --git a/packages/object/tests/Charcoal/Object/ObjectScheduleTest.php b/packages/object/tests/Charcoal/Object/ObjectScheduleTest.php index 3a6a8a864..0516603f0 100644 --- a/packages/object/tests/Charcoal/Object/ObjectScheduleTest.php +++ b/packages/object/tests/Charcoal/Object/ObjectScheduleTest.php @@ -19,22 +19,16 @@ class ObjectScheduleTest extends AbstractTestCase { /** * Tested Class. - * - * @var ObjectSchedule */ - private $obj; + private \Charcoal\Object\ObjectSchedule $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ public function setUp(): void { @@ -43,10 +37,7 @@ public function setUp(): void $this->obj = $container['model/factory']->create(ObjectSchedule::class); } - /** - * @return void - */ - public function testSetTargetType() + public function testSetTargetType(): void { $this->assertNull($this->obj->getTargetType()); $ret = $this->obj->setTargetType('foobar'); @@ -57,10 +48,7 @@ public function testSetTargetType() $this->obj->setTargetType(false); } - /** - * @return void - */ - public function testSetTargetId() + public function testSetTargetId(): void { $this->assertNull($this->obj->getTargetId()); $ret = $this->obj->setTargetId(42); @@ -68,10 +56,7 @@ public function testSetTargetId() $this->assertEquals(42, $this->obj->getTargetId()); } - /** - * @return void - */ - public function testSetDataDiff() + public function testSetDataDiff(): void { $this->assertEquals([], $this->obj->getDataDiff()); $ret = $this->obj->setDataDiff(['foo'=>42]); @@ -79,10 +64,7 @@ public function testSetDataDiff() $this->assertEquals(['foo'=>42], $this->obj->getDataDiff()); } - /** - * @return void - */ - public function testSetProcessed() + public function testSetProcessed(): void { $this->assertFalse($this->obj->getProcessed()); $ret = $this->obj->setProcessed(true); @@ -90,10 +72,7 @@ public function testSetProcessed() $this->assertTrue($this->obj->getProcessed()); } - /** - * @return void - */ - public function testSetScheduledDate() + public function testSetScheduledDate(): void { $obj = $this->obj; $this->assertNull($obj->getScheduledDate()); @@ -109,19 +88,13 @@ public function testSetScheduledDate() $obj->setScheduledDate(false); } - /** - * @return void - */ - public function testSetScheduledDateInvalidTime() + public function testSetScheduledDateInvalidTime(): void { $this->expectException('\InvalidArgumentException'); $this->obj->setScheduledDate('A totally invalid date time'); } - /** - * @return void - */ - public function testSetProcessedDate() + public function testSetProcessedDate(): void { $obj = $this->obj; $this->assertNull($obj->getProcessedDate()); @@ -137,19 +110,13 @@ public function testSetProcessedDate() $obj->setProcessedDate(false); } - /** - * @return void - */ - public function testSetProcessedDateInvalidTime() + public function testSetProcessedDateInvalidTime(): void { $this->expectException('\InvalidArgumentException'); $this->obj->setProcessedDate('A totally invalid date time'); } - /** - * @return void - */ - public function testProcess() + public function testProcess(): void { $container = $this->container(); $this->obj->setModelFactory($container['model/factory']); @@ -167,12 +134,10 @@ public function testProcess() /** * Set up the service container. - * - * @return Container */ - private function container() + private function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerBaseServices($container); diff --git a/packages/object/tests/Charcoal/Object/PublishableTraitTest.php b/packages/object/tests/Charcoal/Object/PublishableTraitTest.php index 59cce1669..7ec9c37f0 100644 --- a/packages/object/tests/Charcoal/Object/PublishableTraitTest.php +++ b/packages/object/tests/Charcoal/Object/PublishableTraitTest.php @@ -26,19 +26,15 @@ class PublishableTraitTest extends AbstractTestCase * * @var PublishableTrait */ - private $obj; + private \Charcoal\Tests\Object\Mocks\PublishableClass $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ public function setUp(): void { @@ -56,10 +52,8 @@ public function setUp(): void * - accepts a string representation of a date/time value * - accepts a {@see \DateTimeInterface} * - accepts an blank value - * - * @return void */ - public function testPublishDate() + public function testPublishDate(): void { $obj = $this->obj; $time = new DateTime('2015-01-01 00:00:00'); @@ -75,10 +69,7 @@ public function testPublishDate() $this->assertEquals($time, $obj->getPublishDate()); } - /** - * @return void - */ - public function testUnexpectedPublishDate() + public function testUnexpectedPublishDate(): void { $obj = $this->obj; @@ -86,10 +77,7 @@ public function testUnexpectedPublishDate() $obj->setPublishDate('foobar'); } - /** - * @return void - */ - public function testInvalidPublishDate() + public function testInvalidPublishDate(): void { $obj = $this->obj; @@ -103,10 +91,8 @@ public function testInvalidPublishDate() * - accepts a string representation of a date/time value * - accepts a {@see \DateTimeInterface} * - accepts an blank value - * - * @return void */ - public function testExpiryDate() + public function testExpiryDate(): void { $obj = $this->obj; $time = new DateTime('2015-01-01 00:00:00'); @@ -122,10 +108,7 @@ public function testExpiryDate() $this->assertEquals($time, $obj->getExpiryDate()); } - /** - * @return void - */ - public function testUnexpectedExpiryDate() + public function testUnexpectedExpiryDate(): void { $obj = $this->obj; @@ -133,10 +116,7 @@ public function testUnexpectedExpiryDate() $obj->setExpiryDate('foobar'); } - /** - * @return void - */ - public function testInvalidExpiryDate() + public function testInvalidExpiryDate(): void { $obj = $this->obj; @@ -144,10 +124,7 @@ public function testInvalidExpiryDate() $obj->setExpiryDate(false); } - /** - * @return void - */ - public function testPublishStatus() + public function testPublishStatus(): void { $obj = $this->obj; @@ -174,14 +151,13 @@ public function testPublishStatus() } /** - * @dataProvider providerPublishStatus * * @param mixed $publishDate A date/time value. * @param mixed $expiryDate A date/time value. * @param string $expectedStatus The expected publication status. - * @return void */ - public function testPublishStatusFromDates($publishDate, $expiryDate, $expectedStatus) + #[\PHPUnit\Framework\Attributes\DataProvider('providerPublishStatus')] + public function testPublishStatusFromDates(?string $publishDate, ?string $expiryDate, string $expectedStatus): void { $obj = $this->obj; if ($publishDate !== null) { @@ -204,10 +180,7 @@ public function testPublishStatusFromDates($publishDate, $expiryDate, $expectedS $this->assertEquals($expectedStatus, $obj->publishDateStatus()); } - /** - * @return array - */ - public function providerPublishStatus() + public static function providerPublishStatus(): array { return [ [ null, null, Publishable::STATUS_PUBLISHED ], @@ -220,10 +193,7 @@ public function providerPublishStatus() ]; } - /** - * @return void - */ - public function testIsPublished() + public function testIsPublished(): void { $obj = $this->obj; @@ -247,12 +217,10 @@ public function testIsPublished() /** * Set up the service container. - * - * @return Container */ - private function container() + private function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerBaseServices($container); diff --git a/packages/object/tests/Charcoal/Object/RevisionableTraitTest.php b/packages/object/tests/Charcoal/Object/RevisionableTraitTest.php index b3d9bbc7f..174d7fd70 100644 --- a/packages/object/tests/Charcoal/Object/RevisionableTraitTest.php +++ b/packages/object/tests/Charcoal/Object/RevisionableTraitTest.php @@ -1 +1,3 @@ assertEquals('', $this->obj->slugPattern()); $ret = $this->obj->setSlugPattern('foo'); @@ -78,10 +69,7 @@ public function testSlugPattern() // $this->assertEquals('', $this->obj->slugPattern()); } - /** - * @return void - */ - public function testSlugPatternRoutable() + public function testSlugPatternRoutable(): void { $this->obj->setMetadata([ 'routable' => [ @@ -91,10 +79,7 @@ public function testSlugPatternRoutable() $this->assertEquals('foofoo', $this->obj->slugPattern()); } - /** - * @return void - */ - public function testSlugPatternWithoutRoutable() + public function testSlugPatternWithoutRoutable(): void { $this->obj->setMetadata([ 'routable' => null, @@ -103,10 +88,7 @@ public function testSlugPatternWithoutRoutable() $this->assertEquals('barbar', $this->obj->slugPattern()); } - /** - * @return void - */ - public function testSlugPatternWithoutMetadata() + public function testSlugPatternWithoutMetadata(): void { $this->obj->setMetadata([]); @@ -114,10 +96,7 @@ public function testSlugPatternWithoutMetadata() $this->obj->slugPattern(); } - /** - * @return void - */ - public function testSlugPrefix() + public function testSlugPrefix(): void { $this->assertEquals('', $this->obj->slugPrefix()); @@ -129,10 +108,7 @@ public function testSlugPrefix() $this->assertEquals('barfoo', $this->obj->slugPrefix()); } - /** - * @return void - */ - public function testSlugSuffix() + public function testSlugSuffix(): void { $this->assertEquals('', $this->obj->slugSuffix()); @@ -144,18 +120,12 @@ public function testSlugSuffix() $this->assertEquals('barfoo', $this->obj->slugSuffix()); } - /** - * @return void - */ - public function testIsSlugEditableIsFalseByDefault() + public function testIsSlugEditableIsFalseByDefault(): void { $this->assertFalse($this->obj->isSlugEditable()); } - /** - * @return void - */ - public function testIsSlugEditable() + public function testIsSlugEditable(): void { $this->obj->setMetadata([ 'routable' => [ @@ -165,10 +135,7 @@ public function testIsSlugEditable() $this->assertTrue($this->obj->isSlugEditable()); } - /** - * @return void - */ - public function testSlug() + public function testSlug(): void { $this->assertNull($this->obj->getSlug()); @@ -180,12 +147,9 @@ public function testSlug() $this->assertNull($this->obj->getSlug()); } - /** - * @return void - */ - public function testGenerateSlug() + public function testGenerateSlug(): void { - $container = $this->container(); + $this->container(); $this->obj->setMetadata([ 'routable' => [ @@ -200,21 +164,17 @@ public function testGenerateSlug() } /** - * @dataProvider providerSlugs * * @param string $str A dirty slug. * @param string $slug A clean $str. - * @return void */ - public function testSlugify($str, $slug) + #[\PHPUnit\Framework\Attributes\DataProvider('providerSlugs')] + public function testSlugify(string $str, string $slug): void { $this->assertEquals($slug, $this->obj->slugify($str)); } - /** - * @return array - */ - public function providerSlugs() + public static function providerSlugs(): array { return [ [ 'A B C', 'a-b-c' ], @@ -228,12 +188,9 @@ public function providerSlugs() ]; } - /** - * @return Translator - */ - private function translator() + private function translator(): \Charcoal\Translator\Translator { - if ($this->translator === null) { + if (!$this->translator instanceof \Charcoal\Translator\Translator) { $this->translator = new Translator([ 'manager' => new LocalesManager([ 'locales' => [ @@ -251,12 +208,10 @@ private function translator() /** * Set up the service container. - * - * @return Container */ - private function container() + private function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerBaseServices($container); diff --git a/packages/object/tests/Charcoal/Object/UserDataTest.php b/packages/object/tests/Charcoal/Object/UserDataTest.php index cfb29210f..409500f7c 100644 --- a/packages/object/tests/Charcoal/Object/UserDataTest.php +++ b/packages/object/tests/Charcoal/Object/UserDataTest.php @@ -19,22 +19,16 @@ class UserDataTest extends AbstractTestCase { /** * Tested Class. - * - * @var UserData */ - private $obj; + private \Charcoal\Object\UserData $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ public function setUp(): void { @@ -43,20 +37,14 @@ public function setUp(): void $this->obj = $container['model/factory']->create(UserData::class); } - /** - * @return void - */ - public function testDefaults() + public function testDefaults(): void { $this->assertNull($this->obj['ip']); $this->assertNull($this->obj['lang']); $this->assertNull($this->obj['ts']); } - /** - * @return void - */ - public function testSetData() + public function testSetData(): void { $ret = $this->obj->setData([ 'ip'=>'192.168.1.1', @@ -70,12 +58,8 @@ public function testSetData() $this->assertEquals($expected, $this->obj['ts']); } - /** - * @return void - */ - public function testSetIp() + public function testSetIp(): void { - $this->obj = $this->obj; $ret = $this->obj->setIp('1.1.1.1'); $this->assertSame($ret, $this->obj); $this->assertEquals(ip2long('1.1.1.1'), $this->obj['ip']); @@ -84,12 +68,8 @@ public function testSetIp() $this->assertEquals(2349255, $this->obj['ip']); } - /** - * @return void - */ - public function testSetLang() + public function testSetLang(): void { - $this->obj = $this->obj; $ret = $this->obj->setLang('en'); $this->assertSame($ret, $this->obj); $this->assertEquals('en', $this->obj['lang']); @@ -98,12 +78,8 @@ public function testSetLang() $this->obj->setLang(false); } - /** - * @return void - */ - public function testSetTs() + public function testSetTs(): void { - $this->obj = $this->obj; $ret = $this->obj->setTs('July 1st, 2014'); $this->assertSame($ret, $this->obj); $expected = new DateTime('July 1st, 2014'); @@ -115,12 +91,10 @@ public function testSetTs() /** * Set up the service container. - * - * @return Container */ - private function container() + private function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerBaseServices($container); diff --git a/packages/property/composer.json b/packages/property/composer.json index 6ee514c9d..9bc73749f 100644 --- a/packages/property/composer.json +++ b/packages/property/composer.json @@ -1,8 +1,9 @@ { - "type": "library", "name": "charcoal/property", "description": "Charcoal Property defines an object (provides metadata)", - "keywords": ["charcoal"], + "keywords": [ + "charcoal" + ], "homepage": "https://charcoal.locomotive.ca", "license": "MIT", "authors": [ @@ -15,13 +16,9 @@ "homepage": "https://locomotive.ca" } ], - "extra": { - "branch-alias": { - "dev-main": "5.x-dev" - } - }, + "type": "library", "require": { - "php": "^7.4 || ^8.0", + "php": "^8.3", "ext-fileinfo": "*", "ext-pdo": "*", "ext-simplexml": "*", @@ -34,7 +31,7 @@ "charcoal/translator": "^5.1" }, "require-dev": { - "phpunit/phpunit": "^9.5", + "phpunit/phpunit": "^12.5", "squizlabs/php_codesniffer": "^3.5", "php-coveralls/php-coveralls": "^2.2", "cache/void-adapter": "^1.0", @@ -52,8 +49,10 @@ "Charcoal\\Tests\\": "tests/Charcoal" } }, - "replace": { - "locomotivemtl/charcoal-property": "*" + "extra": { + "branch-alias": { + "dev-main": "5.x-dev" + } }, "scripts": { "test": [ @@ -69,6 +68,9 @@ "phpcbf": "php vendor/bin/phpcbf -ps --colors src/ tests/", "phpunit": "php vendor/bin/phpunit --coverage-text" }, + "replace": { + "locomotivemtl/charcoal-property": "*" + }, "minimum-stability": "dev", "prefer-stable": true } diff --git a/packages/property/src/Charcoal/Property/AbstractProperty.php b/packages/property/src/Charcoal/Property/AbstractProperty.php index 2931d03e7..ef8292fb3 100644 --- a/packages/property/src/Charcoal/Property/AbstractProperty.php +++ b/packages/property/src/Charcoal/Property/AbstractProperty.php @@ -43,7 +43,8 @@ abstract class AbstractProperty extends AbstractEntity implements DescribablePropertyInterface, LoggerAwareInterface, StorablePropertyInterface, - ValidatableInterface + ValidatableInterface, + \Stringable { use LoggerAwareTrait; use DescribableTrait; @@ -62,10 +63,7 @@ abstract class AbstractProperty extends AbstractEntity implements public const DEFAULT_VALIDATABLE = true; public const DEFAULT_ACTIVE = true; - /** - * @var string - */ - private $ident = ''; + private string $ident = ''; /** * @var mixed @@ -77,15 +75,9 @@ abstract class AbstractProperty extends AbstractEntity implements */ private $label; - /** - * @var boolean - */ - private $l10n = self::DEFAULT_L10N; + private bool $l10n = self::DEFAULT_L10N; - /** - * @var boolean - */ - private $multiple = self::DEFAULT_MULTIPLE; + private bool $multiple = self::DEFAULT_MULTIPLE; /** * Array of options for multiple properties @@ -96,45 +88,34 @@ abstract class AbstractProperty extends AbstractEntity implements */ private $multipleOptions; - /** - * @var boolean - */ - private $hidden = self::DEFAULT_HIDDEN; + private bool $hidden = self::DEFAULT_HIDDEN; /** * If true, this property *must* have a value - * @var boolean */ - private $required = self::DEFAULT_REQUIRED; + private bool $required = self::DEFAULT_REQUIRED; /** * Unique properties should not share he same value across 2 objects - * @var boolean */ - private $unique = self::DEFAULT_UNIQUE; + private bool $unique = self::DEFAULT_UNIQUE; - /** - * @var boolean $allowNull - */ - private $allowNull = self::DEFAULT_ALLOW_NULL; + private bool $allowNull = self::DEFAULT_ALLOW_NULL; /** * Only the storable properties should be saved in storage. - * @var boolean */ - private $storable = self::DEFAULT_STORABLE; + private bool $storable = self::DEFAULT_STORABLE; /** * Whether to validate the property. - * @var boolean */ - private $validatable = self::DEFAULT_VALIDATABLE; + private bool $validatable = self::DEFAULT_VALIDATABLE; /** * Inactive properties should be hidden everywhere / unused - * @var boolean */ - private $active = self::DEFAULT_ACTIVE; + private bool $active = self::DEFAULT_ACTIVE; /** * @var Translation|null @@ -169,7 +150,7 @@ abstract class AbstractProperty extends AbstractEntity implements * * @param array $data Optional. Class Dependencies. */ - public function __construct(array $data = null) + public function __construct(?array $data = null) { $this->setLogger($data['logger']); $this->setPdo($data['database']); @@ -190,21 +171,16 @@ public function __construct(array $data = null) } } - /** - * @return string - * @deprecated - */ - public function __toString() + #[\Deprecated] + public function __toString(): string { $val = $this->val(); if (is_string($val)) { return $val; + } elseif (is_object($val)) { + return (string)$val; } else { - if (is_object($val)) { - return (string)$val; - } else { - return ''; - } + return ''; } } @@ -294,11 +270,11 @@ public function l10nIdent($lang = null) /** * Set the property's value. * - * @deprecated * * @param mixed $val The property (raw) value. * @return self */ + #[\Deprecated] final public function setVal($val) { $this->val = $this->parseVal($val); @@ -309,10 +285,10 @@ final public function setVal($val) /** * Clear the property's value. * - * @deprecated * * @return self */ + #[\Deprecated] final public function clearVal() { $this->val = null; @@ -323,10 +299,10 @@ final public function clearVal() /** * Retrieve the property's value. * - * @deprecated * * @return mixed */ + #[\Deprecated] final public function val() { return $this->val; @@ -358,7 +334,6 @@ final public function parseVal($val) if ($this['multiple']) { $val = $this->parseValAsMultiple($val); - if (empty($val)) { if ($this['allowNull'] === false) { throw new InvalidArgumentException(sprintf( @@ -369,18 +344,14 @@ final public function parseVal($val) return $val; } - - $val = array_map([ $this, 'parseOne' ], $val); - } else { - if ($this['l10n']) { - $val = $this->parseValAsL10n($val); - - if ($val) { - $val->sanitize([ $this, 'parseOne' ]); - } - } else { - $val = $this->parseOne($val); + $val = array_map($this->parseOne(...), $val); + } elseif ($this['l10n']) { + $val = $this->parseValAsL10n($val); + if ($val instanceof \Charcoal\Translator\TranslatableInterface) { + $val->sanitize($this->parseOne(...)); } + } else { + $val = $this->parseOne($val); } return $val; @@ -425,10 +396,8 @@ public function inputVal($val, array $options = []) } /** Parse multiple values / ensure they are of array type. */ - if ($this['multiple']) { - if (is_array($propertyValue)) { - $propertyValue = implode($this->multipleSeparator(), $propertyValue); - } + if ($this['multiple'] && is_array($propertyValue)) { + $propertyValue = implode($this->multipleSeparator(), $propertyValue); } if (!is_scalar($propertyValue)) { @@ -437,8 +406,7 @@ public function inputVal($val, array $options = []) } elseif (($options['pretty'] ?? false)) { $flags = JSON_PRETTY_PRINT; } - $propertyValue = json_encode($propertyValue, ($flags ?? 0)); - return $propertyValue; + return json_encode($propertyValue, ($flags ?? 0)); } return (string)$propertyValue; @@ -468,10 +436,8 @@ public function displayVal($val, array $options = []) } /** Parse multiple values / ensure they are of array type. */ - if ($this['multiple']) { - if (!is_array($propertyValue)) { - $propertyValue = $this->parseValAsMultiple($propertyValue); - } + if ($this['multiple'] && !is_array($propertyValue)) { + $propertyValue = $this->parseValAsMultiple($propertyValue); } if (is_array($propertyValue)) { @@ -515,7 +481,7 @@ public function getLabel() */ public function setL10n($l10n) { - $this->l10n = !!$l10n; + $this->l10n = (bool) $l10n; return $this; } @@ -545,7 +511,7 @@ public function parseValAsL10n($val): ?TranslatableInterface */ public function setHidden($hidden) { - $this->hidden = !!$hidden; + $this->hidden = (bool) $hidden; return $this; } @@ -577,7 +543,7 @@ public function setMultiple($multiple) } } - $this->multiple = !!$multiple; + $this->multiple = $multiple; return $this; } @@ -703,7 +669,7 @@ public function parseValAsMultiple($val) */ public function setAllowNull($allow) { - $this->allowNull = !!$allow; + $this->allowNull = (bool) $allow; return $this; } @@ -727,7 +693,7 @@ public function getAllowNull() */ public function setRequired($required) { - $this->required = !!$required; + $this->required = (bool) $required; return $this; } @@ -751,7 +717,7 @@ public function getRequired() */ public function setUnique($unique) { - $this->unique = !!$unique; + $this->unique = (bool) $unique; return $this; } @@ -770,7 +736,7 @@ public function getUnique() */ public function setActive($active) { - $this->active = !!$active; + $this->active = (bool) $active; return $this; } @@ -799,7 +765,7 @@ public function active() */ public function setValidatable($validatable) { - $this->validatable = !!$validatable; + $this->validatable = (bool) $validatable; return $this; } @@ -818,7 +784,7 @@ public function getValidatable() */ public function setStorable($storable) { - $this->storable = !!$storable; + $this->storable = (bool) $storable; return $this; } @@ -908,10 +874,6 @@ public function validateRequired() */ public function validateUnique() { - if (!$this['unique']) { - return true; - } - /** @todo Check in the model's storage if the value already exists. */ return true; } @@ -1028,14 +990,10 @@ protected function setDependencies(Container $container) * @param mixed $lang The language to return the value in. * @return string|null */ - protected function l10nVal($val, $lang = null) + protected function l10nVal(array $val, $lang = null) { if (!is_string($lang)) { - if (is_array($lang) && isset($lang['lang'])) { - $lang = $lang['lang']; - } else { - $lang = $this->translator()->getLocale(); - } + $lang = is_array($lang) && isset($lang['lang']) ? $lang['lang'] : $this->translator()->getLocale(); } return ($val[$lang] ?? null); @@ -1048,7 +1006,7 @@ protected function l10nVal($val, $lang = null) * @see DescribableTrait::createMetadata() * @return PropertyMetadata */ - protected function createMetadata(array $data = null) + protected function createMetadata(?array $data = null) { $class = $this->metadataClass(); return new $class($data); @@ -1073,16 +1031,13 @@ protected function metadataClass() */ protected function createValidator() { - $validator = new PropertyValidator($this); - - return $validator; + return new PropertyValidator($this); } /** * @param PDO $pdo The database connection (PDO) instance. - * @return void */ - private function setPdo(PDO $pdo) + private function setPdo(PDO $pdo): void { $this->pdo = $pdo; } diff --git a/packages/property/src/Charcoal/Property/AudioProperty.php b/packages/property/src/Charcoal/Property/AudioProperty.php index 44267c9ca..e08d2df1d 100644 --- a/packages/property/src/Charcoal/Property/AudioProperty.php +++ b/packages/property/src/Charcoal/Property/AudioProperty.php @@ -1,5 +1,7 @@ minLength; } @@ -64,7 +57,7 @@ public function getMinLength() * @throws InvalidArgumentException If the length is not an integer. * @return AudioProperty Chainable */ - public function setMaxLength($maxLength) + public function setMaxLength($maxLength): static { if (!is_int($maxLength)) { throw new InvalidArgumentException( @@ -75,10 +68,7 @@ public function setMaxLength($maxLength) return $this; } - /** - * @return integer - */ - public function getMaxLength() + public function getMaxLength(): int { return $this->maxLength; } @@ -90,7 +80,8 @@ public function getMaxLength() * * @return string[] */ - public function getDefaultAcceptedMimetypes() + #[\Override] + public function getDefaultAcceptedMimetypes(): array { return [ 'audio/mp3', @@ -110,26 +101,15 @@ public function getDefaultAcceptedMimetypes() * @param string $type The MIME type to resolve. * @return string|null The extension based on the MIME type. */ - protected function resolveExtensionFromMimeType($type) + #[\Override] + protected function resolveExtensionFromMimeType($type): ?string { - switch ($type) { - case 'audio/mp3': - case 'audio/mpeg': - return 'mp3'; - - case 'audio/ogg': - return 'ogg'; - - case 'audio/webm': - return 'webm'; - - case 'audio/wav': - case 'audio/wave': - case 'audio/x-wav': - case 'audio/x-pn-wav': - return 'wav'; - } - - return null; + return match ($type) { + 'audio/mp3', 'audio/mpeg' => 'mp3', + 'audio/ogg' => 'ogg', + 'audio/webm' => 'webm', + 'audio/wav', 'audio/wave', 'audio/x-wav', 'audio/x-pn-wav' => 'wav', + default => null, + }; } } diff --git a/packages/property/src/Charcoal/Property/BooleanProperty.php b/packages/property/src/Charcoal/Property/BooleanProperty.php index d9bcec5ed..7e9569dd3 100644 --- a/packages/property/src/Charcoal/Property/BooleanProperty.php +++ b/packages/property/src/Charcoal/Property/BooleanProperty.php @@ -1,5 +1,7 @@ translator()->translate($label); @@ -81,12 +75,12 @@ public function displayVal($val, array $options = []) * * @param boolean $multiple The multiple flag. * @throws InvalidArgumentException If multiple is true. (must be false for boolean properties). - * @return self */ - public function setMultiple($multiple) + #[\Override] + public function setMultiple($multiple): static { - $multiple = !!$multiple; - if ($multiple === true) { + $multiple = (bool) $multiple; + if ($multiple) { throw new InvalidArgumentException( 'Multiple can not be true for boolean property.' ); @@ -98,19 +92,17 @@ public function setMultiple($multiple) * Multiple is always false for boolean property. * * @see AbstractProperty::getMultiple() - * - * @return boolean */ - public function getMultiple() + #[\Override] + public function getMultiple(): bool { return false; } /** * @param mixed $label The true label. - * @return self */ - public function setTrueLabel($label) + public function setTrueLabel($label): static { $this->trueLabel = $this->translator()->translation($label); return $this; @@ -130,9 +122,8 @@ public function getTrueLabel() /** * @param mixed $label The false label. - * @return self */ - public function setFalseLabel($label) + public function setFalseLabel($label): static { $this->falseLabel = $this->translator()->translation($label); return $this; @@ -159,7 +150,7 @@ public function getFalseLabel() * * @return string The SQL type */ - public function sqlType() + public function sqlType(): string { $dbDriver = $this->pdo->getAttribute(PDO::ATTR_DRIVER_NAME); if ($dbDriver === 'sqlite') { @@ -171,26 +162,22 @@ public function sqlType() /** * @see StorablePropertyTrait::sqlPdoType() - * - * @return integer */ - public function sqlPdoType() + public function sqlPdoType(): int { return PDO::PARAM_BOOL; } /** * @see SelectablePropertyTrait::choices() - * - * @return array */ - public function choices() + public function choices(): array { $val = $this->val(); return [ [ 'label' => $this['trueLabel'], - 'selected' => !!$val, + 'selected' => (bool) $val, 'value' => 1, ], [ diff --git a/packages/property/src/Charcoal/Property/ColorProperty.php b/packages/property/src/Charcoal/Property/ColorProperty.php index f1fb13f36..ac9d2c1c1 100644 --- a/packages/property/src/Charcoal/Property/ColorProperty.php +++ b/packages/property/src/Charcoal/Property/ColorProperty.php @@ -14,15 +14,9 @@ class ColorProperty extends AbstractProperty { public const DEFAULT_SUPPORT_ALPHA = false; - /** - * @var boolean $supportAlpha - */ - private $supportAlpha = self::DEFAULT_SUPPORT_ALPHA; + private bool $supportAlpha = self::DEFAULT_SUPPORT_ALPHA; - /** - * @return string - */ - public function type() + public function type(): string { return 'color'; } @@ -31,16 +25,13 @@ public function type() * @param boolean $support The alpha support flag. * @return ColorProperty Chainable */ - public function setSupportAlpha($support) + public function setSupportAlpha($support): static { - $this->supportAlpha = !!$support; + $this->supportAlpha = (bool) $support; return $this; } - /** - * @return boolean - */ - public function getSupportAlpha() + public function getSupportAlpha(): bool { return $this->supportAlpha; } @@ -53,7 +44,8 @@ public function getSupportAlpha() * @throws InvalidArgumentException If the value does not match property's options. * @return string|null */ - public function parseOne($val) + #[\Override] + public function parseOne($val): null|array|float|int|string|false { if ($val === null || $val === '') { if ($this['allowNull']) { @@ -71,7 +63,7 @@ public function parseOne($val) * @param string|array $val The color value to sanitize to an hexadecimal or rgba() value. * @return string The color string. Hexadecimal or rgba() if alpha is supported.. */ - public function colorVal($val) + public function colorVal($val): array|int|float|false|null|string { if (!$val) { return $val; @@ -99,7 +91,7 @@ public function colorVal($val) * * @return string The SQL type */ - public function sqlType() + public function sqlType(): string { // Multiple strings are always stored as TEXT because they can hold multiple values if ($this['multiple']) { @@ -115,10 +107,8 @@ public function sqlType() /** * @see StorablePropertyTrait::sqlPdoType() - * - * @return integer */ - public function sqlPdoType() + public function sqlPdoType(): int { return PDO::PARAM_STR; } @@ -129,7 +119,7 @@ public function sqlPdoType() * @param integer $b Blue value (0 to 255). * @return string Hexadecimal color value, as uppercased hexadecimal without the "#" prefix. */ - protected function rgbToHexadecimal($r, $g, $b) + protected function rgbToHexadecimal($r, $g, $b): string { $hex = ''; $hex .= str_pad(dechex($r), 2, '0', STR_PAD_LEFT); @@ -156,7 +146,7 @@ private function parseColorVal($val) * @throws InvalidArgumentException If the array does not have at least 3 items. * @return array The parsed `[r,g,b,a]` color array. */ - private function parseArray(array $val) + private function parseArray(array $val): array { if (count($val) < 3) { throw new InvalidArgumentException( @@ -168,12 +158,12 @@ private function parseArray(array $val) $r = $val['r']; $g = $val['g']; $b = $val['b']; - $a = isset($val['a']) ? $val['a'] : 0; + $a = $val['a'] ?? 0; } else { $r = $val[0]; $g = $val[1]; $b = $val[2]; - $a = isset($val[3]) ? $val[3] : 0; + $a = $val[3] ?? 0; } return [ @@ -213,11 +203,11 @@ private function parseString($val) * @param string $val The hexadecimal color string to parse. * @return array The parsed `[r,g,b,a]` color array. */ - private function parseHexadecimal($val) + private function parseHexadecimal(string $val): array { $val = str_replace('#', '', $val); - if (strlen($val) == 3) { + if (strlen($val) === 3) { return [ 'r' => hexdec(substr($val, 0, 1) . substr($val, 0, 1)), 'g' => hexdec(substr($val, 1, 1) . substr($val, 1, 1)), @@ -241,7 +231,7 @@ private function parseHexadecimal($val) * @throws InvalidArgumentException If the color can not be parsed. * @return array The parsed `[r,g,b,a]` color array. */ - private function parseRgb($val) + private function parseRgb(string $val): array { $match = preg_match('/rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i', $val, $m); if (!$match) { @@ -263,7 +253,7 @@ private function parseRgb($val) * @throws InvalidArgumentException If The colors string is invalid (does not match rgba format). * @return array The parsed `[r,g,b,a]` color array. */ - private function parseRgba($val) + private function parseRgba(string $val): array { $match = preg_match('/rgba\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\(\d+)\s*\)/i', $val, $m); if (!$match) { @@ -284,11 +274,11 @@ private function parseRgba($val) * @throws InvalidArgumentException If the string is not an existing SVG color. * @return array The parsed `[r,g,b,a]` color array. */ - private function parseNamedColor($val) + private function parseNamedColor(string|array $val) { static $colors; if (!$colors) { - $colors = include 'data/colors.php'; + $colors = include __DIR__ . '/data/colors.php'; } $val = strtolower($val); if (in_array($val, array_keys($colors))) { diff --git a/packages/property/src/Charcoal/Property/DateTimeProperty.php b/packages/property/src/Charcoal/Property/DateTimeProperty.php index 85f64f943..c832793bd 100644 --- a/packages/property/src/Charcoal/Property/DateTimeProperty.php +++ b/packages/property/src/Charcoal/Property/DateTimeProperty.php @@ -19,25 +19,13 @@ class DateTimeProperty extends AbstractProperty public const DEFAULT_MAX = null; public const DEFAULT_FORMAT = 'Y-m-d H:i:s'; - /** - * @var DateTimeInterface|null - */ - private $min = self::DEFAULT_MIN; + private ?\DateTimeInterface $min = self::DEFAULT_MIN; - /** - * @var DateTimeInterface|null - */ - private $max = self::DEFAULT_MAX; + private ?\DateTimeInterface $max = self::DEFAULT_MAX; - /** - * @var string - */ - private $format = self::DEFAULT_FORMAT; + private string $format = self::DEFAULT_FORMAT; - /** - * @return string - */ - public function type() + public function type(): string { return 'date-time'; } @@ -49,12 +37,12 @@ public function type() * * @param boolean $multiple Multiple flag. * @throws InvalidArgumentException If the multiple argument is true (must be false). - * @return self */ - public function setMultiple($multiple) + #[\Override] + public function setMultiple($multiple): static { - $multiple = !!$multiple; - if ($multiple === true) { + $multiple = (bool) $multiple; + if ($multiple) { throw new InvalidArgumentException( 'Multiple can not be TRUE for date/time property.' ); @@ -66,10 +54,9 @@ public function setMultiple($multiple) * Multiple is always false for DateTime property. * * @see AbstractProperty::getMultiple() - * - * @return boolean */ - public function getMultiple() + #[\Override] + public function getMultiple(): bool { return false; } @@ -81,9 +68,9 @@ public function getMultiple() * @see AbstractProperty::parseVal() * * @param string|DateTimeInterface $val The value to set. - * @return DateTimeInterface|null */ - public function parseOne($val) + #[\Override] + public function parseOne($val): ?\DateTimeInterface { return $this->dateTimeVal($val); } @@ -96,9 +83,9 @@ public function parseOne($val) * @param mixed $val The value to to convert for input. * @param array $options Unused, optional options. * @throws Exception If the date/time is invalid. - * @return string|null */ - public function inputVal($val, array $options = []) + #[\Override] + public function inputVal($val, array $options = []): string { unset($options); $val = $this->dateTimeVal($val); @@ -117,9 +104,9 @@ public function inputVal($val, array $options = []) * * @param string|DateTime $val Optional. Value to convert to storage format. * @throws Exception If the date/time is invalid. - * @return string|null */ - public function storageVal($val) + #[\Override] + public function storageVal($val): ?string { $val = $this->dateTimeVal($val); @@ -143,20 +130,16 @@ public function storageVal($val) * * @param mixed $val The value to to convert for display. * @param array $options Optional display options. - * @return string */ - public function displayVal($val, array $options = []) + #[\Override] + public function displayVal($val, array $options = []): string { $val = $this->dateTimeVal($val); - if ($val === null) { + if (!$val instanceof \DateTimeInterface) { return ''; } - if (isset($options['format'])) { - $format = $options['format']; - } else { - $format = $this->getFormat(); - } + $format = $options['format'] ?? $this->getFormat(); return $val->format($format); } @@ -164,9 +147,8 @@ public function displayVal($val, array $options = []) /** * @param string|DateTime|null $min The minimum allowed value. * @throws InvalidArgumentException If the date/time is invalid. - * @return self */ - public function setMin($min) + public function setMin($min): static { try { $this->min = $this->dateTimeVal($min); @@ -176,10 +158,7 @@ public function setMin($min) } } - /** - * @return DateTimeInterface|null - */ - public function getMin() + public function getMin(): ?\DateTimeInterface { return $this->min; } @@ -187,9 +166,8 @@ public function getMin() /** * @param string|DateTime|null $max The maximum allowed value. * @throws InvalidArgumentException If the date/time is invalid. - * @return self */ - public function setMax($max) + public function setMax($max): static { try { $this->max = $this->dateTimeVal($max); @@ -199,10 +177,7 @@ public function setMax($max) } } - /** - * @return DateTimeInterface|null - */ - public function getMax() + public function getMax(): ?\DateTimeInterface { return $this->max; } @@ -212,7 +187,7 @@ public function getMax() * @throws InvalidArgumentException If the format is not a string. * @return DateTimeProperty Chainable */ - public function setFormat($format) + public function setFormat($format): static { if ($format === null) { $format = ''; @@ -226,18 +201,13 @@ public function setFormat($format) return $this; } - /** - * @return string - */ - public function getFormat() + public function getFormat(): string { return $this->format; } - /** - * @return array - */ - public function validationMethods() + #[\Override] + public function validationMethods(): array { $parentMethods = parent::validationMethods(); @@ -253,7 +223,7 @@ public function validationMethods() public function validateMin() { $min = $this->getMin(); - if (!$min) { + if (!$min instanceof \DateTimeInterface) { return true; } $valid = ($this->val() >= $min); @@ -269,7 +239,7 @@ public function validateMin() public function validateMax() { $max = $this->getMax(); - if (!$max) { + if (!$max instanceof \DateTimeInterface) { return true; } $valid = ($this->val() <= $max); @@ -281,18 +251,16 @@ public function validateMax() /** * @see StorablePropertyTrait::sqlType() - * @return string */ - public function sqlType() + public function sqlType(): string { return 'DATETIME'; } /** * @see StorablePropertyTrait::sqlPdoType() - * @return integer */ - public function sqlPdoType() + public function sqlPdoType(): int { return PDO::PARAM_STR; } @@ -300,14 +268,13 @@ public function sqlPdoType() /** * @param mixed $val Value to convert to DateTime. * @throws InvalidArgumentException If the value is not a valid datetime. - * @return DateTimeInterface|null */ - private function dateTimeVal($val) + private function dateTimeVal($val): ?\DateTimeInterface { if ( $val === null || (is_string($val) && ! strlen(trim($val))) || - (is_array($val) && ! count(array_filter($val, 'strlen'))) + (is_array($val) && ! count(array_filter($val, strlen(...)))) ) { return null; } @@ -332,9 +299,8 @@ private function dateTimeVal($val) /** * @param integer|string $timestamp Timestamp. - * @return boolean */ - private function isValidTimeStamp($timestamp) + private function isValidTimeStamp(int $timestamp): bool { return (is_int($timestamp)) && ($timestamp <= PHP_INT_MAX) diff --git a/packages/property/src/Charcoal/Property/DescribablePropertyInterface.php b/packages/property/src/Charcoal/Property/DescribablePropertyInterface.php index 234242c56..031be6db5 100644 --- a/packages/property/src/Charcoal/Property/DescribablePropertyInterface.php +++ b/packages/property/src/Charcoal/Property/DescribablePropertyInterface.php @@ -1,5 +1,7 @@ metadata(); @@ -39,10 +39,6 @@ public function properties(array $propertyIdents = null) $propertyIdents = array_keys($this->metadata()->properties()); } - if (empty($propertyIdents)) { - return; - } - foreach ($propertyIdents as $propertyIdent) { yield $propertyIdent => $this->property($propertyIdent); } @@ -61,7 +57,7 @@ public function property($propertyIdent) if (!is_string($propertyIdent)) { throw new InvalidArgumentException( '[%s] Property identifier must be a string', - get_class($this) + $this::class ); } @@ -99,14 +95,13 @@ public function p($propertyIdent = null) * * @param string $propertyIdent The property identifier to lookup. * @throws InvalidArgumentException If the property identifier is not a string. - * @return boolean */ - public function hasProperty($propertyIdent) + public function hasProperty($propertyIdent): bool { if (!is_string($propertyIdent)) { throw new InvalidArgumentException( '[%s] Property identifier must be a string', - get_class($this) + $this::class ); } @@ -158,7 +153,7 @@ protected function propertyFactory() if ($this->propertyFactory === null) { throw new RuntimeException(sprintf( '[%s] Model does not have a property factory', - get_class($this) + $this::class )); } @@ -178,8 +173,8 @@ protected function createProperty($propertyIdent) if (!is_string($propertyIdent)) { throw new InvalidArgumentException( '[%s] Property identifier must be a string, received %s', - get_class($this), - (is_object($propertyIdent) ? get_class($propertyIdent) : gettype($propertyIdent)) + $this::class, + (get_debug_type($propertyIdent)) ); } @@ -190,7 +185,7 @@ protected function createProperty($propertyIdent) if (empty($props)) { throw new RuntimeException(sprintf( '[%s] Invalid model metadata - No properties defined (must define at least "%s")', - get_class($this), + $this::class, $propertyIdent )); } @@ -198,7 +193,7 @@ protected function createProperty($propertyIdent) if (!isset($props[$propertyIdent])) { throw new RuntimeException(sprintf( '[%s] Invalid model metadata - Undefined property metadata for "%s"', - get_class($this), + $this::class, $propertyIdent )); } @@ -208,7 +203,7 @@ protected function createProperty($propertyIdent) if (!isset($propertyMetadata['type'])) { throw new RuntimeException(sprintf( '[%s] Invalid model metadata - Undefined property type for "%s"', - get_class($this), + $this::class, $propertyIdent )); } diff --git a/packages/property/src/Charcoal/Property/EmailProperty.php b/packages/property/src/Charcoal/Property/EmailProperty.php index 22536ba9d..46ba68917 100644 --- a/packages/property/src/Charcoal/Property/EmailProperty.php +++ b/packages/property/src/Charcoal/Property/EmailProperty.php @@ -10,10 +10,8 @@ */ class EmailProperty extends StringProperty { - /** - * @return string - */ - public function type() + #[\Override] + public function type(): string { return 'email'; } @@ -22,18 +20,15 @@ public function type() * Email's maximum length is defined in RFC-3696 (+ errata) as 254 characters. * * This overrides PropertyString's maxLength() to ensure compliance with the email standards. - * - * @return integer */ - public function getMaxLength() + #[\Override] + public function getMaxLength(): int { return 254; } - /** - * @return array - */ - public function validationMethods() + #[\Override] + public function validationMethods(): array { $parentMethods = parent::validationMethods(); @@ -42,10 +37,7 @@ public function validationMethods() ]); } - /** - * @return boolean - */ - public function validateEmail() + public function validateEmail(): bool { if ($this['allowNull'] && !$this['required']) { return true; @@ -67,8 +59,9 @@ public function validateEmail() * @param mixed $val A single value to parse. * @return string */ - public function parseOne($val) + #[\Override] + public function parseOne($val): string|false { - return filter_var(strip_tags($val), FILTER_SANITIZE_EMAIL); + return filter_var(strip_tags((string) $val), FILTER_SANITIZE_EMAIL); } } diff --git a/packages/property/src/Charcoal/Property/FileProperty.php b/packages/property/src/Charcoal/Property/FileProperty.php index d54562cf6..367d69f9d 100644 --- a/packages/property/src/Charcoal/Property/FileProperty.php +++ b/packages/property/src/Charcoal/Property/FileProperty.php @@ -37,17 +37,13 @@ class FileProperty extends AbstractProperty /** * Whether uploaded files should be accessible from the web root. - * - * @var boolean */ - private $publicAccess = self::DEFAULT_PUBLIC_ACCESS; + private bool $publicAccess = self::DEFAULT_PUBLIC_ACCESS; /** * The relative path to the storage directory. - * - * @var string */ - private $uploadPath = self::DEFAULT_UPLOAD_PATH; + private string $uploadPath = self::DEFAULT_UPLOAD_PATH; /** * The base path for the Charcoal installation. @@ -65,24 +61,20 @@ class FileProperty extends AbstractProperty /** * Whether existing destinations should be overwritten. - * - * @var boolean */ - private $overwrite = self::DEFAULT_OVERWRITE; + private bool $overwrite = self::DEFAULT_OVERWRITE; /** * Collection of accepted MIME types. * * @var string[] */ - private $acceptedMimetypes; + private ?array $acceptedMimetypes = null; /** * Current file mimetype - * - * @var string */ - private $mimetype; + private ?string $mimetype = null; /** * Maximum allowed file size, in bytes. @@ -117,10 +109,7 @@ class FileProperty extends AbstractProperty */ protected static $normalizePathCache = []; - /** - * @return string - */ - public function type() + public function type(): string { return 'file'; } @@ -129,21 +118,18 @@ public function type() * Set whether uploaded files should be publicly available. * * @param boolean $public Whether uploaded files should be accessible (TRUE) or not (FALSE) from the web root. - * @return self */ - public function setPublicAccess($public) + public function setPublicAccess($public): static { - $this->publicAccess = !!$public; + $this->publicAccess = (bool) $public; return $this; } /** * Determine if uploaded files should be publicly available. - * - * @return boolean */ - public function getPublicAccess() + public function getPublicAccess(): bool { return $this->publicAccess; } @@ -155,9 +141,8 @@ public function getPublicAccess() * * @param string $path The destination directory, relative to project's root. * @throws InvalidArgumentException If the path is not a string. - * @return self */ - public function setUploadPath($path) + public function setUploadPath($path): static { if (!is_string($path)) { throw new InvalidArgumentException( @@ -173,10 +158,8 @@ public function setUploadPath($path) /** * Retrieve the destination for the uploaded file(s). - * - * @return string */ - public function getUploadPath() + public function getUploadPath(): string { return $this->uploadPath; } @@ -185,21 +168,18 @@ public function getUploadPath() * Set whether existing destinations should be overwritten. * * @param boolean $overwrite Whether existing destinations should be overwritten (TRUE) or not (FALSE). - * @return self */ - public function setOverwrite($overwrite) + public function setOverwrite($overwrite): static { - $this->overwrite = !!$overwrite; + $this->overwrite = (bool) $overwrite; return $this; } /** * Determine if existing destinations should be overwritten. - * - * @return boolean */ - public function getOverwrite() + public function getOverwrite(): bool { return $this->overwrite; } @@ -209,14 +189,13 @@ public function getOverwrite() * * @param mixed $types One or many MIME types. * @throws InvalidArgumentException If the $types argument is not NULL or a list. - * @return self */ - public function setAcceptedMimetypes($types) + public function setAcceptedMimetypes($types): static { if (is_array($types)) { $types = array_filter($types); - if (empty($types)) { + if ($types === []) { $types = null; } } @@ -238,11 +217,11 @@ public function setAcceptedMimetypes($types) */ public function hasAcceptedMimetypes() { - if (!empty($this->acceptedMimetypes)) { + if ($this->acceptedMimetypes !== null && $this->acceptedMimetypes !== []) { return true; } - return !empty($this->getDefaultAcceptedMimetypes()); + return $this->getDefaultAcceptedMimetypes() !== []; } /** @@ -266,7 +245,7 @@ public function getAcceptedMimetypes() * * @return string[] */ - public function getDefaultAcceptedMimetypes() + public function getDefaultAcceptedMimetypes(): array { return []; } @@ -278,7 +257,7 @@ public function getDefaultAcceptedMimetypes() * @throws InvalidArgumentException If the MIME type argument is not a string. * @return FileProperty Chainable */ - public function setMimetype($type) + public function setMimetype($type): static { if ($type === null || $type === false) { $this->mimetype = null; @@ -302,11 +281,11 @@ public function setMimetype($type) * * @return integer Returns the MIME type for the first value. */ - public function getMimetype() + public function getMimetype(): ?string { if ($this->mimetype === null) { $files = $this->parseValAsFileList($this->val()); - if (empty($files)) { + if ($files === []) { return null; } @@ -355,7 +334,7 @@ public function getMimetypeFor($file) * @throws InvalidArgumentException If the size argument is not an integer. * @return FileProperty Chainable */ - public function setMaxFilesize($size) + public function setMaxFilesize($size): static { $this->maxFilesize = $this->parseIniSize($size); @@ -371,7 +350,7 @@ public function setMaxFilesize($size) */ public function getMaxFilesize() { - if (!isset($this->maxFilesize)) { + if ($this->maxFilesize === null) { return $this->maxFilesizeAllowedByPhp(); } @@ -407,7 +386,7 @@ public function maxFilesizeAllowedByPhp(&$iniDirective = null) * @throws InvalidArgumentException If the size argument is not an integer. * @return FileProperty Chainable */ - public function setFilesize($size) + public function setFilesize($size): static { if (!is_int($size) && $size !== null) { throw new InvalidArgumentException( @@ -430,7 +409,7 @@ public function getFilesize() { if ($this->filesize === null) { $files = $this->parseValAsFileList($this->val()); - if (empty($files)) { + if ($files === []) { return 0; } @@ -453,7 +432,7 @@ public function getFilesize() * @return integer|null Returns the file size in bytes, * or NULL in case of an error or the file is missing. */ - public function getFilesizeFor($file) + public function getFilesizeFor($file): ?int { if (!$this->isAbsolutePath($file)) { $file = $this->pathFor($file); @@ -478,13 +457,9 @@ public function getFilesizeFor($file) * @param integer $decimals Precision of number of decimal places. Default 0. * @return string|null Returns the formatted number or NULL. */ - public function formatFilesize($bytes, $decimals = 2) + public function formatFilesize($bytes, $decimals = 2): string { - if ($bytes === 0) { - $factor = 0; - } else { - $factor = floor((strlen($bytes) - 1) / 3); - } + $factor = $bytes === 0 ? 0 : floor((strlen((string) $bytes) - 1) / 3); $unit = [ 'B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB' ]; @@ -494,13 +469,11 @@ public function formatFilesize($bytes, $decimals = 2) $factor = 0; } - return sprintf('%.' . $decimals . 'f', ($bytes / pow(1024, $factor))) . ' ' . $unit[$factor]; + return sprintf('%.' . $decimals . 'f', ($bytes / 1024 ** $factor)) . ' ' . $unit[$factor]; } - /** - * @return array - */ - public function validationMethods() + #[\Override] + public function validationMethods(): array { $parentMethods = parent::validationMethods(); @@ -526,7 +499,7 @@ public function validateMimetypes() $files = $this->parseValAsFileList($this->val()); - if (empty($files)) { + if ($files === []) { return true; } @@ -572,7 +545,7 @@ public function validateFilesizes() $files = $this->parseValAsFileList($this->val()); - if (empty($files)) { + if ($files === []) { return true; } @@ -611,7 +584,7 @@ public function validateFilesizes() * @param mixed $value A multi-dimensional variable. * @return string[] The array of values. */ - public function parseValAsFileList($value) + public function parseValAsFileList($value): array { $files = []; @@ -620,14 +593,12 @@ public function parseValAsFileList($value) } $array = $this->parseValAsMultiple($value); - array_walk_recursive($array, function ($item) use (&$files) { + array_walk_recursive($array, function ($item) use (&$files): void { $array = $this->parseValAsMultiple($item); $files = array_merge($files, $array); }); - $files = array_filter($files, function ($file) { - return is_string($file) && isset($file[0]); - }); + $files = array_filter($files, fn($file): bool => is_string($file) && isset($file[0])); $files = array_unique($files); $files = array_values($files); @@ -642,7 +613,7 @@ public function parseValAsFileList($value) * @see StorablePropertyTrait::sqlType() * @return string The SQL type */ - public function sqlType() + public function sqlType(): string { // Multiple strings are always stored as TEXT because they can hold multiple values if ($this['multiple']) { @@ -654,9 +625,8 @@ public function sqlType() /** * @see StorablePropertyTrait::sqlPdoType() - * @return integer */ - public function sqlPdoType() + public function sqlPdoType(): int { return PDO::PARAM_STR; } @@ -667,13 +637,10 @@ public function sqlPdoType() * @param mixed $val The value, at time of saving. * @return mixed */ + #[\Override] public function save($val) { - if ($val instanceof Translation) { - $values = $val->data(); - } else { - $values = $val; - } + $values = $val instanceof Translation ? $val->data() : $val; $uploadedFiles = $this->getUploadedFiles(); @@ -689,7 +656,7 @@ public function save($val) $parsedFiles = $this->saveFileUploads($uploadedFiles[$lang]); } - if (empty($parsedFiles)) { + if ($parsedFiles === []) { $parsedFiles = $this->saveDataUploads($values[$lang]); } @@ -702,7 +669,7 @@ public function save($val) $parsedFiles = $this->saveFileUploads($uploadedFiles); } - if (empty($parsedFiles)) { + if ($parsedFiles === []) { $parsedFiles = $this->saveDataUploads($values); } @@ -719,7 +686,7 @@ public function save($val) * @param mixed $values One or more data URIs, data entries, or processed file paths. * @return string|string[] One or more paths to the processed uploaded files. */ - protected function saveDataUploads($values) + protected function saveDataUploads($values): array { // Bag value if singular if (!is_array($values) || isset($values['id'])) { @@ -731,21 +698,18 @@ protected function saveDataUploads($values) if ($this->isDataArr($value) || $this->isDataUri($value)) { try { $path = $this->dataUpload($value); - if ($path !== null) { - $parsed[] = $path; - - $this->logger->notice(sprintf( - 'File [%s] uploaded succesfully', - $path - )); - } + $parsed[] = $path; + $this->logger->notice(sprintf( + 'File [%s] uploaded succesfully', + $path + )); } catch (Exception $e) { $this->logger->warning(sprintf( 'Upload error on data URI: %s', $e->getMessage() )); } - } elseif (is_string($value) && !empty($value)) { + } elseif (is_string($value) && ($value !== '' && $value !== '0')) { $parsed[] = $value; } } @@ -759,7 +723,7 @@ protected function saveDataUploads($values) * @param mixed $files One or more normalized $_FILE entries. * @return string[] One or more paths to the processed uploaded files. */ - protected function saveFileUploads($files) + protected function saveFileUploads(array $files): array { // Bag value if singular if (isset($files['error'])) { @@ -771,14 +735,11 @@ protected function saveFileUploads($files) if (isset($file['error'])) { try { $path = $this->fileUpload($file); - if ($path !== null) { - $parsed[] = $path; - - $this->logger->notice(sprintf( - 'File [%s] uploaded succesfully', - $path - )); - } + $parsed[] = $path; + $this->logger->notice(sprintf( + 'File [%s] uploaded succesfully', + $path + )); } catch (Exception $e) { $this->logger->warning(sprintf( 'Upload error on file [%s]: %s', @@ -807,10 +768,8 @@ protected function parseSavedValues($saved, $default = null) if (!is_array($values)) { $values = empty($values) && !is_numeric($values) ? [] : [ $values ]; } - } else { - if (is_array($values)) { - $values = reset($values); - } + } elseif (is_array($values)) { + $values = reset($values); } return $values; @@ -825,7 +784,7 @@ protected function parseSavedValues($saved, $default = null) * @throws Exception If the upload fails or the $data is bad. * @return string|null The file path to the uploaded data. */ - public function dataUpload($data) + public function dataUpload($data): string { $filename = null; $contents = false; @@ -849,7 +808,7 @@ public function dataUpload($data) $contents = file_get_contents($tmpFile); - if (strlen($data['name']) > 0) { + if ((string) $data['name'] !== '') { $filename = $data['name']; } @@ -898,9 +857,8 @@ public function dataUpload($data) } $basePath = $this->basePath(); - $targetPath = str_replace($basePath, '', $targetPath); - return $targetPath; + return str_replace($basePath, '', $targetPath); } /** @@ -914,7 +872,7 @@ public function dataUpload($data) * @throws Exception If the upload fails or the $file is bad. * @return string|null The file path to the uploaded file. */ - public function fileUpload(array $file) + public function fileUpload(array $file): string { if (!isset($file['tmp_name'], $file['name'], $file['size'], $file['error'])) { throw new InvalidArgumentException( @@ -970,9 +928,8 @@ public function fileUpload(array $file) } $basePath = $this->basePath(); - $targetPath = str_replace($basePath, '', $targetPath); - return $targetPath; + return str_replace($basePath, '', $targetPath); } /** @@ -983,19 +940,14 @@ public function fileUpload(array $file) * * @param string|null $filename Optional. The filename to save as. * If NULL, a default filename will be generated. - * @return string */ - public function uploadTarget($filename = null) + public function uploadTarget($filename = null): string { $this->assertValidUploadPath(); $uploadPath = $this->pathFor($this['uploadPath']); - if ($filename === null) { - $filename = $this->generateFilename(); - } else { - $filename = $this->sanitizeFilename($filename); - } + $filename = $filename === null ? $this->generateFilename() : $this->sanitizeFilename($filename); $targetPath = $uploadPath . '/' . $filename; @@ -1021,9 +973,8 @@ public function uploadTarget($filename = null) * * @param string $file The full file to check. * @param boolean $caseInsensitive Case-insensitive by default. - * @return boolean */ - public function fileExists($file, $caseInsensitive = true) + public function fileExists($file, $caseInsensitive = true): bool { $file = (string)$file; @@ -1059,7 +1010,7 @@ public function fileExists($file, $caseInsensitive = true) * @throws Exception If the filename is invalid. * @return string The sanitized filename. */ - public function sanitizeFilename($filename) + public function sanitizeFilename($filename): string { // Remove blacklisted caharacters $blacklist = [ '/', '\\', '\0', '*', ':', '?', '"', '<', '>', '|', '#', '&', '!', '`', ' ' ]; @@ -1068,7 +1019,7 @@ public function sanitizeFilename($filename) // Avoid hidden file or trailing dot $filename = trim($filename, '.'); - if (strlen($filename) === 0) { + if ($filename === '') { throw new Exception( 'Bad file name after sanitization' ); @@ -1090,19 +1041,19 @@ public function sanitizeFilename($filename) * @throws UnexpectedValueException If the renaming failed. * @return string Returns the rendered target. */ - public function renderFileRenamePattern($from, $to, $args = null) + public function renderFileRenamePattern($from, $to, $args = null): string { if (!is_string($from)) { throw new InvalidArgumentException(sprintf( 'The target to rename must be a string, received %s', - (is_object($from) ? get_class($from) : gettype($from)) + (get_debug_type($from)) )); } if (!is_string($to)) { throw new InvalidArgumentException(sprintf( 'The rename pattern must be a string, received %s', - (is_object($to) ? get_class($to) : gettype($to)) + (get_debug_type($to)) )); } @@ -1110,7 +1061,7 @@ public function renderFileRenamePattern($from, $to, $args = null) $args = $this->renamePatternArgs($info, $args); $to = strtr($to, $args); - if (strpos($to, '{{') !== false) { + if (str_contains($to, '{{')) { preg_match_all('~\{\{\s*(.*?)\s*\}\}~i', $to, $matches); throw new UnexpectedValueException(sprintf( @@ -1119,18 +1070,15 @@ public function renderFileRenamePattern($from, $to, $args = null) )); } - $to = str_replace($info['basename'], $to, $from); - - return $to; + return str_replace($info['basename'], $to, $from); } /** * Generate a new filename from the property. * * @param string|null $extension An extension to append to the generated filename. - * @return string */ - public function generateFilename($extension = null) + public function generateFilename($extension = null): string { $filename = $this->sanitizeFilename($this['fallbackFilename']); $filename = $filename . ' ' . date('Y-m-d\TH-i-s'); @@ -1147,26 +1095,21 @@ public function generateFilename($extension = null) * * @param string|array $filename The filename to alter. * @throws InvalidArgumentException If the given filename is invalid. - * @return string */ - public function generateUniqueFilename($filename) + public function generateUniqueFilename($filename): string { - if (is_string($filename)) { - $info = pathinfo($filename); - } else { - $info = $filename; - } + $info = is_string($filename) ? pathinfo($filename) : $filename; - if (!isset($info['filename']) || strlen($info['filename']) === 0) { + if (!isset($info['filename']) || (string) $info['filename'] === '') { throw new InvalidArgumentException(sprintf( 'File must be a string [file path] or an array [pathfino()], received %s', - (is_object($filename) ? get_class($filename) : gettype($filename)) + (get_debug_type($filename)) )); } $filename = $info['filename'] . '-' . uniqid(); - if (isset($info['extension']) && strlen($info['extension']) > 0) { + if (isset($info['extension']) && (string) $info['extension'] !== '') { $filename .= '.' . $info['extension']; } @@ -1180,7 +1123,7 @@ public function generateUniqueFilename($filename) * * @return string Returns the file extension based on the MIME type for the first value. */ - public function generateExtension() + public function generateExtension(): ?string { $type = $this->getMimetype(); @@ -1213,7 +1156,7 @@ public function generateExtensionFromFile($file) return null; } - if (strpos($ext, '/') !== false) { + if (str_contains($ext, '/')) { $ext = explode('/', $ext); $ext = reset($ext); } @@ -1227,7 +1170,7 @@ public function generateExtensionFromFile($file) * @param string $type The MIME type to parse. * @return string|null The extension based on the MIME type. */ - public function generateExtensionFromMimeType($type) + public function generateExtensionFromMimeType($type): ?string { if (in_array($type, $this->getAcceptedMimetypes())) { return $this->resolveExtensionFromMimeType($type); @@ -1244,21 +1187,18 @@ public function generateExtensionFromMimeType($type) * @param string $type The MIME type to resolve. * @return string|null The extension based on the MIME type. */ - protected function resolveExtensionFromMimeType($type) + protected function resolveExtensionFromMimeType($type): ?string { - switch ($type) { - case 'text/plain': - return 'txt'; - } - - return null; + return match ($type) { + 'text/plain' => 'txt', + default => null, + }; } /** * @param mixed $fallback The fallback filename. - * @return self */ - public function setFallbackFilename($fallback) + public function setFallbackFilename($fallback): static { $this->fallbackFilename = $this->translator()->translation($fallback); return $this; @@ -1286,9 +1226,8 @@ public function getFilesystem() /** * @param string $filesystem The file system. - * @return self */ - public function setFilesystem($filesystem) + public function setFilesystem($filesystem): static { $this->filesystem = $filesystem; @@ -1301,6 +1240,7 @@ public function setFilesystem($filesystem) * @param Container $container A dependencies container instance. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -1332,9 +1272,8 @@ protected function basePath() * it will be prepended. * * @param string $path The end path. - * @return string */ - protected function pathFor($path) + protected function pathFor($path): string { $path = trim($path, '/'); @@ -1392,7 +1331,7 @@ protected function parseIniSize($size) $size = preg_replace('/[^0-9\.]/', '', $size); if ($unit) { - $size = ($size * pow(1024, stripos($quant, $unit[0]))); + $size *= 1024 ** stripos($quant, $unit[0]); } return round($size); @@ -1407,7 +1346,7 @@ protected function parseIniSize($size) * @return boolean Returns TRUE if the MIME type is acceptable. * Otherwise, returns FALSE. */ - protected function isAcceptedMimeType($type, array $accepted = null) + protected function isAcceptedMimeType($type, ?array $accepted = null) { if ($accepted === null) { $accepted = $this['acceptedMimetypes']; @@ -1452,7 +1391,7 @@ protected function isAcceptedFilesize($size, $max = null) * @param string $file A file path. * @return boolean Returns TRUE if the given path is absolute. Otherwise, returns FALSE. */ - protected function isAbsolutePath($file) + protected function isAbsolutePath($file): bool { $file = (string)$file; @@ -1468,9 +1407,8 @@ protected function isAbsolutePath($file) * Determine if the given value is a data URI. * * @param mixed $val The value to check. - * @return boolean */ - protected function isDataUri($val) + protected function isDataUri($val): bool { return is_string($val) && preg_match('/^data:/i', $val); } @@ -1479,9 +1417,8 @@ protected function isDataUri($val) * Determine if the given value is a data array. * * @param mixed $val The value to check. - * @return boolean */ - protected function isDataArr($val) + protected function isDataArr($val): bool { return is_array($val) && isset($val['id']); } @@ -1495,20 +1432,16 @@ protected function isDataArr($val) * @throws UnexpectedValueException If the given path is invalid. * @return string Returns the rendered target. */ - private function renamePatternArgs($path, $args = null) + private function renamePatternArgs(string|array $path, $args = null) { if (!is_string($path) && !is_array($path)) { throw new InvalidArgumentException(sprintf( 'The target must be a string or an array from [pathfino()], received %s', - (is_object($path) ? get_class($path) : gettype($path)) + (get_debug_type($path)) )); } - if (is_string($path)) { - $info = pathinfo($path); - } else { - $info = $path; - } + $info = is_string($path) ? pathinfo($path) : $path; if (!isset($info['basename']) || $info['basename'] === '') { throw new UnexpectedValueException( @@ -1554,7 +1487,7 @@ private function renamePatternArgs($path, $args = null) } else { throw new InvalidArgumentException(sprintf( 'Arguments must be an array or a callable that returns an array, received %s', - (is_object($args) ? get_class($args) : gettype($args)) + (get_debug_type($args)) )); } } @@ -1571,12 +1504,9 @@ public function getUploadedFiles() { $propIdent = $this->ident(); - $filterErrNoFile = function (array $file) { - return $file['error'] !== UPLOAD_ERR_NO_FILE; - }; - $uploadedFiles = static::parseUploadedFiles($_FILES, $filterErrNoFile, $propIdent); + $filterErrNoFile = (fn(array $file): bool => $file['error'] !== UPLOAD_ERR_NO_FILE); - return $uploadedFiles; + return static::parseUploadedFiles($_FILES, $filterErrNoFile, $propIdent); } /** @@ -1592,7 +1522,7 @@ public function getUploadedFiles() * @param mixed $searchKey If specified, then only top-level keys containing these values are returned. * @return array A tree of normalized $_FILE entries. */ - public static function parseUploadedFiles(array $uploadedFiles, callable $filterCallback = null, $searchKey = null) + public static function parseUploadedFiles(array $uploadedFiles, ?callable $filterCallback = null, $searchKey = null) { if ($searchKey !== null) { if (is_array($searchKey)) { @@ -1637,15 +1567,15 @@ public static function parseUploadedFiles(array $uploadedFiles, callable $filter $parsedFiles[$field] = [ 'tmp_name' => $uploadedFile['tmp_name'], - 'name' => isset($uploadedFile['name']) ? $uploadedFile['name'] : null, - 'type' => isset($uploadedFile['type']) ? $uploadedFile['type'] : null, - 'size' => isset($uploadedFile['size']) ? $uploadedFile['size'] : null, + 'name' => $uploadedFile['name'] ?? null, + 'type' => $uploadedFile['type'] ?? null, + 'size' => $uploadedFile['size'] ?? null, 'error' => $uploadedFile['error'], ]; } } else { $subArray = []; - foreach ($uploadedFile['error'] as $fileIdx => $error) { + foreach (array_keys($uploadedFile['error']) as $fileIdx) { // normalise subarray and re-parse to move the input's keyname up a level $subArray[$fileIdx] = [ 'tmp_name' => $uploadedFile['tmp_name'][$fileIdx], @@ -1682,7 +1612,7 @@ public static function parseUploadedFiles(array $uploadedFiles, callable $filter * @param string $encoding The name of the path iconv() encoding. * @return string The path, normalised. */ - public static function normalizePath($path, $encoding = 'UTF-8') + public static function normalizePath($path, string $encoding = 'UTF-8'): string|false { $key = $path; @@ -1693,12 +1623,12 @@ public static function normalizePath($path, $encoding = 'UTF-8') // Attempt to avoid path encoding problems. $path = iconv($encoding, $encoding . '//IGNORE//TRANSLIT', $path); - if (strpos($path, '..') !== false || strpos($path, './') !== false) { + if (str_contains($path, '..') || str_contains($path, './')) { // Process the components $parts = explode('/', $path); $safe = []; - foreach ($parts as $idx => $part) { - if ((empty($part) && !is_numeric($part)) || ($part === '.')) { + foreach ($parts as $part) { + if ((($part === '' || $part === '0') && !is_numeric($part)) || ($part === '.')) { continue; } elseif ($part === '..') { array_pop($safe); diff --git a/packages/property/src/Charcoal/Property/GenericProperty.php b/packages/property/src/Charcoal/Property/GenericProperty.php index 3028015a3..ce81a0a23 100644 --- a/packages/property/src/Charcoal/Property/GenericProperty.php +++ b/packages/property/src/Charcoal/Property/GenericProperty.php @@ -1,5 +1,7 @@ filesystem = $filesystem; @@ -57,9 +57,9 @@ public function setFilesystem($filesystem) * Unlike strings' default upper limit of 255, HTML has no default max length (0). * * @see StringProperty::defaultMaxLength() - * @return integer */ - public function defaultMaxLength() + #[\Override] + public function defaultMaxLength(): int { return 0; } @@ -68,9 +68,9 @@ public function defaultMaxLength() * Unlike the parent's String Property, HTML property obviously always allow HTML. * * @see StringProperty::allowHtml() - * @return boolean */ - public function getAllowHtml() + #[\Override] + public function getAllowHtml(): bool { return true; } diff --git a/packages/property/src/Charcoal/Property/IdProperty.php b/packages/property/src/Charcoal/Property/IdProperty.php index 732cc77f3..ad75156ed 100644 --- a/packages/property/src/Charcoal/Property/IdProperty.php +++ b/packages/property/src/Charcoal/Property/IdProperty.php @@ -35,10 +35,8 @@ class IdProperty extends AbstractProperty /** * Retrieve the property type. - * - * @return string */ - public function type() + public function type(): string { return 'id'; } @@ -51,12 +49,12 @@ public function type() * @see AbstractProperty::setMultiple() * @param boolean $flag The multiple flag. * @throws InvalidArgumentException If the multiple argument is TRUE (must be FALSE). - * @return self */ - public function setMultiple($flag) + #[\Override] + public function setMultiple($flag): static { - $flag = !!$flag; - if ($flag === true) { + $flag = (bool) $flag; + if ($flag) { throw new InvalidArgumentException( 'The ID property does not support multiple values.' ); @@ -69,9 +67,9 @@ public function setMultiple($flag) * Multiple is always FALSE for ID property. * * @see AbstractProperty::getMultiple() - * @return boolean */ - public function getMultiple() + #[\Override] + public function getMultiple(): bool { return false; } @@ -82,13 +80,13 @@ public function getMultiple() * @see AbstractProperty::setL10n() * @param boolean $flag The l10n, or "translatable" flag. * @throws InvalidArgumentException If the L10N argument is TRUE (must be FALSE). - * @return self */ - public function setL10n($flag) + #[\Override] + public function setL10n($flag): static { - $flag = !!$flag; + $flag = (bool) $flag; - if ($flag === true) { + if ($flag) { throw new InvalidArgumentException( 'The ID property can not be translatable.' ); @@ -101,19 +99,17 @@ public function setL10n($flag) * L10N is always FALSE for ID property. * * @see AbstractProperty::getL10n() - * @return boolean */ - public function getL10n() + #[\Override] + public function getL10n(): bool { return false; } /** * Retrieve the available ID modes. - * - * @return array */ - public function availableModes() + public function availableModes(): array { return [ self::MODE_AUTO_INCREMENT, @@ -128,9 +124,8 @@ public function availableModes() * * @param string $mode The ID mode ("auto-increment", "custom", "uniqid" or "uuid"). * @throws InvalidArgumentException If the mode is not one of the 4 valid modes. - * @return self */ - public function setMode($mode) + public function setMode($mode): static { $availableModes = $this->availableModes(); if (!in_array($mode, $availableModes)) { @@ -163,6 +158,7 @@ public function getMode() * @param mixed $val The value, at time of saving. * @return mixed */ + #[\Override] public function save($val) { if (!$val) { @@ -175,6 +171,7 @@ public function save($val) /** * @return boolean */ + #[\Override] public function validateRequired() { $mode = $this->getMode(); @@ -199,9 +196,8 @@ public function validateRequired() * - A random RFC-4122 UUID value. * * @throws DomainException If the mode does not have a value generator. - * @return string|null */ - public function autoGenerate() + public function autoGenerate(): ?string { $mode = $this['mode']; @@ -218,9 +214,8 @@ public function autoGenerate() * Generate a RFC-4122 v4 Universally-Unique Identifier (UUID). * * @see http://tools.ietf.org/html/rfc4122#section-4.4 - * @return string */ - private function generateUuid() + private function generateUuid(): string { // Generate a uniq string identifer (valid v4 uuid) return sprintf( @@ -249,7 +244,7 @@ private function generateUuid() * @see StorablePropertyTrait::sqlExtra() * @return string */ - public function sqlExtra() + public function sqlExtra(): null { $mode = $this->getMode(); @@ -271,7 +266,7 @@ public function sqlExtra() * @see StorablePropertyTrait::sqlType() * @return string The SQL type. */ - public function sqlType() + public function sqlType(): ?string { $mode = $this->getMode(); @@ -289,15 +284,15 @@ public function sqlType() } elseif ($mode === self::MODE_CUSTOM) { return 'VARCHAR(255)'; } + return null; } /** * Get the PDO data type. * * @see StorablePropertyTrait::sqlPdoType() - * @return integer */ - public function sqlPdoType() + public function sqlPdoType(): int { $mode = $this->getMode(); diff --git a/packages/property/src/Charcoal/Property/ImageProperty.php b/packages/property/src/Charcoal/Property/ImageProperty.php index 970d30e0c..405755d2f 100644 --- a/packages/property/src/Charcoal/Property/ImageProperty.php +++ b/packages/property/src/Charcoal/Property/ImageProperty.php @@ -42,34 +42,26 @@ class ImageProperty extends FileProperty /** * The type of image processing engine. - * - * @var string */ - private $driverType = self::DEFAULT_DRIVER_TYPE; + private string $driverType = self::DEFAULT_DRIVER_TYPE; /** * Internal storage of the image factory instance. - * - * @var ImageFactory */ - private $imageFactory; + private ?\Charcoal\Image\ImageFactory $imageFactory = null; - /** - * @return string - */ - public function type() + #[\Override] + public function type(): string { return 'image'; } /** * Retrieve the image factory. - * - * @return ImageFactory */ - public function imageFactory() + public function imageFactory(): \Charcoal\Image\ImageFactory { - if ($this->imageFactory === null) { + if (!$this->imageFactory instanceof \Charcoal\Image\ImageFactory) { $this->imageFactory = $this->createImageFactory(); } @@ -83,12 +75,12 @@ public function imageFactory() * @throws InvalidArgumentException If the drive type is not a string. * @return ImageProperty Chainable */ - public function setDriverType($type) + public function setDriverType($type): static { if (!is_string($type)) { throw new InvalidArgumentException(sprintf( 'Image driver type must be a string, received %s', - (is_object($type) ? get_class($type) : gettype($type)) + (get_debug_type($type)) )); } @@ -99,10 +91,8 @@ public function setDriverType($type) /** * Retrieve the name of the property's image processing driver. - * - * @return string */ - public function getDriverType() + public function getDriverType(): string { return $this->driverType; } @@ -114,7 +104,7 @@ public function getDriverType() * @throws OutOfBoundsException If the effects event does not exist. * @return ImageProperty Chainable */ - public function setApplyEffects($event) + public function setApplyEffects($event): static { if ($event === false) { $this->applyEffects = self::EFFECTS_EVENT_NEVER; @@ -128,7 +118,7 @@ public function setApplyEffects($event) if (!in_array($event, $this->acceptedEffectsEvents())) { if (!is_string($event)) { - $event = (is_object($event) ? get_class($event) : gettype($event)); + $event = (get_debug_type($event)); } throw new OutOfBoundsException(sprintf( 'Unsupported image property event "%s" provided', @@ -158,11 +148,11 @@ public function getApplyEffects() * @throws OutOfBoundsException If the effects event does not exist. * @return mixed Returns TRUE or FALSE if the property applies effects for the given event. */ - public function canApplyEffects($event) + public function canApplyEffects($event): bool { if (!in_array($event, $this->acceptedEffectsEvents())) { if (!is_string($event)) { - $event = (is_object($event) ? get_class($event) : gettype($event)); + $event = (get_debug_type($event)); } throw new OutOfBoundsException(sprintf( 'Unsupported image property event "%s" provided', @@ -175,10 +165,8 @@ public function canApplyEffects($event) /** * Retrieve the supported events where effects can be applied. - * - * @return array */ - public function acceptedEffectsEvents() + public function acceptedEffectsEvents(): array { return [ self::EFFECTS_EVENT_UPLOAD, @@ -193,7 +181,7 @@ public function acceptedEffectsEvents() * @param array $effects The effects to set to the image. * @return ImageProperty Chainable */ - public function setEffects(array $effects) + public function setEffects(array $effects): static { $this->effects = []; foreach ($effects as $effect) { @@ -206,7 +194,7 @@ public function setEffects(array $effects) * @param mixed $effect An image effect. * @return ImageProperty Chainable */ - public function addEffect($effect) + public function addEffect($effect): static { $this->effects[] = $effect; return $this; @@ -229,7 +217,7 @@ public function getEffects() * @return mixed Returns the given images. Depending on the effects applied, * certain images might be renamed. */ - public function processEffects($value, array $effects = null, ImageInterface $image = null) + public function processEffects($value, ?array $effects = null, ?ImageInterface $image = null) { $value = $this->parseVal($value); if ($value instanceof Translation) { @@ -240,8 +228,8 @@ public function processEffects($value, array $effects = null, ImageInterface $im $effects = $this->batchEffects(); } - if ($effects) { - if ($image === null) { + if ($effects !== []) { + if (!$image instanceof \Charcoal\Image\ImageInterface) { $image = $this->createImage(); } if (is_array($value)) { @@ -263,7 +251,8 @@ public function processEffects($value, array $effects = null, ImageInterface $im * * @return string[] */ - public function getDefaultAcceptedMimetypes() + #[\Override] + public function getDefaultAcceptedMimetypes(): array { return [ 'image/gif', @@ -283,35 +272,24 @@ public function getDefaultAcceptedMimetypes() * @param string $type The MIME type to resolve. * @return string|null The extension based on the MIME type. */ - protected function resolveExtensionFromMimeType($type) + #[\Override] + protected function resolveExtensionFromMimeType($type): ?string { - switch ($type) { - case 'image/gif': - return 'gif'; - - case 'image/jpg': - case 'image/jpeg': - case 'image/pjpeg': - return 'jpg'; - - case 'image/png': - return 'png'; - - case 'image/svg+xml': - case 'image/svg': - return 'svg'; - - case 'image/webp': - return 'webp'; - } - - return null; + return match ($type) { + 'image/gif' => 'gif', + 'image/jpg', 'image/jpeg', 'image/pjpeg' => 'jpg', + 'image/png' => 'png', + 'image/svg+xml', 'image/svg' => 'svg', + 'image/webp' => 'webp', + default => null, + }; } /** * @param mixed $val The value, at time of saving. * @return mixed */ + #[\Override] public function save($val) { $val = parent::save($val); @@ -328,9 +306,9 @@ public function save($val) * * @see FileProperty::fileUpload() * @param string $fileData The file data, raw. - * @return string */ - public function dataUpload($fileData) + #[\Override] + public function dataUpload($fileData): string { $target = parent::dataUpload($fileData); @@ -346,9 +324,9 @@ public function dataUpload($fileData) * * @see FileProperty::fileUpload() * @param array $fileData The file data to upload. - * @return string */ - public function fileUpload(array $fileData) + #[\Override] + public function fileUpload(array $fileData): string { $target = parent::fileUpload($fileData); @@ -363,9 +341,8 @@ public function fileUpload(array $fileData) * Set an image factory. * * @param ImageFactory $factory The image factory, to manipulate images. - * @return self */ - protected function setImageFactory(ImageFactory $factory) + protected function setImageFactory(ImageFactory $factory): static { $this->imageFactory = $factory; @@ -374,10 +351,8 @@ protected function setImageFactory(ImageFactory $factory) /** * Create an image factory. - * - * @return ImageFactory */ - protected function createImageFactory() + protected function createImageFactory(): \Charcoal\Image\ImageFactory { return new ImageFactory(); } @@ -392,10 +367,7 @@ protected function createImage() return $this->imageFactory()->create($this['driverType']); } - /** - * @return array - */ - protected function batchEffects() + protected function batchEffects(): array { $effects = $this['effects']; $grouped = []; @@ -438,7 +410,7 @@ protected function batchEffects() } } - if (empty($grouped)) { + if ($grouped === []) { $grouped[] = $fxGroup; } } @@ -455,7 +427,7 @@ protected function batchEffects() * @throws InvalidArgumentException If the $value is not a string. * @return mixed Returns the processed target or NULL. */ - private function processEffectsOne($value, array $effects = null, ImageInterface $image = null) + private function processEffectsOne($value, ?array $effects = null, ?ImageInterface $image = null): ?string { if ($value === null || $value === '') { return null; @@ -464,7 +436,7 @@ private function processEffectsOne($value, array $effects = null, ImageInterface if (!is_string($value)) { throw new InvalidArgumentException(sprintf( 'Target image must be a string, received %s', - (is_object($value) ? get_class($value) : gettype($value)) + (get_debug_type($value)) )); } @@ -472,7 +444,7 @@ private function processEffectsOne($value, array $effects = null, ImageInterface return $value; } - if ($image === null) { + if (!$image instanceof \Charcoal\Image\ImageInterface) { $image = $this->createImage(); } @@ -480,7 +452,7 @@ private function processEffectsOne($value, array $effects = null, ImageInterface $effects = $this->batchEffects(); } - if ($effects) { + if ($effects !== []) { $basePath = $this->basePath() . DIRECTORY_SEPARATOR; $isAbsolute = false; @@ -514,18 +486,16 @@ private function processEffectsOne($value, array $effects = null, ImageInterface } break; } + } elseif (is_string($fxGroup['condition'])) { + $this->logger->warning(sprintf( + '[Image Property] Unsupported conditional effect: \'%s\'', + $fxGroup['condition'] + )); } else { - if (is_string($fxGroup['condition'])) { - $this->logger->warning(sprintf( - '[Image Property] Unsupported conditional effect: \'%s\'', - $fxGroup['condition'] - )); - } else { - $this->logger->warning(sprintf( - '[Image Property] Invalid conditional effect: \'%s\'', - gettype($fxGroup['condition']) - )); - } + $this->logger->warning(sprintf( + '[Image Property] Invalid conditional effect: \'%s\'', + gettype($fxGroup['condition']) + )); } } elseif ($fxGroup['save']) { $rename = $fxGroup['rename']; diff --git a/packages/property/src/Charcoal/Property/IpProperty.php b/packages/property/src/Charcoal/Property/IpProperty.php index 3c0223eea..c9cd7175b 100644 --- a/packages/property/src/Charcoal/Property/IpProperty.php +++ b/packages/property/src/Charcoal/Property/IpProperty.php @@ -19,17 +19,13 @@ class IpProperty extends AbstractProperty /** * The storage mode can be either "string" (default) or "int". - * - * @var string $storageMode */ - private $storageMode = self::DEFAULT_STORAGE_MODE; + private string $storageMode = self::DEFAULT_STORAGE_MODE; /** * Retrieve the property type. - * - * @return string */ - public function type() + public function type(): string { return 'ip'; } @@ -42,11 +38,12 @@ public function type() * @see AbstractProperty::setMultiple() * @return IdProperty Chainable */ - public function setMultiple($flag) + #[\Override] + public function setMultiple($flag): static { - $flag = !!$flag; + $flag = (bool) $flag; - if ($flag === true) { + if ($flag) { throw new InvalidArgumentException( 'The ID property does not support multiple values.' ); @@ -59,9 +56,9 @@ public function setMultiple($flag) * Multiple is always FALSE for ID property. * * @see AbstractProperty::getMultiple() - * @return boolean */ - public function getMultiple() + #[\Override] + public function getMultiple(): bool { return false; } @@ -74,11 +71,12 @@ public function getMultiple() * @see AbstractProperty::setL10n() * @return IdProperty Chainable */ - public function setL10n($flag) + #[\Override] + public function setL10n($flag): static { - $flag = !!$flag; + $flag = (bool) $flag; - if ($flag === true) { + if ($flag) { throw new InvalidArgumentException( 'The ID property is not translatable.' ); @@ -91,9 +89,9 @@ public function setL10n($flag) * L10N is always FALSE for IP property. * * @see AbstractProperty::getL10n() - * @return boolean */ - public function getL10n() + #[\Override] + public function getL10n(): bool { return false; } @@ -101,9 +99,8 @@ public function getL10n() /** * @param string $mode Either "string" or "int". * @throws InvalidArgumentException If the storage mode is invalid. - * @return self */ - public function setStorageMode($mode) + public function setStorageMode($mode): static { $validModes = [ self::STORAGE_MODE_STRING, @@ -118,10 +115,7 @@ public function setStorageMode($mode) return $this; } - /** - * @return string - */ - public function getStorageMode() + public function getStorageMode(): string { return $this->storageMode; } @@ -132,7 +126,7 @@ public function getStorageMode() * @param mixed $val The value to convert (if necessary) to integer. * @return integer */ - public function intVal($val) + public function intVal($val): int|false { if (is_numeric($val)) { return (int)$val; @@ -145,9 +139,8 @@ public function intVal($val) * Get the IP value as an string (IPv4 dotted format). * * @param mixed $val The value to convert to string. - * @return string */ - public function stringVal($val) + public function stringVal($val): string { if (is_string($val)) { return $val; @@ -163,7 +156,8 @@ public function stringVal($val) * @see StorablePropertyTrait::storageVal() * @return string */ - public function storageVal($val) + #[\Override] + public function storageVal($val): int|false|string { $mode = $this->getStorageMode(); @@ -180,7 +174,7 @@ public function storageVal($val) * @param mixed $val The value to convert to string. * @return string */ - public function hostname($val) + public function hostname($val): string|false { $val = $this->stringVal($val); return gethostbyaddr($val); @@ -188,9 +182,8 @@ public function hostname($val) /** * @see StorableProperyTrait:sqlType() - * @return string */ - public function sqlType() + public function sqlType(): string { $mode = $this->getStorageMode(); @@ -203,9 +196,8 @@ public function sqlType() /** * @see StorableProperyTrait::sqlPdoType() - * @return integer */ - public function sqlPdoType() + public function sqlPdoType(): int { $mode = $this->getStorageMode(); diff --git a/packages/property/src/Charcoal/Property/LangProperty.php b/packages/property/src/Charcoal/Property/LangProperty.php index 627a1c7df..ec08b3abb 100644 --- a/packages/property/src/Charcoal/Property/LangProperty.php +++ b/packages/property/src/Charcoal/Property/LangProperty.php @@ -19,10 +19,7 @@ class LangProperty extends AbstractProperty implements SelectablePropertyInterfa { use SelectablePropertyTrait; - /** - * @return string - */ - public function type() + public function type(): string { return 'lang'; } @@ -32,9 +29,8 @@ public function type() * * @param array $choices One or more choice structures. * @see SelectablePropertyTrait::setChoices() - * @return self */ - public function setChoices(array $choices) + public function setChoices(array $choices): static { unset($choices); @@ -50,9 +46,8 @@ public function setChoices(array $choices) * * @param array $choices One or more choice structures. * @see SelectablePropertyTrait::setChoices() - * @return self */ - public function addChoices(array $choices) + public function addChoices(array $choices): static { unset($choices); @@ -71,7 +66,7 @@ public function addChoices(array $choices) * @see SelectablePropertyTrait::addChoice() * @return LangProperty Chainable. */ - public function addChoice($choiceIdent, $choice) + public function addChoice($choiceIdent, $choice): static { unset($choiceIdent, $choice); @@ -86,11 +81,10 @@ public function addChoice($choiceIdent, $choice) * Determine if choices are available. * * @see SelectablePropertyTrait::hasChoices() - * @return boolean */ - public function hasChoices() + public function hasChoices(): bool { - return !!$this->translator()->locales(); + return (bool) $this->translator()->locales(); } /** @@ -98,9 +92,8 @@ public function hasChoices() * * @param string $choiceIdent The choice identifier to lookup. * @see SelectablePropertyTrait::hasChoice() - * @return boolean */ - public function hasChoice($choiceIdent) + public function hasChoice($choiceIdent): bool { if (empty($this->choices)) { $this->choices(); @@ -133,7 +126,7 @@ public function choices() } else { $trans = 'locale.' . $langCode; if ($trans === $this->translator()->translate($trans)) { - $label = strtoupper($langCode); + $label = strtoupper((string) $langCode); } else { $label = $this->translator()->translation($trans); } @@ -160,6 +153,7 @@ public function choices() * @param array $options Optional display options. * @return string */ + #[\Override] public function displayVal($val, array $options = []) { if ($val === null || $val === '') { @@ -179,10 +173,8 @@ public function displayVal($val, array $options = []) } /** Parse multiple values / ensure they are of array type. */ - if ($this['multiple']) { - if (!is_array($propertyValue)) { - $propertyValue = $this->parseValAsMultiple($propertyValue); - } + if ($this['multiple'] && !is_array($propertyValue)) { + $propertyValue = $this->parseValAsMultiple($propertyValue); } if (is_array($propertyValue)) { @@ -217,7 +209,7 @@ public function displayVal($val, array $options = []) * @see StorablePropertyTrait::sqlType() * @return string The SQL type */ - public function sqlType() + public function sqlType(): string { if ($this['multiple']) { return 'TEXT'; @@ -226,10 +218,7 @@ public function sqlType() return 'CHAR(2)'; } - /** - * @return integer - */ - public function sqlPdoType() + public function sqlPdoType(): int { return PDO::PARAM_STR; } diff --git a/packages/property/src/Charcoal/Property/MapStructureProperty.php b/packages/property/src/Charcoal/Property/MapStructureProperty.php index 47e8e0ca7..776bf0605 100644 --- a/packages/property/src/Charcoal/Property/MapStructureProperty.php +++ b/packages/property/src/Charcoal/Property/MapStructureProperty.php @@ -1,5 +1,7 @@ structureVal($val); $val = []; - if (!empty($objs)) { + if ($objs !== []) { $val = []; foreach ($objs as $obj) { if ($obj->validate() === false) { @@ -241,9 +230,8 @@ public function getStructureMetadata() * * @param MetadataInterface|array|null $data The property's structure (fields, data). * @throws InvalidArgumentException If the structure is invalid. - * @return self */ - public function setStructureMetadata($data) + public function setStructureMetadata($data): static { if ($data === null) { $this->structureMetadata = $data; @@ -260,7 +248,7 @@ public function setStructureMetadata($data) } else { throw new InvalidArgumentException(sprintf( 'Structure [%s] is invalid (must be array or an instance of %s).', - (is_object($data) ? get_class($data) : gettype($data)), + (get_debug_type($data)), StructureMetadata::class )); } @@ -272,12 +260,10 @@ public function setStructureMetadata($data) /** * Retrieve the metadata interfaces used by the property as a structure. - * - * @return array */ - public function getStructureInterfaces() + public function getStructureInterfaces(): array { - if (empty($this->structureInterfaces)) { + if ($this->structureInterfaces === []) { return $this->structureInterfaces; } @@ -286,21 +272,18 @@ public function getStructureInterfaces() /** * Determine if the property has any structure metadata interfaces. - * - * @return boolean */ - public function hasStructureInterfaces() + public function hasStructureInterfaces(): bool { - return !empty($this->structureInterfaces); + return $this->structureInterfaces !== []; } /** * Set the given metadata interfaces for the property to use as a structure. * * @param array $interfaces One or more metadata interfaces to use. - * @return self */ - public function setStructureInterfaces(array $interfaces) + public function setStructureInterfaces(array $interfaces): static { $this->structureInterfaces = []; @@ -313,9 +296,8 @@ public function setStructureInterfaces(array $interfaces) * Add the given metadata interfaces for the property to use as a structure. * * @param array $interfaces One or more metadata interfaces to use. - * @return self */ - public function addStructureInterfaces(array $interfaces) + public function addStructureInterfaces(array $interfaces): static { foreach ($interfaces as $interface) { $this->addStructureInterface($interface); @@ -329,18 +311,17 @@ public function addStructureInterfaces(array $interfaces) * * @param string $interface A metadata interface to use. * @throws InvalidArgumentException If the interface is not a string. - * @return self */ - public function addStructureInterface($interface) + public function addStructureInterface($interface): static { if (!is_string($interface)) { throw new InvalidArgumentException(sprintf( 'Structure interface must to be a string, received %s', - is_object($interface) ? get_class($interface) : gettype($interface) + get_debug_type($interface) )); } - if (!empty($interface)) { + if ($interface !== '' && $interface !== '0') { $interface = $this->parseStructureInterface($interface); $this->structureInterfaces[$interface] = true; @@ -368,7 +349,7 @@ protected function loadStructureMetadata() $structureInterfaces = (array)$this->getStructureModelType(); } - if (!empty($structureInterfaces)) { + if ($structureInterfaces !== []) { $metadataLoader = $this->metadataLoader(); $metadataClass = $this->getStructureMetadataClass(); @@ -417,10 +398,8 @@ public function structureProto() /** * Retrieve the default data-model structure class name. - * - * @return string */ - public static function getDefaultStructureModelClass() + public static function getDefaultStructureModelClass(): string { return StructureModel::class; } @@ -432,9 +411,8 @@ public static function getDefaultStructureModelClass() * * @param string $className The class name of the structure. * @throws InvalidArgumentException If the class name is invalid. - * @return self */ - protected function setStructureModelClass($className) + protected function setStructureModelClass($className): static { if ($className === null) { $this->structureModelClass = static::getDefaultStructureModelClass(); @@ -448,11 +426,11 @@ protected function setStructureModelClass($className) ); } - if (strpos($className, '/') !== false) { + if (str_contains($className, '/')) { try { $this->structureModelType = $className; $prototype = $this->structureModelFactory()->get($className); - $className = get_class($prototype); + $className = $prototype::class; } catch (\Exception $e) { throw new InvalidArgumentException(sprintf( 'Invalid structure class name: %s', @@ -469,10 +447,8 @@ protected function setStructureModelClass($className) /** * Determine if the property is using a custom data-model. - * - * @return boolean */ - public function hasCustomStructureModelClass() + public function hasCustomStructureModelClass(): bool { return $this->getStructureModelClass() !== static::getDefaultStructureModelClass(); } @@ -489,10 +465,8 @@ public function getStructureModelClass() /** * Retrieve the class name of the data-model structure. - * - * @return string */ - public function getStructureModelType() + public function getStructureModelType(): string { if ($this->structureModelType === null) { $this->structureModelType = $this->parseStructureInterface($this->getStructureModelClass()); @@ -532,7 +506,7 @@ public function structureVal($val, $options = []) } else { throw new InvalidArgumentException(sprintf( 'Structure value options must to be an array or an instance of %2$s, received %1$s', - is_object($options) ? get_class($options) : gettype($options), + get_debug_type($options), StructureMetadata::class )); } @@ -603,6 +577,7 @@ public function toModel($model = null) * @param mixed $val The value, at time of saving. * @return mixed */ + #[\Override] public function save($val) { $val = parent::save($val); @@ -612,7 +587,7 @@ public function save($val) if ($proto instanceof ModelInterface) { $objs = (array)$this->structureVal($val); $val = []; - if (!empty($objs)) { + if ($objs !== []) { $val = []; foreach ($objs as $obj) { $obj->saveProperties(); @@ -634,9 +609,9 @@ public function save($val) /** * @param mixed $val The value to to convert for display. * @param array $options Optional display options. - * @return string */ - public function displayVal($val, array $options = []) + #[\Override] + public function displayVal($val, array $options = []): string { if ($val === null || $val === '') { return ''; @@ -667,6 +642,7 @@ public function displayVal($val, array $options = []) * @param Container $container A dependencies container instance. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -682,10 +658,10 @@ protected function setDependencies(Container $container) */ protected function structureModelFactory() { - if (!isset($this->structureModelFactory)) { + if ($this->structureModelFactory === null) { throw new RuntimeException(sprintf( 'Model Factory is not defined for "%s"', - get_class($this) + static::class )); } @@ -696,9 +672,8 @@ protected function structureModelFactory() * Set an structure model factory. * * @param FactoryInterface $factory The model factory, to create objects. - * @return self */ - private function setStructureModelFactory(FactoryInterface $factory) + private function setStructureModelFactory(FactoryInterface $factory): static { $this->structureModelFactory = $factory; @@ -711,14 +686,12 @@ private function setStructureModelFactory(FactoryInterface $factory) * Change `\` and `.` to `/` and force lowercase * * @param string $interface A metadata interface to convert. - * @return string */ - protected function parseStructureInterface($interface) + protected function parseStructureInterface($interface): string { $ident = preg_replace('/([a-z])([A-Z])/', '$1-$2', $interface); - $ident = strtolower(str_replace('\\', '/', $ident)); - return $ident; + return strtolower(str_replace('\\', '/', $ident)); } /** @@ -736,10 +709,8 @@ protected function createStructureMetadata() /** * Retrieve the class name of the metadata object. - * - * @return string */ - protected function getStructureMetadataClass() + protected function getStructureMetadataClass(): string { return StructureMetadata::class; } @@ -783,17 +754,15 @@ private function createStructureModelWith( if (!$model instanceof DescribableInterface) { throw new UnexpectedValueException(sprintf( 'Structure [%s] must implement [%s]', - get_class($model), + $model::class, DescribableInterface::class )); } $model->setMetadata($metadata); - if ($datasets) { - foreach ($datasets as $data) { - $model->setData($data); - } + foreach ($datasets as $data) { + $model->setData($data); } return $model; diff --git a/packages/property/src/Charcoal/Property/MultiObjectProperty.php b/packages/property/src/Charcoal/Property/MultiObjectProperty.php index 107471a40..520798b40 100644 --- a/packages/property/src/Charcoal/Property/MultiObjectProperty.php +++ b/packages/property/src/Charcoal/Property/MultiObjectProperty.php @@ -9,26 +9,15 @@ */ class MultiObjectProperty extends AbstractProperty { - /** - * @var array $allowedTypes - */ - private $allowedTypes; - - /** - * @var boolean $groupedByType - */ - private $groupedByType = true; + private ?array $allowedTypes = null; - /** - * @var string $joinTable - */ - private $joinTable = 'charcoal_multi_objects'; + private string $joinTable = 'charcoal_multi_objects'; /** * @param array $types The allowed types map. * @return MultiObjectProperty Chainable */ - public function setAllowedTypes(array $types) + public function setAllowedTypes(array $types): static { foreach ($types as $type => $typeOptions) { $this->addAllowedType($type, $typeOptions); @@ -41,7 +30,7 @@ public function setAllowedTypes(array $types) * @param array $typeOptions Extra options for the type. * @return MultiObjectProperty Chainable */ - public function addAllowedType($type, array $typeOptions = []) + public function addAllowedType($type, array $typeOptions = []): static { $this->allowedTypes[$type] = $typeOptions; return $this; @@ -50,7 +39,7 @@ public function addAllowedType($type, array $typeOptions = []) /** * @return array */ - public function getAllowedTypes() + public function getAllowedTypes(): ?array { return $this->allowedTypes; } @@ -60,7 +49,7 @@ public function getAllowedTypes() * @throws InvalidArgumentException If the table is not a string or contains invalid table characters. * @return MultiObjectProperty Chainable */ - public function setJoinTable($table) + public function setJoinTable($table): static { if (!is_string($table)) { throw new InvalidArgumentException( @@ -69,7 +58,7 @@ public function setJoinTable($table) } // For security reason, only alphanumeric characters (+ underscores) are valid table names. // Although SQL can support more, there's really no reason to. - if (!preg_match('/[A-Za-z0-9_]/', $table)) { + if (!preg_match('/\w/', $table)) { throw new InvalidArgumentException( sprintf('Table name "%s" is invalid: must be alphanumeric / underscore.', $table) ); @@ -78,22 +67,17 @@ public function setJoinTable($table) return $this; } - /** - * @return string - */ - public function getJoinTable() + public function getJoinTable(): string { return $this->joinTable; } /** * Create the join table on the database source, if it does not exist. - * - * @return void */ - public function createJoinTable() + public function createJoinTable(): void { - if ($this->joinTableExists() === true) { + if ($this->joinTableExists()) { return; } @@ -109,23 +93,17 @@ public function createJoinTable() $this->source()->db()->query($q); } - /** - * @return boolean - */ - public function joinTableExists() + public function joinTableExists(): bool { $q = 'SHOW TABLES LIKE \'' . $this->getJoinTable() . '\''; $this->logger->debug($q); $res = $this->source()->db()->query($q); $tableExists = $res->fetchColumn(0); - return !!$tableExists; + return (bool) $tableExists; } - /** - * @return string - */ - public function type() + public function type(): string { return 'multi-object'; } @@ -133,15 +111,12 @@ public function type() /** * @return string|null */ - public function sqlType() + public function sqlType(): null { return null; } - /** - * @return integer - */ - public function sqlPdoType() + public function sqlPdoType(): int { return 0; } diff --git a/packages/property/src/Charcoal/Property/NumberProperty.php b/packages/property/src/Charcoal/Property/NumberProperty.php index 87b60ea82..3340f4890 100644 --- a/packages/property/src/Charcoal/Property/NumberProperty.php +++ b/packages/property/src/Charcoal/Property/NumberProperty.php @@ -29,10 +29,7 @@ class NumberProperty extends AbstractProperty */ private $max; - /** - * @return string - */ - public function type() + public function type(): string { return 'number'; } @@ -41,9 +38,8 @@ public function type() * Set the minimal value. * * @param mixed|null $min The minimal value. - * @return self */ - public function setMin($min) + public function setMin($min): static { $this->min = $min; return $this; @@ -63,9 +59,8 @@ public function getMin() * Set the maximal value. * * @param mixed|null $max The maximal value. - * @return self */ - public function setMax($max) + public function setMax($max): static { $this->max = $max; return $this; @@ -86,7 +81,8 @@ public function getMax() * * @return string[] */ - public function validationMethods() + #[\Override] + public function validationMethods(): array { $parentMethods = parent::validationMethods(); @@ -96,10 +92,8 @@ public function validationMethods() ]); } - /** - * @return boolean - */ - public function validateRequired() + #[\Override] + public function validateRequired(): bool { if ($this['required'] && !is_numeric($this->val())) { $this->validator()->error('Value is required.', 'required'); @@ -150,7 +144,7 @@ public function validateMax() * @see StorablePropertyTrait::sqlType() * @return string The SQL type */ - public function sqlType() + public function sqlType(): string { // Multiple number are stocked as TEXT because we do not know the maximum length if ($this['multiple']) { @@ -162,9 +156,8 @@ public function sqlType() /** * @see StorablePropertyTrait::sqlPdoType() - * @return integer */ - public function sqlPdoType() + public function sqlPdoType(): int { return PDO::PARAM_STR; } diff --git a/packages/property/src/Charcoal/Property/ObjectProperty.php b/packages/property/src/Charcoal/Property/ObjectProperty.php index 2a54c3b83..2d4656f2d 100644 --- a/packages/property/src/Charcoal/Property/ObjectProperty.php +++ b/packages/property/src/Charcoal/Property/ObjectProperty.php @@ -37,17 +37,13 @@ class ObjectProperty extends AbstractProperty implements SelectablePropertyInter /** * The object type to build the choices from. - * - * @var string */ - private $objType; + private ?string $objType = null; /** * The pattern for rendering the choice as a label. - * - * @var string */ - private $pattern = self::DEFAULT_PATTERN; + private string $pattern = self::DEFAULT_PATTERN; /** * The available selectable choices. @@ -60,10 +56,8 @@ class ObjectProperty extends AbstractProperty implements SelectablePropertyInter /** * Store the collection loader for the current class. - * - * @var CollectionLoader */ - private $collectionLoader; + private ?\Charcoal\Loader\CollectionLoader $collectionLoader = null; /** * The field which defines the data's model. @@ -95,10 +89,8 @@ class ObjectProperty extends AbstractProperty implements SelectablePropertyInter /** * Store the PSR-6 caching service. - * - * @var CacheItemPoolInterface */ - private $cachePool; + private ?\Psr\Cache\CacheItemPoolInterface $cachePool = null; /** * Store all model loaders. @@ -109,15 +101,10 @@ class ObjectProperty extends AbstractProperty implements SelectablePropertyInter /** * Store the factory instance for the current class. - * - * @var FactoryInterface */ - private $modelFactory; + private ?\Charcoal\Factory\FactoryInterface $modelFactory = null; - /** - * @return string - */ - public function type() + public function type(): string { return 'object'; } @@ -127,9 +114,8 @@ public function type() * * @param string $objType The object type. * @throws InvalidArgumentException If the object type is not a string. - * @return self */ - public function setObjType($objType) + public function setObjType($objType): static { if (!is_string($objType)) { throw new InvalidArgumentException(sprintf( @@ -148,9 +134,8 @@ public function setObjType($objType) * Retrieve the object type to build the choices from. * * @throws RuntimeException If the object type was not previously set. - * @return string */ - public function getObjType() + public function getObjType(): string { if ($this->objType === null) { throw new RuntimeException(sprintf( @@ -167,7 +152,7 @@ public function getObjType() * @throws InvalidArgumentException If the pattern is not a string. * @return ObjectProperty Chainable */ - public function setPattern($pattern) + public function setPattern($pattern): static { if (!is_string($pattern)) { throw new InvalidArgumentException( @@ -180,10 +165,7 @@ public function setPattern($pattern) return $this; } - /** - * @return string - */ - public function getPattern() + public function getPattern(): string { return $this->pattern; } @@ -226,6 +208,7 @@ public function sqlPdoType() * @param mixed $val Value to be parsed. * @return mixed */ + #[\Override] public function parseOne($val) { if ($val instanceof StorableInterface) { @@ -241,6 +224,7 @@ public function parseOne($val) * @param mixed $val Optional. The value to convert to storage value. * @return mixed */ + #[\Override] public function storageVal($val) { if ($val === null || $val === '') { @@ -250,10 +234,8 @@ public function storageVal($val) $val = $this->parseVal($val); - if ($this['multiple']) { - if (is_array($val)) { - $val = implode($this->multipleSeparator(), $val); - } + if ($this['multiple'] && is_array($val)) { + $val = implode($this->multipleSeparator(), $val); } if (!is_scalar($val)) { @@ -278,6 +260,7 @@ public function proto() * @param array $options Unused input options. * @return string */ + #[\Override] public function inputVal($val, array $options = []) { unset($options); @@ -292,10 +275,8 @@ public function inputVal($val, array $options = []) $val = $this->parseVal($val); - if ($this['multiple']) { - if (is_array($val)) { - $val = implode($this->multipleSeparator(), $val); - } + if ($this['multiple'] && is_array($val)) { + $val = implode($this->multipleSeparator(), $val); } if (!is_scalar($val)) { @@ -310,28 +291,21 @@ public function inputVal($val, array $options = []) * @param array $options Optional display options. * @return string */ + #[\Override] public function displayVal($val, array $options = []) { if ($val === null) { return ''; } - if (isset($options['pattern'])) { - $pattern = $options['pattern']; - } else { - $pattern = null; - } + $pattern = $options['pattern'] ?? null; - if (isset($options['lang'])) { - $lang = $options['lang']; - } else { - $lang = null; - } + $lang = $options['lang'] ?? null; if ($val instanceof ModelInterface) { $propertyVal = $this->renderObjPattern($val, $pattern, $lang); - if (empty($propertyVal) && !is_numeric($propertyVal)) { + if (($propertyVal === [] || ($propertyVal === '' || $propertyVal === '0') || $propertyVal === null) && !is_numeric($propertyVal)) { $propertyVal = $val->id(); } @@ -371,7 +345,7 @@ public function displayVal($val, array $options = []) } } - if (empty($label) && !is_numeric($label)) { + if (($label === [] || ($label === '' || $label === '0') || $label === null) && !is_numeric($label)) { $label = $val; } @@ -390,9 +364,8 @@ public function displayVal($val, array $options = []) * Set the available choices. * * @param array $choices One or more choice structures. - * @return self */ - public function setChoices(array $choices) + public function setChoices(array $choices): static { unset($choices); @@ -407,9 +380,8 @@ public function setChoices(array $choices) * Merge the available choices. * * @param array $choices One or more choice structures. - * @return self */ - public function addChoices(array $choices) + public function addChoices(array $choices): static { unset($choices); @@ -425,9 +397,8 @@ public function addChoices(array $choices) * * @param string $choiceIdent The choice identifier (will be key / default ident). * @param string|array $choice A string representing the choice label or a structure. - * @return self */ - public function addChoice($choiceIdent, $choice) + public function addChoice($choiceIdent, $choice): static { unset($choiceIdent, $choice); @@ -451,9 +422,8 @@ public function dynamicTypeField() /** * @param string|null $field The field to use for dynamic object type. * @throws InvalidArgumentException If the field is not a string. - * @return self */ - public function setDynamicTypeField($field) + public function setDynamicTypeField($field): static { if (!is_string($field) && !is_null($field)) { throw new InvalidArgumentException( @@ -472,7 +442,7 @@ public function setDynamicTypeField($field) * @param array $pagination Pagination settings. * @return ObjectProperty Chainable */ - public function setPagination(array $pagination) + public function setPagination(array $pagination): static { $this->pagination = $pagination; @@ -495,7 +465,7 @@ public function pagination() * @param array $orders An array of orders. * @return ObjectProperty Chainable */ - public function setOrders(array $orders) + public function setOrders(array $orders): static { $this->orders = $orders; @@ -518,7 +488,7 @@ public function orders() * @param array $filters An array of filters. * @return ObjectProperty Chainable */ - public function setFilters(array $filters) + public function setFilters(array $filters): static { $this->filters = $filters; @@ -553,9 +523,8 @@ public function hasChoices() * Retrieve the available choice structures. * * @see SelectablePropertyInterface::choices() - * @return array */ - public function choices() + public function choices(): array { $proto = $this->proto(); if (!$proto->source()->tableExists()) { @@ -563,9 +532,8 @@ public function choices() } $objects = $this->collectionModelLoader()->load(); - $choices = $this->parseChoices($objects); - return $choices; + return $this->parseChoices($objects); } /** @@ -573,9 +541,8 @@ public function choices() * * @see SelectablePropertyInterface::hasChoice() * @param string $choiceIdent The choice identifier to lookup. - * @return boolean */ - public function hasChoice($choiceIdent) + public function hasChoice($choiceIdent): bool { $obj = $this->loadObject($choiceIdent); @@ -591,7 +558,7 @@ public function hasChoice($choiceIdent) * @param string $choiceIdent The choice identifier to lookup or object to format. * @return mixed The matching choice. */ - public function choice($choiceIdent) + public function choice($choiceIdent): ?array { $obj = $this->loadObject($choiceIdent); @@ -599,9 +566,7 @@ public function choice($choiceIdent) return null; } - $choice = $this->parseChoice($obj); - - return $choice; + return $this->parseChoice($obj); } /** @@ -645,6 +610,7 @@ public function choiceLabel($choice) * @param Container $container A dependencies container instance. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -658,14 +624,13 @@ protected function setDependencies(Container $container) * Retrieve the cache service. * * @throws RuntimeException If the cache service was not previously set. - * @return CacheItemPoolInterface */ - protected function cachePool() + protected function cachePool(): \Psr\Cache\CacheItemPoolInterface { - if (!isset($this->cachePool)) { + if (!$this->cachePool instanceof \Psr\Cache\CacheItemPoolInterface) { throw new RuntimeException(sprintf( 'Cache Pool is not defined for "%s"', - get_class($this) + static::class )); } @@ -679,7 +644,7 @@ protected function cachePool() * @throws InvalidArgumentException If the collection of objects is not iterable. * @return array Returns a collection of choice structures. */ - protected function parseChoices($objs) + protected function parseChoices($objs): array { if (!is_array($objs) && !$objs instanceof Traversable) { throw new InvalidArgumentException('Must be iterable'); @@ -688,10 +653,8 @@ protected function parseChoices($objs) $parsed = []; foreach ($objs as $choice) { $choice = $this->parseChoice($choice); - if ($choice !== null) { - $choiceIdent = $choice['value']; - $parsed[$choiceIdent] = $choice; - } + $choiceIdent = $choice['value']; + $parsed[$choiceIdent] = $choice; } return $parsed; @@ -703,7 +666,7 @@ protected function parseChoices($objs) * @param ModelInterface $obj An object to format. * @return array Returns a choice structure. */ - protected function parseChoice(ModelInterface $obj) + protected function parseChoice(ModelInterface $obj): array { $label = $this->renderObjPattern($obj); $choice = [ @@ -724,14 +687,13 @@ protected function parseChoice(ModelInterface $obj) * Retrieve the model collection loader. * * @throws RuntimeException If the collection loader was not previously set. - * @return CollectionLoader */ - protected function collectionLoader() + protected function collectionLoader(): \Charcoal\Loader\CollectionLoader { - if ($this->collectionLoader === null) { + if (!$this->collectionLoader instanceof \Charcoal\Loader\CollectionLoader) { throw new RuntimeException(sprintf( 'Collection Loader is not defined for "%s"', - get_class($this) + static::class )); } @@ -784,7 +746,7 @@ protected function collectionModelLoader() * @throws InvalidArgumentException If the pattern is not a string. * @return string */ - protected function renderObjPattern(ModelInterface $obj, $pattern = null, $lang = null) + protected function renderObjPattern(ModelInterface $obj, $pattern = null, $lang = null): string|array|null { if ($pattern === null) { $pattern = $this->getPattern(); @@ -809,13 +771,13 @@ protected function renderObjPattern(ModelInterface $obj, $pattern = null, $lang $origLang = $this->translator()->getLocale(); $this->translator()->setLocale($lang); - if (strpos($pattern, '{{') === false) { + if (!str_contains($pattern, '{{')) { $output = (string)$obj[$pattern]; } elseif (($obj instanceof ViewableInterface) && $obj->view()) { $output = $obj->renderTemplate($pattern); } else { - $callback = function ($matches) use ($obj) { - $prop = trim($matches[1]); + $callback = function ($matches) use ($obj): string { + $prop = trim((string) $matches[1]); return (string)$obj[$prop]; }; @@ -884,14 +846,13 @@ protected function modelLoader($objType = null) * Retrieve the object model factory. * * @throws RuntimeException If the model factory was not previously set. - * @return FactoryInterface */ - protected function modelFactory() + protected function modelFactory(): \Charcoal\Factory\FactoryInterface { - if (!isset($this->modelFactory)) { + if (!$this->modelFactory instanceof \Charcoal\Factory\FactoryInterface) { throw new RuntimeException(sprintf( 'Model Factory is not defined for "%s"', - get_class($this) + static::class )); } @@ -902,9 +863,8 @@ protected function modelFactory() * Set an object model factory. * * @param FactoryInterface $factory The model factory, to create objects. - * @return void */ - private function setModelFactory(FactoryInterface $factory) + private function setModelFactory(FactoryInterface $factory): void { $this->modelFactory = $factory; } @@ -913,9 +873,8 @@ private function setModelFactory(FactoryInterface $factory) * Set a model collection loader. * * @param CollectionLoader $loader The collection loader. - * @return void */ - private function setCollectionLoader(CollectionLoader $loader) + private function setCollectionLoader(CollectionLoader $loader): void { $this->collectionLoader = $loader; } @@ -924,9 +883,8 @@ private function setCollectionLoader(CollectionLoader $loader) * Set the cache service. * * @param CacheItemPoolInterface $cache A PSR-6 compliant cache pool instance. - * @return void */ - private function setCachePool(CacheItemPoolInterface $cache) + private function setCachePool(CacheItemPoolInterface $cache): void { $this->cachePool = $cache; } diff --git a/packages/property/src/Charcoal/Property/PasswordProperty.php b/packages/property/src/Charcoal/Property/PasswordProperty.php index 563d0d3e5..5f1ca26ac 100644 --- a/packages/property/src/Charcoal/Property/PasswordProperty.php +++ b/packages/property/src/Charcoal/Property/PasswordProperty.php @@ -12,10 +12,8 @@ */ class PasswordProperty extends StringProperty { - /** - * @return string - */ - public function type() + #[\Override] + public function type(): string { return 'password'; } @@ -29,6 +27,7 @@ public function type() * @param mixed $val The value, at time of saving. * @return string */ + #[\Override] public function save($val) { if ($val === null || $val === '') { @@ -36,7 +35,7 @@ public function save($val) } if (!$this->isHashed($val)) { - $val = password_hash($val, PASSWORD_DEFAULT); + $val = password_hash((string) $val, PASSWORD_DEFAULT); } return $val; @@ -44,17 +43,12 @@ public function save($val) /** * Retrieve the maximum number of characters allowed. - * - * @return integer */ - public function getMaxLength() + #[\Override] + public function getMaxLength(): int { - if (PASSWORD_DEFAULT === PASSWORD_BCRYPT) { - /** @link https://www.php.net/manual/en/function.password-hash.php */ - return 72; - } - - return parent::getMaxLength(); + /** @link https://www.php.net/manual/en/function.password-hash.php */ + return 72; } /** @@ -63,12 +57,11 @@ public function getMaxLength() * If the hash is corruped or the algorithm is not recognized, the value is assumed to be plain-text (not hashed). * * @param string $hash The value to test. - * @return boolean */ - public function isHashed($hash) + public function isHashed($hash): bool { $info = password_get_info($hash); - return strtolower($info['algoName']) !== 'unknown'; + return strtolower((string) $info['algoName']) !== 'unknown'; } /** diff --git a/packages/property/src/Charcoal/Property/PhoneProperty.php b/packages/property/src/Charcoal/Property/PhoneProperty.php index 03d5c047c..31fa647c1 100644 --- a/packages/property/src/Charcoal/Property/PhoneProperty.php +++ b/packages/property/src/Charcoal/Property/PhoneProperty.php @@ -12,10 +12,8 @@ */ class PhoneProperty extends StringProperty { - /** - * @return string - */ - public function type() + #[\Override] + public function type(): string { return 'phone'; } @@ -24,9 +22,9 @@ public function type() * Set StringProperty's `defaultMaxLength` to 16 for phone numbers. * * @see StringProperty::defaultMaxLength() - * @return integer */ - public function defaultMaxLength() + #[\Override] + public function defaultMaxLength(): int { return 16; } @@ -37,9 +35,9 @@ public function defaultMaxLength() * @param mixed $val Optional. The value to sanitize. If none provided, use `val()`. * @return string */ - public function sanitize($val) + public function sanitize($val): ?string { - return preg_replace('/[^0-9]/', '', $val); + return preg_replace('/[^0-9]/', '', (string) $val); } /** @@ -47,18 +45,18 @@ public function sanitize($val) * * @param mixed $val The value to to convert for display. * @param array $options Unused display options. - * @return string */ - public function displayVal($val, array $options = []) + #[\Override] + public function displayVal($val, array $options = []): string { unset($options); $val = $this->sanitize($val); - if (strlen($val) == 10) { - $areaCode = substr($val, 0, 3); - $part1 = substr($val, 3, 3); - $part2 = substr($val, 6, 4); + if (strlen((string) $val) === 10) { + $areaCode = substr((string) $val, 0, 3); + $part1 = substr((string) $val, 3, 3); + $part2 = substr((string) $val, 6, 4); return '(' . $areaCode . ') ' . $part1 . '-' . $part2; } else { return $val; diff --git a/packages/property/src/Charcoal/Property/PropertyFactory.php b/packages/property/src/Charcoal/Property/PropertyFactory.php index e780a52e0..2c701766c 100644 --- a/packages/property/src/Charcoal/Property/PropertyFactory.php +++ b/packages/property/src/Charcoal/Property/PropertyFactory.php @@ -1,5 +1,7 @@ setIdent($data['ident']); @@ -104,7 +92,7 @@ public function setData(array $data) * @throws InvalidArgumentException If the identifier is not a string. * @return PropertyField Chainable */ - public function setIdent($ident) + public function setIdent($ident): static { if (!is_string($ident)) { throw new InvalidArgumentException( @@ -115,10 +103,7 @@ public function setIdent($ident) return $this; } - /** - * @return string|null - */ - public function ident() + public function ident(): ?string { return $this->ident; } @@ -128,7 +113,7 @@ public function ident() * @throws InvalidArgumentException If the label is not a string. * @return PropertyField Chainable */ - public function setLabel($label) + public function setLabel($label): static { if (!is_string($label) && $label !== null) { throw new InvalidArgumentException( @@ -152,7 +137,7 @@ public function label() * @throws InvalidArgumentException If the SQL type is not a string. * @return PropertyField Chainable */ - public function setSqlType($sqlType) + public function setSqlType($sqlType): static { if (!is_string($sqlType) && $sqlType !== null) { throw new InvalidArgumentException( @@ -176,9 +161,9 @@ public function sqlType() * @throws InvalidArgumentException If the PDO type is not an integer. * @return PropertyField Chainable */ - public function setSqlPdoType($sqlPdoType) + public function setSqlPdoType($sqlPdoType): static { - if (!is_integer($sqlPdoType)) { + if (!is_int($sqlPdoType)) { throw new InvalidArgumentException( 'PDO Type must be an integer' ); @@ -190,7 +175,7 @@ public function setSqlPdoType($sqlPdoType) /** * @return integer */ - public function sqlPdoType() + public function sqlPdoType(): ?int { if ($this->val() === null) { return PDO::PARAM_NULL; @@ -204,7 +189,7 @@ public function sqlPdoType() * @throws InvalidArgumentException If the extra is not a string. * @return PropertyField Chainable */ - public function setExtra($extra) + public function setExtra($extra): static { if (!is_string($extra) && $extra !== null) { throw new InvalidArgumentException( @@ -215,10 +200,7 @@ public function setExtra($extra) return $this; } - /** - * @return string|null - */ - public function extra() + public function extra(): ?string { return $this->extra; } @@ -228,7 +210,7 @@ public function extra() * @throws InvalidArgumentException If the encoding is not a string. * @return PropertyField Chainable */ - public function setSqlEncoding($encoding) + public function setSqlEncoding($encoding): static { if (!is_string($encoding) && $encoding !== null) { throw new InvalidArgumentException( @@ -251,7 +233,7 @@ public function sqlEncoding() * @param mixed $val The field value. * @return PropertyField Chainable */ - public function setVal($val) + public function setVal($val): static { $this->val = $val; return $this; @@ -269,7 +251,7 @@ public function val() * @param mixed $defaultVal The default field value. * @return PropertyField Chainable */ - public function setDefaultVal($defaultVal) + public function setDefaultVal($defaultVal): static { $this->defaultVal = $defaultVal; return $this; @@ -287,26 +269,24 @@ public function defaultVal() * @param boolean $allowNull The field allow null flag. * @return PropertyField Chainable */ - public function setAllowNull($allowNull) + public function setAllowNull($allowNull): static { - $this->allowNull = !!$allowNull; + $this->allowNull = (bool) $allowNull; return $this; } /** * @return boolean */ - public function allowNull() + public function allowNull(): ?bool { return $this->allowNull; } /** * Generates the SQL table column. - * - * @return string|null */ - public function sql() + public function sql(): ?string { $ident = $this->ident(); if (!$ident) { @@ -339,7 +319,7 @@ public function sql() $default = $this->defaultVal(); if ($default) { - $parts[] = sprintf('DEFAULT \'%s\'', addslashes($default)); + $parts[] = sprintf('DEFAULT \'%s\'', addslashes((string) $default)); } $comment = $this->label(); @@ -356,7 +336,7 @@ public function sql() * @param string $value The string to snakeize. * @return string The snake_case string. */ - protected function snakeize($value) + protected function snakeize($value): string { $key = $value; @@ -364,7 +344,7 @@ protected function snakeize($value) return static::$snakeCache[$key]; } - $value = strtolower(preg_replace('/(?ident; } diff --git a/packages/property/src/Charcoal/Property/SelectablePropertyInterface.php b/packages/property/src/Charcoal/Property/SelectablePropertyInterface.php index 28dbc9071..5e14d3c9a 100644 --- a/packages/property/src/Charcoal/Property/SelectablePropertyInterface.php +++ b/packages/property/src/Charcoal/Property/SelectablePropertyInterface.php @@ -1,5 +1,7 @@ choices; + return (bool) $this->choices; } /** @@ -89,9 +87,8 @@ public function choices() * Determine if the given choice is available. * * @param string $choiceIdent The choice identifier to lookup. - * @return boolean */ - public function hasChoice($choiceIdent) + public function hasChoice($choiceIdent): bool { return isset($this->choices[$choiceIdent]); } @@ -159,7 +156,7 @@ public function choiceLabel($choice) * @param array $choices One or more values to format. * @return array Returns a collection of choice structures. */ - protected function parseChoices(array $choices) + protected function parseChoices(array $choices): array { $parsed = []; foreach ($choices as $choiceIdent => $choice) { diff --git a/packages/property/src/Charcoal/Property/SpriteProperty.php b/packages/property/src/Charcoal/Property/SpriteProperty.php index 86687c24a..3b4aabbc5 100644 --- a/packages/property/src/Charcoal/Property/SpriteProperty.php +++ b/packages/property/src/Charcoal/Property/SpriteProperty.php @@ -24,20 +24,15 @@ class SpriteProperty extends AbstractProperty implements SelectablePropertyInter /** * The sprite svg to build the choices from. - * - * @var string */ - private $sprite; + private ?string $sprite = null; /** * @var ViewInterface */ private $view; - /** - * @return string - */ - public function type() + public function type(): string { return 'sprite'; } @@ -47,9 +42,9 @@ public function type() * * @uses self::offsetSet() * @param array $data Key-value array of data to append. - * @return self */ - public function setData(array $data) + #[\Override] + public function setData(array $data): static { parent::setData($data); @@ -60,10 +55,8 @@ public function setData(array $data) /** * Retrievs the spritesheet SVG file path. - * - * @return string|null */ - public function getSprite() + public function getSprite(): ?string { return $this->sprite; } @@ -73,9 +66,8 @@ public function getSprite() * * @param string $sprite The SVG file path. * @throws InvalidArgumentException If the argument is not a string. - * @return self */ - public function setSprite($sprite) + public function setSprite($sprite): static { if (!is_string($sprite)) { throw new InvalidArgumentException( @@ -95,7 +87,7 @@ public function setSprite($sprite) * * @return string The SQL type */ - public function sqlType() + public function sqlType(): string { // Multiple strings are always stored as TEXT because they can hold multiple values if ($this['multiple']) { @@ -105,10 +97,7 @@ public function sqlType() return 'VARCHAR(255)'; } - /** - * @return integer - */ - public function sqlPdoType() + public function sqlPdoType(): int { return PDO::PARAM_STR; } @@ -117,9 +106,8 @@ public function sqlPdoType() * Retrieve the available choice structures. * * @see SelectablePropertyInterface::choices() - * @return array */ - public function buildChoicesFromSprite() + public function buildChoicesFromSprite(): array { $spritePath = $this['sprite']; @@ -134,8 +122,8 @@ public function buildChoicesFromSprite() $choices = []; - if (!isset($xml->symbol)) { - if (isset($xml->defs->symbol)) { + if (!property_exists($xml, 'symbol') || $xml->symbol === null) { + if (property_exists($xml->defs, 'symbol') && $xml->defs->symbol !== null) { $xml = $xml->defs; } else { throw new RuntimeException( @@ -150,7 +138,7 @@ public function buildChoicesFromSprite() $id = (string)$node->attributes()->id; - if (!$id) { + if ($id === '' || $id === '0') { $this->logger->warning(sprintf( 'Invalid SVG/XML spritesheet: Missing or empty ID attribute on: %s', $node->asXML() @@ -200,6 +188,7 @@ public function buildChoicesFromSprite() * @see AbstractPropery::displayVal() * @return string */ + #[\Override] public function displayVal($val, array $options = []) { $val = parent::displayVal($val, $options); @@ -244,6 +233,7 @@ public function spriteVal($val) * @param Container $container A Pimple DI container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); @@ -279,11 +269,11 @@ protected function parseChoice($choice, $choiceIdent) } if (!isset($choice['spritePathWithHash'])) { - $choice['spritePathWithHash'] = (string)$this['sprite'] . '#' . $choiceIdent; + $choice['spritePathWithHash'] = $this['sprite'] . '#' . $choiceIdent; } if (!isset($choice['spritePathWithHash'])) { - $choice['spritePathWithHash'] = (string)$this['sprite'] . '#' . $choiceIdent; + $choice['spritePathWithHash'] = $this['sprite'] . '#' . $choiceIdent; $choice['label'] = $this->translator()->translation($choiceIdent); } } else { diff --git a/packages/property/src/Charcoal/Property/StorablePropertyInterface.php b/packages/property/src/Charcoal/Property/StorablePropertyInterface.php index 547384916..e93d26af3 100644 --- a/packages/property/src/Charcoal/Property/StorablePropertyInterface.php +++ b/packages/property/src/Charcoal/Property/StorablePropertyInterface.php @@ -1,5 +1,7 @@ fields)) { - $this->fields = $this->generateFields($val); - } else { - $this->fields = $this->updatedFields($this->fields, $val); - } + $this->fields = empty($this->fields) ? $this->generateFields($val) : $this->updatedFields($this->fields, $val); return $this->fields; } @@ -126,7 +122,7 @@ public function fieldNames() * @param mixed $val The value to set as field value. * @return mixed */ - protected function fieldValue($key, $val) + protected function fieldValue($key, array $val) { if ($val === null) { return null; @@ -169,16 +165,12 @@ public function storageVal($val) if ($val === '') { return $val; } - } else { - if ($this['allowNull'] && $val === '') { - return null; - } + } elseif ($this['allowNull'] && $val === '') { + return null; } - if ($this['multiple']) { - if (is_array($val)) { - $val = implode($this->multipleSeparator(), $val); - } + if ($this['multiple'] && is_array($val)) { + $val = implode($this->multipleSeparator(), $val); } if (!is_scalar($val)) { @@ -228,7 +220,7 @@ public function parseFromFlatData(array $flatData) */ protected function updatedFields(array $fields, $val) { - if (empty($fields)) { + if ($fields === []) { $fields = $this->generateFields($val); } @@ -245,7 +237,7 @@ protected function updatedFields(array $fields, $val) * @param mixed $val The value to set as field value. * @return PropertyField[] */ - protected function generateFields($val = null) + protected function generateFields($val = null): array { $fields = []; @@ -270,9 +262,8 @@ protected function generateFields($val = null) /** * @param array $data Optional. Field data. - * @return PropertyField */ - protected function createPropertyField(array $data = null) + protected function createPropertyField(?array $data = null): \Charcoal\Property\PropertyField { $field = new PropertyField(); @@ -287,9 +278,8 @@ protected function createPropertyField(array $data = null) * Determine if the given value is a valid field key suffix. * * @param mixed $key The key to test. - * @return boolean */ - protected function isValidFieldKey($key) + protected function isValidFieldKey($key): bool { return (!empty($key) || is_numeric($key)); } @@ -300,7 +290,7 @@ protected function isValidFieldKey($key) * @param string $value The string to snakeize. * @return string The snake_case string. */ - protected function snakeize($value) + protected function snakeize($value): string { $key = $value; @@ -308,7 +298,7 @@ protected function snakeize($value) return static::$snakeCache[$key]; } - $value = strtolower(preg_replace('/(?parseValAsMultiple($propertyValue); - } + if ($this['multiple'] && !is_array($propertyValue)) { + $propertyValue = $this->parseValAsMultiple($propertyValue); } if (is_array($propertyValue)) { @@ -120,11 +104,10 @@ public function displayVal($val, array $options = []) * * @param integer $maxLength The max length allowed. * @throws InvalidArgumentException If the parameter is not an integer or < 0. - * @return self */ - public function setMaxLength($maxLength) + public function setMaxLength($maxLength): static { - if (!is_integer($maxLength)) { + if (!is_int($maxLength)) { throw new InvalidArgumentException( 'Max length must be an integer.' ); @@ -143,10 +126,8 @@ public function setMaxLength($maxLength) /** * Retrieve the maximum number of characters allowed. - * - * @return integer */ - public function getMaxLength() + public function getMaxLength(): int { if ($this->maxLength === null) { $this->maxLength = $this->defaultMaxLength(); @@ -157,10 +138,8 @@ public function getMaxLength() /** * Retrieve the default maximum number of characters allowed. - * - * @return integer */ - public function defaultMaxLength() + public function defaultMaxLength(): int { return self::DEFAULT_MAX_LENGTH; } @@ -170,11 +149,10 @@ public function defaultMaxLength() * * @param integer $minLength The minimum length allowed. * @throws InvalidArgumentException If the parameter is not an integer or < 0. - * @return self */ - public function setMinLength($minLength) + public function setMinLength($minLength): static { - if (!is_integer($minLength)) { + if (!is_int($minLength)) { throw new InvalidArgumentException( 'Min length must be an integer.' ); @@ -193,10 +171,8 @@ public function setMinLength($minLength) /** * Retrieve the minimum number of characters allowed. - * - * @return integer */ - public function getMinLength() + public function getMinLength(): int { if ($this->minLength === null) { $this->minLength = $this->defaultMinLength(); @@ -207,10 +183,8 @@ public function getMinLength() /** * Retrieve the default minimum number of characters allowed. - * - * @return integer */ - public function defaultMinLength() + public function defaultMinLength(): int { return 0; } @@ -220,9 +194,8 @@ public function defaultMinLength() * * @param string $regexp A regular expression. * @throws InvalidArgumentException If the parameter is not a string. - * @return self */ - public function setRegexp($regexp) + public function setRegexp($regexp): static { if (!is_string($regexp)) { throw new InvalidArgumentException( @@ -237,10 +210,8 @@ public function setRegexp($regexp) /** * Retrieve the regular expression to check the value against. - * - * @return string */ - public function getRegexp() + public function getRegexp(): string { if ($this->regexp === null) { $this->regexp = self::DEFAULT_REGEXP; @@ -253,21 +224,18 @@ public function getRegexp() * Set whether the value is allowed to be empty. * * @param boolean $allowEmpty The allow empty flag. - * @return self */ - public function setAllowEmpty($allowEmpty) + public function setAllowEmpty($allowEmpty): static { - $this->allowEmpty = !!$allowEmpty; + $this->allowEmpty = (bool) $allowEmpty; return $this; } /** * Determine if the value is allowed to be empty. - * - * @return boolean */ - public function getAllowEmpty() + public function getAllowEmpty(): bool { return $this->allowEmpty; } @@ -276,19 +244,15 @@ public function getAllowEmpty() * Set whether the value is allowed to contain HTML. * * @param boolean $allowHtml The allow HTML flag. - * @return self */ - public function setAllowHtml($allowHtml) + public function setAllowHtml($allowHtml): static { - $this->allowHtml = !!$allowHtml; + $this->allowHtml = (bool) $allowHtml; return $this; } - /** - * @return boolean - */ - public function getAllowHtml() + public function getAllowHtml(): bool { return $this->allowHtml; } @@ -301,9 +265,8 @@ public function getAllowHtml() * 2. If the property is a multiton, all values are counted together. * * @todo Support `multiple` / `l10n` - * @return integer */ - public function length() + public function length(): int { $val = $this->displayVal($this->val()); @@ -315,7 +278,8 @@ public function length() * * @return string[] */ - public function validationMethods() + #[\Override] + public function validationMethods(): array { $parentMethods = parent::validationMethods(); @@ -342,7 +306,7 @@ public function validateMaxLength() } $maxLength = $this->getMaxLength(); - if ($maxLength == 0) { + if ($maxLength === 0) { return true; } @@ -354,7 +318,7 @@ public function validateMaxLength() } else { $valid = true; foreach ($val as $v) { - $valid = (mb_strlen($v) <= $maxLength); + $valid = (mb_strlen((string) $v) <= $maxLength); if (!$valid) { $this->validator()->error('Maximum length error', 'maxLength'); return $valid; @@ -397,7 +361,7 @@ public function validateMinLength() } else { $valid = true; foreach ($val as $v) { - $valid = (mb_strlen($v) >= $minLength); + $valid = (mb_strlen((string) $v) >= $minLength); if (!$valid) { $this->validator()->error('Minimum length error', 'minLength'); return $valid; @@ -422,7 +386,7 @@ public function validateRegexp() return true; } - $valid = !!preg_match($regexp, $val); + $valid = (bool) preg_match($regexp, (string) $val); if (!$valid) { $this->validator()->error('Regexp error', 'regexp'); } @@ -432,10 +396,8 @@ public function validateRegexp() /** * Validate if the property's value is allowed to be empty. - * - * @return boolean */ - public function validateAllowEmpty() + public function validateAllowEmpty(): bool { $val = $this->val(); if (!$this['allowEmpty'] && empty($val) && !is_numeric($val)) { @@ -457,12 +419,11 @@ public function validateAllowEmpty() * @param mixed $val A single value to parse. * @return mixed The parsed value. */ + #[\Override] public function parseOne($val) { - if ($this['allowHtml'] === false) { - if (is_string($val)) { - return strip_tags($val); - } + if ($this['allowHtml'] === false && is_string($val)) { + return strip_tags($val); } return $val; @@ -476,7 +437,7 @@ public function parseOne($val) * @see StorablePropertyTrait::sqlType() * @return string The SQL type */ - public function sqlType() + public function sqlType(): string { // Multiple strings are always stored as TEXT because they can hold multiple values if ($this['multiple']) { @@ -494,9 +455,8 @@ public function sqlType() /** * @see StorablePropertyTrait::sqlPdoType() - * @return integer */ - public function sqlPdoType() + public function sqlPdoType(): int { return PDO::PARAM_STR; } diff --git a/packages/property/src/Charcoal/Property/Structure/StructureMetadata.php b/packages/property/src/Charcoal/Property/Structure/StructureMetadata.php index 7f1d36c78..fde9f0216 100644 --- a/packages/property/src/Charcoal/Property/Structure/StructureMetadata.php +++ b/packages/property/src/Charcoal/Property/Structure/StructureMetadata.php @@ -13,17 +13,13 @@ class StructureMetadata extends AbstractMetadata { /** * The metadata identifier. - * - * @var string|null */ - private $ident; + private ?string $ident = null; /** * Store the admin module config. - * - * @var array */ - private $admin = []; + private array $admin = []; /** * Set the metadata identifier. @@ -32,7 +28,7 @@ class StructureMetadata extends AbstractMetadata * @throws InvalidArgumentException If identifier is not a string. * @return StructureMetadata Chainable */ - public function setIdent($ident) + public function setIdent($ident): static { if ($ident === null) { $this->ident = null; @@ -43,8 +39,8 @@ public function setIdent($ident) throw new InvalidArgumentException( sprintf( '[%s] Identifier must be a string; received %s', - get_called_class(), - (is_object($ident) ? get_class($ident) : gettype($ident)) + static::class, + (get_debug_type($ident)) ) ); } @@ -56,10 +52,8 @@ public function setIdent($ident) /** * Retrieve the metadata identifier. - * - * @return string|null */ - public function ident() + public function ident(): ?string { return $this->ident; } @@ -68,9 +62,9 @@ public function ident() * Set the object's default values. * * @param array $data An associative array. - * @return StructureMetadata */ - public function setDefaultData(array $data) + #[\Override] + public function setDefaultData(array $data): static { foreach ($data as $key => $val) { $key = $this->camelize($key); @@ -84,9 +78,9 @@ public function setDefaultData(array $data) * Set the properties. * * @param array $properties One or more properties. - * @return StructureMetadata */ - public function setProperties(array $properties) + #[\Override] + public function setProperties(array $properties): static { foreach ($properties as $propertyIdent => $propertyMetadata) { $propertyIdent = $this->camelize($propertyIdent); @@ -108,9 +102,8 @@ public function setProperties(array $properties) * * @param string $propertyIdent The property identifier to lookup. * @throws InvalidArgumentException If the identifier argument is not a string. - * @return boolean */ - public function hasProperty($propertyIdent) + public function hasProperty($propertyIdent): bool { if (!is_string($propertyIdent)) { throw new InvalidArgumentException( @@ -124,10 +117,8 @@ public function hasProperty($propertyIdent) /** * Retrieve the admin module's metadata. - * - * @return array */ - public function admin() + public function admin(): array { return $this->admin; } @@ -136,9 +127,8 @@ public function admin() * Set the admin module's metadata. * * @param array $data Metadata. - * @return StructureMetadata */ - public function setAdmin(array $data) + public function setAdmin(array $data): static { $this->admin = array_replace_recursive($this->admin, $data); diff --git a/packages/property/src/Charcoal/Property/Structure/StructureModel.php b/packages/property/src/Charcoal/Property/Structure/StructureModel.php index b71b80b21..d5b14817a 100644 --- a/packages/property/src/Charcoal/Property/Structure/StructureModel.php +++ b/packages/property/src/Charcoal/Property/Structure/StructureModel.php @@ -1,5 +1,7 @@ translator()->getLocale(); - } + $lang = is_array($lang) && isset($lang['lang']) ? $lang['lang'] : $this->translator()->getLocale(); } if ($val instanceof TranslatableValue) { @@ -156,6 +151,7 @@ public function toJson($options = 0) * @throws InvalidArgumentException If the value is invalid. * @return array */ + #[\Override] public function parseOne($val) { if ($val === null || $val === '') { @@ -182,6 +178,7 @@ public function parseOne($val) * @param mixed $val A L10N variable. * @return TranslatableInterface|null The translation value. */ + #[\Override] public function parseValAsL10n($val): ?TranslatableInterface { return new TranslatableValue($val); @@ -192,9 +189,8 @@ public function parseValAsL10n($val): ?TranslatableInterface * * @param string $sqlType The field SQL column type. * @throws InvalidArgumentException If the SQL type is invalid. - * @return self */ - public function setSqlType($sqlType) + public function setSqlType($sqlType): static { if (!is_string($sqlType)) { throw new InvalidArgumentException( @@ -202,31 +198,15 @@ public function setSqlType($sqlType) ); } - switch (strtoupper($sqlType)) { - case 'TEXT': - $sqlType = 'TEXT'; - break; - - case 'TINY': - case 'TINYTEXT': - $sqlType = 'TINYTEXT'; - break; - - case 'MEDIUM': - case 'MEDIUMTEXT': - $sqlType = 'MEDIUMTEXT'; - break; - - case 'LONG': - case 'LONGTEXT': - $sqlType = 'LONGTEXT'; - break; - - default: - throw new InvalidArgumentException( - 'SQL Type must be one of TEXT, TINYTEXT, MEDIUMTEXT, LONGTEXT.' - ); - } + $sqlType = match (strtoupper($sqlType)) { + 'TEXT' => 'TEXT', + 'TINY', 'TINYTEXT' => 'TINYTEXT', + 'MEDIUM', 'MEDIUMTEXT' => 'MEDIUMTEXT', + 'LONG', 'LONGTEXT' => 'LONGTEXT', + default => throw new InvalidArgumentException( + 'SQL Type must be one of TEXT, TINYTEXT, MEDIUMTEXT, LONGTEXT.' + ), + }; $this->sqlType = $sqlType; @@ -239,7 +219,6 @@ public function setSqlType($sqlType) * For a lack of better array support in mysql, data is stored as encoded JSON in a TEXT. * * @see StorableProperyTrait::sqlType() - * @return string */ public function sqlType(): string { @@ -250,9 +229,8 @@ public function sqlType(): string * Retrieve the property's PDO data type. * * @see StorablePropertyTrait::sqlPdoType() - * @return integer */ - public function sqlPdoType() + public function sqlPdoType(): int { return PDO::PARAM_STR; } diff --git a/packages/property/src/Charcoal/Property/TextProperty.php b/packages/property/src/Charcoal/Property/TextProperty.php index e6af8b062..f78860673 100644 --- a/packages/property/src/Charcoal/Property/TextProperty.php +++ b/packages/property/src/Charcoal/Property/TextProperty.php @@ -1,5 +1,7 @@ long = !!$long; + $this->long = (bool) $long; return $this; } @@ -48,9 +47,9 @@ public function getLong() * (0 = no max length). * * @see StringProperty::defaultMaxLength() - * @return integer */ - public function defaultMaxLength() + #[\Override] + public function defaultMaxLength(): int { return 0; } @@ -61,7 +60,8 @@ public function defaultMaxLength() * @see StorablePropertyTrait::sqlType() * @return string The SQL type */ - public function sqlType() + #[\Override] + public function sqlType(): string { if ($this['long'] === true) { return 'LONGTEXT'; diff --git a/packages/property/src/Charcoal/Property/UrlProperty.php b/packages/property/src/Charcoal/Property/UrlProperty.php index c752af9c4..7c601e095 100644 --- a/packages/property/src/Charcoal/Property/UrlProperty.php +++ b/packages/property/src/Charcoal/Property/UrlProperty.php @@ -1,5 +1,7 @@ 'mp4', + 'video/webm' => 'webm', + 'video/ogg', 'video/ogv' => 'ogv', + 'video/x-matroska' => 'mkv', + default => null, + }; } } diff --git a/packages/property/src/Charcoal/Property/data/colors.php b/packages/property/src/Charcoal/Property/data/colors.php index 7a492f67a..28e61a384 100644 --- a/packages/property/src/Charcoal/Property/data/colors.php +++ b/packages/property/src/Charcoal/Property/data/colors.php @@ -1,5 +1,7 @@ */ - private $fileMapOfFixtures; + private ?array $fileMapOfFixtures = null; - /** - * @return void - */ protected function setUp(): void { $this->obj = $this->createProperty(); @@ -71,9 +77,8 @@ public function getFileMapOfFixtures() * * @param array $expected The expected results. * @param array $actual The actual results. - * @return void */ - public function assertValidatorHasResults($expected, $actual) + public function assertValidatorHasResults(array|\ArrayAccess $expected, $actual): void { foreach ($actual as $level => $results) { $this->assertArrayHasKey( @@ -102,21 +107,17 @@ public function assertValidatorHasResults($expected, $actual) /** * Asserts that the property implements {@see FileProperty}. - * - * @coversNothing - * @return void */ - public function testFilePropertyInterface() + #[\PHPUnit\Framework\Attributes\CoversNothing] + public function testFilePropertyInterface(): void { $this->assertInstanceOf(FileProperty::class, $this->obj); } /** * Asserts that the property adheres to file property defaults. - * - * @return void */ - public function testPropertyDefaults() + public function testPropertyDefaults(): void { $obj = $this->obj; @@ -132,13 +133,12 @@ public function testPropertyDefaults() * Asserts that the file property will generate * the expected extension from a given dataset. * - * @dataProvider provideDataForGenerateExtension * * @param string $mime A MIME type. * @param string $ext The expected file extension. - * @return void */ - public function testGenerateExtensionFromDataProvider($mime, $ext) + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataForGenerateExtension')] + public function testGenerateExtensionFromDataProvider($mime, $ext): void { $this->obj['mimetype'] = $mime; $this->assertEquals($mime, $this->obj['mimetype']); @@ -148,10 +148,8 @@ public function testGenerateExtensionFromDataProvider($mime, $ext) /** * Asserts that the file property will generate an extension * for all default accepted MIME types. - * - * @return void */ - public function testGenerateExtensionFromDefaultAcceptedMimeTypes() + public function testGenerateExtensionFromDefaultAcceptedMimeTypes(): void { $mimes = $this->obj['defaultAcceptedMimetypes']; if (empty($mimes)) { @@ -174,10 +172,8 @@ public function testGenerateExtensionFromDefaultAcceptedMimeTypes() /** * Asserts that the uploadPath always ends with a trailing "/". - * - * @return void */ - public function testUploadPath() + public function testUploadPath(): void { $obj = $this->obj; @@ -194,12 +190,8 @@ public function testUploadPath() /** * Asserts that the property can store a filesize. - * - * @covers \Charcoal\Property\FileProperty::setFilesize() - * @covers \Charcoal\Property\FileProperty::getFilesize() - * @return void */ - public function testFilesize() + public function testFilesize(): void { $return = $this->obj->setFilesize(1024); $this->assertSame($this->obj, $return); @@ -214,11 +206,8 @@ public function testFilesize() /** * Asserts that the property returns NULL if it can not resolve the filesize from its value. - * - * @covers \Charcoal\Property\FileProperty::getFilesize() - * @return void */ - public function testFilesizeFromBadVal() + public function testFilesizeFromBadVal(): void { $obj = $this->obj; @@ -230,12 +219,8 @@ public function testFilesizeFromBadVal() /** * Asserts that the property can store a MIME type. - * - * @covers \Charcoal\Property\FileProperty::setMimetype() - * @covers \Charcoal\Property\FileProperty::getMimetype() - * @return void */ - public function testMimetype() + public function testMimetype(): void { $return = $this->obj->setMimetype('foo'); $this->assertSame($this->obj, $return); @@ -253,11 +238,8 @@ public function testMimetype() /** * Asserts that the property returns NULL if it can not resolve the MIME type from its value. - * - * @covers \Charcoal\Property\FileProperty::getMimetype() - * @return void */ - public function testMimetypeFromBadVal() + public function testMimetypeFromBadVal(): void { $obj = $this->obj; @@ -269,11 +251,8 @@ public function testMimetypeFromBadVal() /** * Asserts that the property returns x-empty the file is empty. - * - * @covers \Charcoal\Property\FileProperty::getMimetype() - * @return void */ - public function testMimetypeFromEmptyFile() + public function testMimetypeFromEmptyFile(): void { $obj = $this->obj; @@ -285,12 +264,8 @@ public function testMimetypeFromEmptyFile() /** * Asserts that the property supports accepted MIME types. - * - * @covers \Charcoal\Property\FileProperty::setAcceptedMimetypes() - * @covers \Charcoal\Property\FileProperty::getAcceptedMimetypes() - * @return void */ - public function testAcceptedMimeTypes() + public function testAcceptedMimeTypes(): void { $obj = $this->obj; @@ -322,7 +297,6 @@ public function testAcceptedMimeTypes() /** * Asserts that the property adheres to file property defaults. * - * @covers \Charcoal\Property\FileProperty::getDefaultAcceptedMimetypes() * @return void */ abstract public function testDefaulAcceptedMimeTypes(); @@ -331,7 +305,6 @@ abstract public function testDefaulAcceptedMimeTypes(); * Asserts that the property properly checks if * any acceptable MIME types are available. * - * @covers \Charcoal\Property\FileProperty::hasAcceptedMimetypes() * @return void */ abstract public function testHasAcceptedMimeTypes(); @@ -353,7 +326,6 @@ abstract public function testMimetypeFromVal(); /** * Asserts that the `type()` method is "file". * - * @covers \Charcoal\Property\FileProperty::type() * @return void */ abstract public function testPropertyType(); @@ -371,5 +343,5 @@ abstract public function createProperty(); * @used-by self::testGenerateExtension() * @return array Format: `[ "mime-type", "extension" ]` */ - abstract public function provideDataForGenerateExtension(); + abstract public static function provideDataForGenerateExtension(); } diff --git a/packages/property/tests/Charcoal/Property/AbstractPropertyTest.php b/packages/property/tests/Charcoal/Property/AbstractPropertyTest.php index 8a840cd0d..3ceb09ce0 100644 --- a/packages/property/tests/Charcoal/Property/AbstractPropertyTest.php +++ b/packages/property/tests/Charcoal/Property/AbstractPropertyTest.php @@ -24,9 +24,6 @@ class AbstractPropertyTest extends AbstractTestCase */ public $obj; - /** - * @return void - */ protected function setUp(): void { $container = $this->getContainer(); @@ -38,7 +35,7 @@ protected function setUp(): void ]]); } - public function testDefaults() + public function testDefaults(): void { $this->assertEquals('', $this->obj['ident']); $this->assertFalse($this->obj['multiple']); @@ -50,10 +47,7 @@ public function testDefaults() $this->assertTrue($this->obj['allowNull']); } - /** - * @return void - */ - public function testIdent() + public function testIdent(): void { $this->assertEquals('', $this->obj->ident()); @@ -71,10 +65,7 @@ public function testIdent() $this->obj->setIdent([]); } - /** - * @return void - */ - public function testL10nIdent() + public function testL10nIdent(): void { $this->obj->setIdent(''); $this->expectException(RuntimeException::class); @@ -83,13 +74,13 @@ public function testL10nIdent() $this->obj->setL10n(true); $this->assertEquals('foobar_en', $this->obj->l10nIdent()); $this->assertEquals('foobar_fr', $this->obj->l10nIdent('fr')); - $this->assertEquals('foobar_en', $this->obj->l10nIdent(null)); + $this->assertEquals('foobar_en', $this->obj->l10nIdent()); $this->expectException(InvalidArgumentException::class); $this->obj->l10nIdent(false); } - public function testL1onIdentThrowsExceptionIfL10nIsFalse() + public function testL1onIdentThrowsExceptionIfL10nIsFalse(): void { $this->expectException(LogicException::class); $this->obj->setL10n(false); @@ -97,7 +88,7 @@ public function testL1onIdentThrowsExceptionIfL10nIsFalse() $this->obj->l10nIdent(); } - public function testSetLabel() + public function testSetLabel(): void { $this->assertEquals('', $this->obj['label']); @@ -110,10 +101,8 @@ public function testSetLabel() * Asserts that the basic displayVal method: * - returns an empty string if the value is null * - returns the string as is (when not l10n / multiple) - * - * @return void */ - public function testDisplayVal() + public function testDisplayVal(): void { $this->assertFalse($this->obj['multiple']); $this->assertFalse($this->obj['l10n']); @@ -122,10 +111,7 @@ public function testDisplayVal() $this->assertEquals('foo', $this->obj->displayVal('foo')); } - /** - * @return void - */ - public function testDisplayValL10n() + public function testDisplayValL10n(): void { $this->obj['l10n'] = true; @@ -136,10 +122,7 @@ public function testDisplayValL10n() //$this->assertEquals('foo', $this->obj->displayVal(['fr'=>'foo'])); } - /** - * @return void - */ - public function testSetInputVal() + public function testSetInputVal(): void { $this->assertEquals('', $this->obj->inputVal(null)); @@ -149,10 +132,7 @@ public function testSetInputVal() $this->assertEquals('{"foo":"bar"}', str_replace([ "\n", "\r", "\t", ' ' ], '', $ret)); } - /** - * @return void - */ - public function testSetInputValL10n() + public function testSetInputValL10n(): void { $this->obj->setL10n(true); @@ -160,10 +140,7 @@ public function testSetInputValL10n() $this->assertEquals('foo', $this->obj->inputVal('foo')); } - /** - * @return void - */ - public function testSetInputValMultiple() + public function testSetInputValMultiple(): void { $this->obj->setMultiple(true); @@ -171,10 +148,7 @@ public function testSetInputValMultiple() $this->assertEquals('foo', $this->obj->inputVal('foo')); } - /** - * @return void - */ - public function testSetInputValL10nMultiple() + public function testSetInputValL10nMultiple(): void { $this->obj->setL10n(true); $this->obj->setMultiple(true); @@ -183,10 +157,7 @@ public function testSetInputValL10nMultiple() $this->assertEquals('foo', $this->obj->inputVal('foo')); } - /** - * @return void - */ - public function testSetL10n() + public function testSetL10n(): void { $this->assertFalse($this->obj['l10n']); @@ -204,10 +175,7 @@ public function testSetL10n() $this->assertFalse($this->obj['l10n']); } - /** - * @return void - */ - public function testSetHidden() + public function testSetHidden(): void { $this->assertFalse($this->obj['hidden']); @@ -225,10 +193,7 @@ public function testSetHidden() $this->assertFalse($this->obj['hidden']); } - /** - * @return void - */ - public function testSetMultiple() + public function testSetMultiple(): void { $this->assertFalse($this->obj['multiple']); @@ -246,10 +211,7 @@ public function testSetMultiple() $this->assertFalse($this->obj['multiple']); } - /** - * @return void - */ - public function testMultipleSeparator() + public function testMultipleSeparator(): void { $this->assertEquals(',', $this->obj->multipleSeparator()); @@ -259,10 +221,7 @@ public function testMultipleSeparator() $this->assertEquals('/', $this->obj->multipleSeparator()); } - /** - * @return void - */ - public function testSetRequired() + public function testSetRequired(): void { $this->assertFalse($this->obj['required']); @@ -280,10 +239,7 @@ public function testSetRequired() $this->assertFalse($this->obj['required']); } - /** - * @return void - */ - public function testSetUnique() + public function testSetUnique(): void { $this->assertFalse($this->obj['unique']); @@ -301,10 +257,7 @@ public function testSetUnique() $this->assertFalse($this->obj['unique']); } - /** - * @return void - */ - public function testSetAllowNull() + public function testSetAllowNull(): void { $this->assertTrue($this->obj['allowNull']); @@ -322,10 +275,7 @@ public function testSetAllowNull() $this->assertFalse($this->obj['allowNull']); } - /** - * @return void - */ - public function testSetStorable() + public function testSetStorable(): void { $this->assertTrue($this->obj['storable']); @@ -343,18 +293,12 @@ public function testSetStorable() $this->assertFalse($this->obj['storable']); } - /** - * @return void - */ - public function testValidationMethods() + public function testValidationMethods(): void { $this->assertIsArray($this->obj->validationMethods()); } - /** - * @return void - */ - public function testSetSqlEncoding() + public function testSetSqlEncoding(): void { $this->assertEquals('', $this->obj->sqlEncoding()); $ret = $this->obj->setSqlEncoding('utf8mb4'); diff --git a/packages/property/tests/Charcoal/Property/AudioPropertyTest.php b/packages/property/tests/Charcoal/Property/AudioPropertyTest.php index 9516c518f..1a507ac1b 100644 --- a/packages/property/tests/Charcoal/Property/AudioPropertyTest.php +++ b/packages/property/tests/Charcoal/Property/AudioPropertyTest.php @@ -5,17 +5,15 @@ // From 'charcoal-property' use Charcoal\Property\AudioProperty; -/** - * - */ +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Property\AudioProperty::class, 'type()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Property\AudioProperty::class, 'getDefaultAcceptedMimetypes()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Property\AudioProperty::class, 'hasAcceptedMimetypes()')] class AudioPropertyTest extends AbstractFilePropertyTestCase { /** * Create a file property instance. - * - * @return AudioProperty */ - public function createProperty() + public function createProperty(): \Charcoal\Property\AudioProperty { $container = $this->getContainer(); @@ -29,21 +27,17 @@ public function createProperty() /** * Asserts that the `type()` method is "file". - * - * @covers \Charcoal\Property\AudioProperty::type() - * @return void */ - public function testPropertyType() + public function testPropertyType(): void { $this->assertEquals('audio', $this->obj->type()); } /** * Asserts that the property adheres to file property defaults. - * - * @return void */ - public function testPropertyDefaults() + #[\Override] + public function testPropertyDefaults(): void { parent::testPropertyDefaults(); @@ -53,11 +47,8 @@ public function testPropertyDefaults() /** * Asserts that the property adheres to file property defaults. - * - * @covers \Charcoal\Property\AudioProperty::getDefaultAcceptedMimetypes() - * @return void */ - public function testDefaulAcceptedMimeTypes() + public function testDefaulAcceptedMimeTypes(): void { $this->assertIsArray($this->obj['defaultAcceptedMimetypes']); $this->assertNotEmpty($this->obj['defaultAcceptedMimetypes']); @@ -66,11 +57,8 @@ public function testDefaulAcceptedMimeTypes() /** * Asserts that the property properly checks if * any acceptable MIME types are available. - * - * @covers \Charcoal\Property\AudioProperty::hasAcceptedMimetypes() - * @return void */ - public function testHasAcceptedMimeTypes() + public function testHasAcceptedMimeTypes(): void { $this->assertTrue($this->obj->hasAcceptedMimetypes()); @@ -80,10 +68,8 @@ public function testHasAcceptedMimeTypes() /** * Asserts that the property can resolve a filesize from its value. - * - * @return void */ - public function testFilesizeFromVal() + public function testFilesizeFromVal(): void { $obj = $this->obj; @@ -97,10 +83,8 @@ public function testFilesizeFromVal() * Asserts that the property can resolve a MIME type from its value. * * Ignore issues under PHP 7.0 and PHP 7.1, see https://bugs.php.net/bug.php?id=78183 - * - * @return void */ - public function testMimetypeFromVal() + public function testMimetypeFromVal(): void { $obj = $this->obj; @@ -120,10 +104,7 @@ public function testMimetypeFromVal() } } - /** - * @return void - */ - public function testSetData() + public function testSetData(): void { $obj = $this->obj; $data = [ @@ -137,10 +118,7 @@ public function testSetData() $this->assertEquals(500, $obj['maxLength']); } - /** - * @return void - */ - public function testSetDataSnakecase() + public function testSetDataSnakecase(): void { $obj = $this->obj; $data = [ @@ -154,10 +132,7 @@ public function testSetDataSnakecase() $this->assertEquals(500, $obj['maxLength']); } - /** - * @return void - */ - public function testSetMinLength() + public function testSetMinLength(): void { $ret = $this->obj->setMinLength(5); $this->assertSame($ret, $this->obj); @@ -168,10 +143,7 @@ public function testSetMinLength() $this->obj->setMinLength(false); } - /** - * @return void - */ - public function testSetMaxLength() + public function testSetMaxLength(): void { $ret = $this->obj->setMaxLength(5); $this->assertSame($ret, $this->obj); @@ -182,10 +154,8 @@ public function testSetMaxLength() $this->obj->setMaxLength(false); } - /** - * @return void - */ - public function testAcceptedMimetypes() + #[\Override] + public function testAcceptedMimetypes(): void { $ret = $this->obj['acceptedMimetypes']; $this->assertContains('audio/mp3', $ret); @@ -198,9 +168,8 @@ public function testAcceptedMimetypes() * Provide property data for {@see AudioProperty::generateExtension()}. * * @used-by AbstractFilePropertyTestCase::testGenerateExtensionFromDataProvider() - * @return array */ - public function provideDataForGenerateExtension() + public static function provideDataForGenerateExtension(): array { return [ [ 'audio/mp3', 'mp3' ], diff --git a/packages/property/tests/Charcoal/Property/BooleanPropertyTest.php b/packages/property/tests/Charcoal/Property/BooleanPropertyTest.php index bbe46f710..acd1139df 100644 --- a/packages/property/tests/Charcoal/Property/BooleanPropertyTest.php +++ b/packages/property/tests/Charcoal/Property/BooleanPropertyTest.php @@ -21,9 +21,6 @@ class BooleanPropertyTest extends AbstractTestCase */ public $obj; - /** - * @return void - */ protected function setUp(): void { $container = $this->getContainer(); @@ -35,10 +32,7 @@ protected function setUp(): void ]); } - /** - * @return void - */ - public function testType() + public function testType(): void { $this->assertEquals('boolean', $this->obj->type()); } @@ -46,10 +40,8 @@ public function testType() /** * Assert that the boolean property 's `displayVal()` method: * - return the proper label - * - * @return void */ - public function testDisplayVal() + public function testDisplayVal(): void { $this->obj->setTrueLabel('Oui'); $this->obj->setFalseLabel('Non'); @@ -72,10 +64,8 @@ public function testDisplayVal() * - set the multiple to false, if false or falsish value * - throws exception otherwise (truthish or invalid value) * - is chainable - * - * @return void */ - public function testSetMultiple() + public function testSetMultiple(): void { $obj = $this->obj; $ret = $obj->setMultiple(0); @@ -88,19 +78,14 @@ public function testSetMultiple() /** * Asserts that the boolean property is multiple by default - * - * @return void */ - public function testMultiple() + public function testMultiple(): void { $obj = $this->obj; $this->assertFalse($obj['multiple']); } - /** - * @return void - */ - public function testSetData() + public function testSetData(): void { $obj = $this->obj; $data = [ @@ -115,10 +100,7 @@ public function testSetData() $this->assertEquals('bar', $obj['falseLabel']); } - /** - * @return void - */ - public function testSetTrueLabel() + public function testSetTrueLabel(): void { $obj = $this->obj; $ret = $obj->setTrueLabel('foo'); @@ -127,10 +109,7 @@ public function testSetTrueLabel() $this->assertEquals('foo', $obj['trueLabel']); } - /** - * @return void - */ - public function testSetFalseLabel() + public function testSetFalseLabel(): void { $obj = $this->obj; $ret = $obj->setFalseLabel('foo'); @@ -139,36 +118,24 @@ public function testSetFalseLabel() $this->assertEquals('foo', $obj['falseLabel']); } - /** - * @return void - */ - public function testSqlExtra() + public function testSqlExtra(): void { $obj = $this->obj; $this->assertSame(null, $obj->sqlExtra()); } - /** - * @return void - */ - public function testSqlType() + public function testSqlType(): void { //$this->assertEquals('TINYINT(1) UNSIGNED', $this->obj->sqlType()); $this->assertEquals('INT', $this->obj->sqlType()); } - /** - * @return void - */ - public function testSqlPdoType() + public function testSqlPdoType(): void { $this->assertEquals(PDO::PARAM_BOOL, $this->obj->sqlPdoType()); } - /** - * @return void - */ - public function testChoices() + public function testChoices(): void { $obj = $this->obj; $obj->setVal(false); @@ -187,10 +154,7 @@ public function testChoices() $this->assertEquals($choices, $obj->choices()); } - /** - * @return void - */ - public function testSave() + public function testSave(): void { $this->assertTrue($this->obj->save(true)); $this->assertFalse($this->obj->save(false)); diff --git a/packages/property/tests/Charcoal/Property/ColorPropertyTest.php b/packages/property/tests/Charcoal/Property/ColorPropertyTest.php index 1a11f7daf..066300ac7 100644 --- a/packages/property/tests/Charcoal/Property/ColorPropertyTest.php +++ b/packages/property/tests/Charcoal/Property/ColorPropertyTest.php @@ -23,9 +23,6 @@ class ColorPropertyTest extends AbstractTestCase */ public $obj; - /** - * @return void - */ protected function setUp(): void { $container = $this->getContainer(); @@ -37,15 +34,12 @@ protected function setUp(): void ]); } - /** - * @return void - */ - public function testType() + public function testType(): void { $this->assertEquals('color', $this->obj->type()); } - public function testParseOneNull() + public function testParseOneNull(): void { $this->obj->setAllowNull(true); $this->assertNull($this->obj->parseOne(null)); @@ -55,7 +49,7 @@ public function testParseOneNull() $this->obj->parseOne(null); } - public function testParseOneEmpty() + public function testParseOneEmpty(): void { $this->obj->setAllowNull(true); $this->assertNull($this->obj->parseOne('')); @@ -65,13 +59,13 @@ public function testParseOneEmpty() $this->obj->parseOne(''); } - public function parseOneFalse() + public function parseOneFalse(): void { $this->expectException(InvalidArgumentException::class); $this->obj->parseOne(false); } - public function parseOneArray() + public function parseOneArray(): void { $this->assertEquals(['r'=>255, 'g'=>255, 'b'=>255], $this->obj->parseOne([255,255,255])); $this->expectException(InvalidArgumentException::class); @@ -80,18 +74,13 @@ public function parseOneArray() /** * Hello world - * - * @return void */ - public function testDefaults() + public function testDefaults(): void { $this->assertEquals(false, $this->obj['supportAlpha']); } - /** - * @return void - */ - public function testSetSupportAlpha() + public function testSetSupportAlpha(): void { $ret = $this->obj->setSupportAlpha(true); $this->assertSame($ret, $this->obj); @@ -108,35 +97,30 @@ public function testSetSupportAlpha() } /** - * @dataProvider colorProviderNoAlpha * * @param string $color A color to test. * @param string $expected The expected mutation of $color. - * @return void */ - public function testColorValueNoAlpha($color, $expected) + #[\PHPUnit\Framework\Attributes\DataProvider('colorProviderNoAlpha')] + public function testColorValueNoAlpha(string|array $color, string $expected): void { $this->obj->setSupportAlpha(false); $this->assertEquals($expected, $this->obj->colorVal($color)); } /** - * @dataProvider colorProviderAlpha * * @param string $color A color to test. * @param string $expected The expected mutation of $color. - * @return void */ - public function testColorValueAlpha($color, $expected) + #[\PHPUnit\Framework\Attributes\DataProvider('colorProviderAlpha')] + public function testColorValueAlpha(string|array $color, string $expected): void { $this->obj->setSupportAlpha(true); $this->assertEquals($expected, $this->obj->colorVal($color)); } - /** - * @return void - */ - public function testColorValInvalidThrowsException() + public function testColorValInvalidThrowsException(): void { $this->expectException('\InvalidArgumentException'); $this->obj->colorVal('invalid'); @@ -144,10 +128,8 @@ public function testColorValInvalidThrowsException() /** * Provider for hexadcimalValue, in `[$color, $expected]` pairs. - * - * @return array */ - public function colorProviderNoAlpha() + public static function colorProviderNoAlpha(): array { return [ ['#FF00FF', '#FF00FF'], @@ -170,10 +152,8 @@ public function colorProviderNoAlpha() /** * Provider for hexadcimalValue, in `[$color, $result]` pairs. - * - * @return array */ - public function colorProviderAlpha() + public static function colorProviderAlpha(): array { return [ ['#FF00FF', 'rgba(255,0,255,0)'], @@ -193,29 +173,20 @@ public function colorProviderAlpha() ]; } - /** - * @return void - */ - public function testSqlExtra() + public function testSqlExtra(): void { $obj = $this->obj; $this->assertEquals('', $obj->sqlExtra()); } - /** - * @return void - */ - public function testSqlTypeMultiple() + public function testSqlTypeMultiple(): void { $obj = $this->obj; $obj->setMultiple(true); $this->assertEquals('TEXT', $obj->sqlType()); } - /** - * @return void - */ - public function testSqlType() + public function testSqlType(): void { $obj = $this->obj; @@ -226,7 +197,7 @@ public function testSqlType() $this->assertEquals('CHAR(7)', $obj->sqlType()); } - public function testSqlPdoType() + public function testSqlPdoType(): void { $this->assertEquals(PDO::PARAM_STR, $this->obj->sqlPdoType()); } diff --git a/packages/property/tests/Charcoal/Property/ContainerIntegrationTrait.php b/packages/property/tests/Charcoal/Property/ContainerIntegrationTrait.php index 5af9bbfba..153244bc5 100644 --- a/packages/property/tests/Charcoal/Property/ContainerIntegrationTrait.php +++ b/packages/property/tests/Charcoal/Property/ContainerIntegrationTrait.php @@ -51,9 +51,8 @@ protected function getContainerProvider() /** * @see ContainerProvider - * @return void */ - private function setupContainer() + private function setupContainer(): void { $provider = new ContainerProvider(); $container = new Container(); diff --git a/packages/property/tests/Charcoal/Property/ContainerProvider.php b/packages/property/tests/Charcoal/Property/ContainerProvider.php index 59512fc74..600df92c0 100644 --- a/packages/property/tests/Charcoal/Property/ContainerProvider.php +++ b/packages/property/tests/Charcoal/Property/ContainerProvider.php @@ -46,9 +46,8 @@ class ContainerProvider * Register the unit tests required services. * * @param Container $container A DI container. - * @return void */ - public function registerBaseServices(Container $container) + public function registerBaseServices(Container $container): void { $this->registerConfig($container); $this->registerSource($container); @@ -60,9 +59,8 @@ public function registerBaseServices(Container $container) * Setup the application configset. * * @param Container $container A DI container. - * @return void */ - public function registerConfig(Container $container) + public function registerConfig(Container $container): void { $container['config'] = [ 'base_path' => realpath(__DIR__.'/../../..'), @@ -76,11 +74,10 @@ public function registerConfig(Container $container) * Note: Uses SQLite to create a database in memory. * * @param Container $container A DI container. - * @return void */ - public function registerSource(Container $container) + public function registerSource(Container $container): void { - $container['database'] = function () { + $container['database'] = function (): \PDO { $pdo = new PDO('sqlite::memory:'); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); return $pdo; @@ -91,71 +88,57 @@ public function registerSource(Container $container) * Setup the application's logging interface. * * @param Container $container A DI container. - * @return void */ - public function registerLogger(Container $container) + public function registerLogger(Container $container): void { - $container['logger'] = function () { - return new NullLogger(); - }; + $container['logger'] = (fn(): \Psr\Log\NullLogger => new NullLogger()); } /** * Setup the application's caching interface. * * @param Container $container A DI container. - * @return void */ - public function registerCache(Container $container) + public function registerCache(Container $container): void { - $container['cache'] = function () { - return new Pool(); - }; + $container['cache'] = (fn(): \Stash\Pool => new Pool()); } /** * Setup the framework's view renderer. * * @param Container $container A DI container. - * @return void */ - public function registerView(Container $container) + public function registerView(Container $container): void { - $container['view/loader'] = function (Container $container) { - return new MustacheLoader([ - 'logger' => $container['logger'], - 'base_path' => $container['config']['base_path'], - 'paths' => [ - 'views' - ] - ]); - }; - - $container['view/engine'] = function (Container $container) { - return new MustacheEngine([ - 'logger' => $container['logger'], - 'cache' => MustacheEngine::DEFAULT_CACHE_PATH, - 'loader' => $container['view/loader'] - ]); - }; - - $container['view'] = function (Container $container) { - return new GenericView([ - 'logger' => $container['logger'], - 'engine' => $container['view/engine'] - ]); - }; + $container['view/loader'] = (fn(Container $container): \Charcoal\View\Mustache\MustacheLoader => new MustacheLoader([ + 'logger' => $container['logger'], + 'base_path' => $container['config']['base_path'], + 'paths' => [ + 'views' + ] + ])); + + $container['view/engine'] = (fn(Container $container): \Charcoal\View\Mustache\MustacheEngine => new MustacheEngine([ + 'logger' => $container['logger'], + 'cache' => MustacheEngine::DEFAULT_CACHE_PATH, + 'loader' => $container['view/loader'] + ])); + + $container['view'] = (fn(Container $container): \Charcoal\View\GenericView => new GenericView([ + 'logger' => $container['logger'], + 'engine' => $container['view/engine'] + ])); } /** * Setup the application's translator service. * * @param Container $container A DI container. - * @return void */ - public function registerTranslator(Container $container) + public function registerTranslator(Container $container): void { - $container['locales/manager'] = function () { + $container['locales/manager'] = function (): \Charcoal\Translator\LocalesManager { $manager = new LocalesManager([ 'locales' => [ 'en' => [ 'locale' => 'en-US' ] @@ -167,22 +150,19 @@ public function registerTranslator(Container $container) return $manager; }; - $container['translator'] = function (Container $container) { - return new Translator([ - 'manager' => $container['locales/manager'] - ]); - }; + $container['translator'] = (fn(Container $container): \Charcoal\Translator\Translator => new Translator([ + 'manager' => $container['locales/manager'] + ])); } /** * Setup the application's translator service. * * @param Container $container A DI container. - * @return void */ - public function registerMultilingualTranslator(Container $container) + public function registerMultilingualTranslator(Container $container): void { - $container['locales/manager'] = function () { + $container['locales/manager'] = function (): \Charcoal\Translator\LocalesManager { $manager = new LocalesManager([ 'locales' => [ 'en' => [ @@ -217,7 +197,7 @@ public function registerMultilingualTranslator(Container $container) return $manager; }; - $container['translator'] = function (Container $container) { + $container['translator'] = function (Container $container): \Charcoal\Translator\Translator { $translator = new Translator([ 'manager' => $container['locales/manager'] ]); @@ -237,103 +217,88 @@ public function registerMultilingualTranslator(Container $container) * Setup the framework's metadata loader interface. * * @param Container $container A DI container. - * @return void */ - public function registerMetadataLoader(Container $container) + public function registerMetadataLoader(Container $container): void { - $container['metadata/loader'] = function (Container $container) { - return new MetadataLoader([ - 'cache' => $container['cache'], - 'logger' => $container['logger'], - 'base_path' => $container['config']['base_path'], - 'paths' => [ - 'metadata' - ] - ]); - }; + $container['metadata/loader'] = (fn(Container $container): \Charcoal\Model\Service\MetadataLoader => new MetadataLoader([ + 'cache' => $container['cache'], + 'logger' => $container['logger'], + 'base_path' => $container['config']['base_path'], + 'paths' => [ + 'metadata' + ] + ])); } /** * Setup the framework's data source factory. * * @param Container $container A DI container. - * @return void */ - public function registerSourceFactory(Container $container) + public function registerSourceFactory(Container $container): void { - $container['source/factory'] = function ($container) { - return new Factory([ - 'map' => [ - 'database' => DatabaseSource::class - ], - 'arguments' => [[ - 'logger' => $container['logger'], - 'cache' => $container['cache'], - 'pdo' => $container['database'] - ]] - ]); - }; + $container['source/factory'] = (fn($container): \Charcoal\Factory\GenericFactory => new Factory([ + 'map' => [ + 'database' => DatabaseSource::class + ], + 'arguments' => [[ + 'logger' => $container['logger'], + 'cache' => $container['cache'], + 'pdo' => $container['database'] + ]] + ])); } /** * Setup the framework's model factory. * * @param Container $container A DI container. - * @return void */ - public function registerModelFactory(Container $container) + public function registerModelFactory(Container $container): void { - $container['model/factory'] = function ($container) { - return new Factory([ - 'arguments' => [[ - 'container' => $container, - 'logger' => $container['logger'], - 'metadata_loader' => $container['metadata/loader'], - 'source_factory' => $container['source/factory'], - 'property_factory' => $container['property/factory'] - ]] - ]); - }; + $container['model/factory'] = (fn($container): \Charcoal\Factory\GenericFactory => new Factory([ + 'arguments' => [[ + 'container' => $container, + 'logger' => $container['logger'], + 'metadata_loader' => $container['metadata/loader'], + 'source_factory' => $container['source/factory'], + 'property_factory' => $container['property/factory'] + ]] + ])); } /** * Setup the framework's property factory. * * @param Container $container A DI container. - * @return void */ - public function registerPropertyFactory(Container $container) + public function registerPropertyFactory(Container $container): void { - $container['property/factory'] = function (Container $container) { - return new Factory([ - 'resolver_options' => [ - 'prefix' => '\\Charcoal\\Property\\', - 'suffix' => 'Property' - ], - 'arguments' => [[ - 'container' => $container, - 'database' => $container['database'], - 'logger' => $container['logger'], - 'translator' => $container['translator'] - ]] - ]); - }; + $container['property/factory'] = (fn(Container $container): \Charcoal\Factory\GenericFactory => new Factory([ + 'resolver_options' => [ + 'prefix' => '\\Charcoal\\Property\\', + 'suffix' => 'Property' + ], + 'arguments' => [[ + 'container' => $container, + 'database' => $container['database'], + 'logger' => $container['logger'], + 'translator' => $container['translator'] + ]] + ])); } /** * Setup the framework's collection loader interface. * * @param Container $container A DI container. - * @return void */ - public function registerModelCollectionLoader(Container $container) + public function registerModelCollectionLoader(Container $container): void { - $container['model/collection/loader'] = function (Container $container) { - return new CollectionLoader([ - 'logger' => $container['logger'], - 'cache' => $container['cache'], - 'factory' => $container['model/factory'] - ]); - }; + $container['model/collection/loader'] = (fn(Container $container): \Charcoal\Loader\CollectionLoader => new CollectionLoader([ + 'logger' => $container['logger'], + 'cache' => $container['cache'], + 'factory' => $container['model/factory'] + ])); } } diff --git a/packages/property/tests/Charcoal/Property/DateTimePropertyTest.php b/packages/property/tests/Charcoal/Property/DateTimePropertyTest.php index eb0b1c001..5ad45f277 100644 --- a/packages/property/tests/Charcoal/Property/DateTimePropertyTest.php +++ b/packages/property/tests/Charcoal/Property/DateTimePropertyTest.php @@ -22,9 +22,6 @@ class DateTimePropertyTest extends AbstractTestCase */ public $obj; - /** - * @return void - */ protected function setUp(): void { $container = $this->getContainer(); @@ -39,10 +36,8 @@ protected function setUp(): void /** * Assert that the `type` method: * - returns "date-time" - * - * @return void */ - public function testType() + public function testType(): void { $this->assertEquals('date-time', $this->obj->type()); } @@ -51,10 +46,8 @@ public function testType() * Assert that the `setData` method: * - is chainable * - sets the data - * - * @return void */ - public function testSetData() + public function testSetData(): void { $ret = $this->obj->setData([ 'min' => '2015-01-01 00:00:00', @@ -77,10 +70,8 @@ public function testSetData() * - Is chainable * - Set the value to null if "allowNull" is true * - Throw an exception if "allowNull" is false - * - * @return void */ - public function testSetValWithNullValue() + public function testSetValWithNullValue(): void { $this->obj->setAllowNull(true); @@ -98,10 +89,8 @@ public function testSetValWithNullValue() * - Is chainable * - Sets the value when the parameter is a string or a DateTime object * - Throws an exception otherwise - * - * @return void */ - public function testSetVal() + public function testSetVal(): void { $ret = $this->obj->setVal('2000-01-01 00:00:00'); $this->assertSame($ret, $this->obj); @@ -115,10 +104,7 @@ public function testSetVal() $this->assertEquals($dt, $this->obj->val()); } - /** - * @return void - */ - public function testStorageVal() + public function testStorageVal(): void { $this->assertEquals('1984-10-01 00:00:00', $this->obj->storageVal('October 1st, 1984')); @@ -131,10 +117,7 @@ public function testStorageVal() $this->obj->storageVal(null); } - /** - * @return void - */ - public function testDisplayVal() + public function testDisplayVal(): void { // Test default format $this->assertEquals('2015-10-01 15:00:00', $this->obj->displayVal('October 1st, 2015 15:00:00')); @@ -150,7 +133,7 @@ public function testDisplayVal() $this->assertEquals('', $this->obj->displayVal(null)); } - public function testInputVal() + public function testInputVal(): void { // Test default format $this->assertEquals('2015-10-01 15:00:00', $this->obj->inputVal('October 1st, 2015 15:00:00')); @@ -167,10 +150,8 @@ public function testInputVal() * - set the multiple to false, if false or falsish value * - throws exception otherwise (truthish or invalid value) * - is chainable - * - * @return void */ - public function testSetMultiple() + public function testSetMultiple(): void { $ret = $this->obj->setMultiple(0); $this->assertSame($ret, $this->obj); @@ -180,10 +161,7 @@ public function testSetMultiple() $this->obj->setMultiple(1); } - /** - * @return void - */ - public function testMultiple() + public function testMultiple(): void { $this->assertSame(false, $this->obj['multiple']); } @@ -194,10 +172,8 @@ public function testMultiple() * - sets the min value from a string or DateTime object * - accepts null as parameter * - throws exception when the argument is invalid - * - * @return void */ - public function testSetMin() + public function testSetMin(): void { // Setting by string $ret = $this->obj->setMin('2020-01-01 01:02:03'); @@ -225,7 +201,7 @@ public function testSetMin() $this->obj->setMin('foo'); } - public function testSetMinInvalidObjectThrowsException() + public function testSetMinInvalidObjectThrowsException(): void { $this->expectException(InvalidArgumentException::class); $this->obj->setMin(new \StdClass()); @@ -237,10 +213,8 @@ public function testSetMinInvalidObjectThrowsException() * - sets the max value * - accepts null as a parameter * - throws exception when the argument is invalid - * - * @return void */ - public function testSetMax() + public function testSetMax(): void { $ret = $this->obj->setMax('2020-01-01 01:02:03'); $this->assertSame($ret, $this->obj); @@ -267,7 +241,7 @@ public function testSetMax() $this->obj->setMax('foo'); } - public function testSetMaxInvalidObjectThrowsException() + public function testSetMaxInvalidObjectThrowsException(): void { $this->expectException(InvalidArgumentException::class); $this->obj->setMax(new \StdClass()); @@ -282,10 +256,8 @@ public function testSetMaxInvalidObjectThrowsException() * - accepts empty string * - accepts null as a parameter (convert to empty string) * - throws an exception if not a string - * - * @return void */ - public function testSetFormat() + public function testSetFormat(): void { $this->assertEquals('Y-m-d H:i:s', $this->obj['format']); @@ -309,10 +281,7 @@ public function testSetFormat() $this->obj->setFormat(false); } - /** - * @return void - */ - public function testSave() + public function testSave(): void { $this->assertEquals(null, $this->obj->save(null)); @@ -320,7 +289,7 @@ public function testSave() $this->assertEquals($expected, $this->obj->save('2015-01-01')); } - public function testValidationMethods() + public function testValidationMethods(): void { $this->assertContains('min', $this->obj->validationMethods()); $this->assertContains('max', $this->obj->validationMethods()); @@ -331,10 +300,8 @@ public function testValidationMethods() * - Returns true if no "min" is set * - Returns true when the value is equal or bigger * - Returns false when the value is smaller - * - * @return void */ - public function testValidateMin() + public function testValidateMin(): void { $this->assertTrue($this->obj->validateMin()); @@ -358,10 +325,8 @@ public function testValidateMin() * - Returns true if no "max" is set * - Returns true when the value is equal or smaller * - Returns false when the value is bigger - * - * @return void */ - public function testValidateMax() + public function testValidateMax(): void { $this->assertTrue($this->obj->validateMax()); @@ -380,26 +345,17 @@ public function testValidateMax() $this->assertNotTrue($this->obj->validateMax()); } - /** - * @return void - */ - public function testSqlExtra() + public function testSqlExtra(): void { $this->assertSame(null, $this->obj->sqlExtra()); } - /** - * @return void - */ - public function testSqlType() + public function testSqlType(): void { $this->assertEquals('DATETIME', $this->obj->sqlType()); } - /** - * @return void - */ - public function testSqlPdoType() + public function testSqlPdoType(): void { $this->assertEquals(PDO::PARAM_STR, $this->obj->sqlPdoType()); } diff --git a/packages/property/tests/Charcoal/Property/EmailPropertyTest.php b/packages/property/tests/Charcoal/Property/EmailPropertyTest.php index 593631ee0..2860cbeac 100644 --- a/packages/property/tests/Charcoal/Property/EmailPropertyTest.php +++ b/packages/property/tests/Charcoal/Property/EmailPropertyTest.php @@ -18,9 +18,6 @@ class EmailPropertyTest extends AbstractTestCase */ public $obj; - /** - * @return void - */ protected function setUp(): void { $container = $this->getContainer(); @@ -34,18 +31,13 @@ protected function setUp(): void /** * Asserts that the `type()` method returns "url". - * - * @return void */ - public function testType() + public function testType(): void { $this->assertEquals('email', $this->obj->type()); } - /** - * @return void - */ - public function testMaxLength() + public function testMaxLength(): void { $this->assertEquals(254, $this->obj['maxLength']); @@ -53,10 +45,7 @@ public function testMaxLength() $this->assertEquals(254, $this->obj['maxLength']); } - /** - * @return void - */ - public function testValidateEmail() + public function testValidateEmail(): void { $this->obj['allowNull'] = false; $this->obj['required'] = true; @@ -76,30 +65,27 @@ public function testValidateEmail() $this->assertFalse($this->obj->validateEmail()); } - /** - * @return void - */ - public function testValidationMethods() + public function testValidationMethods(): void { $this->assertContains('email', $this->obj->validationMethods()); } - public function testParseVal() + public function testParseVal(): void { $this->assertEquals('charcoal@example.com', $this->obj->parseVal('charcoal@example.com')); } - public function testDisplayVal() + public function testDisplayVal(): void { $this->assertEquals('charcoal@example.com', $this->obj->displayVal('charcoal@example.com')); } - public function testInputVal() + public function testInputVal(): void { $this->assertEquals('charcoal@example.com', $this->obj->inputVal('charcoal@example.com')); } - public function testStorageVal() + public function testStorageVal(): void { $this->assertEquals('charcoal@example.com', $this->obj->storageVal('charcoal@example.com')); } diff --git a/packages/property/tests/Charcoal/Property/FilePropertyTest.php b/packages/property/tests/Charcoal/Property/FilePropertyTest.php index 2978d68ca..5e925c123 100644 --- a/packages/property/tests/Charcoal/Property/FilePropertyTest.php +++ b/packages/property/tests/Charcoal/Property/FilePropertyTest.php @@ -12,17 +12,15 @@ // From 'charcoal-property' use Charcoal\Property\FileProperty; -/** - * - */ +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Property\FileProperty::class, 'type()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Property\FileProperty::class, 'getDefaultAcceptedMimetypes()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Property\FileProperty::class, 'hasAcceptedMimetypes()')] class FilePropertyTest extends AbstractFilePropertyTestCase { /** * Create a file property instance. - * - * @return FileProperty */ - public function createProperty() + public function createProperty(): \Charcoal\Property\FileProperty { $container = $this->getContainer(); @@ -36,22 +34,16 @@ public function createProperty() /** * Asserts that the `type()` method is "file". - * - * @covers \Charcoal\Property\FileProperty::type() - * @return void */ - public function testPropertyType() + public function testPropertyType(): void { $this->assertEquals('file', $this->obj->type()); } /** * Asserts that the property adheres to file property defaults. - * - * @covers \Charcoal\Property\FileProperty::getDefaultAcceptedMimetypes() - * @return void */ - public function testDefaulAcceptedMimeTypes() + public function testDefaulAcceptedMimeTypes(): void { $this->assertIsArray($this->obj['defaultAcceptedMimetypes']); $this->assertEmpty($this->obj['defaultAcceptedMimetypes']); @@ -60,11 +52,8 @@ public function testDefaulAcceptedMimeTypes() /** * Asserts that the property properly checks if * any acceptable MIME types are available. - * - * @covers \Charcoal\Property\FileProperty::hasAcceptedMimetypes() - * @return void */ - public function testHasAcceptedMimeTypes() + public function testHasAcceptedMimeTypes(): void { $obj = $this->obj; @@ -84,10 +73,8 @@ public function testHasAcceptedMimeTypes() /** * Asserts that the property can resolve a filesize from its value. - * - * @return void */ - public function testFilesizeFromVal() + public function testFilesizeFromVal(): void { $obj = $this->obj; @@ -99,10 +86,8 @@ public function testFilesizeFromVal() /** * Asserts that the property can resolve a MIME type from its value. - * - * @return void */ - public function testMimetypeFromVal() + public function testMimetypeFromVal(): void { $obj = $this->obj; @@ -112,10 +97,7 @@ public function testMimetypeFromVal() $this->assertEquals('text/plain', $obj['mimetype']); } - /** - * @return void - */ - public function testSetData() + public function testSetData(): void { $obj = $this->obj; $ret = $obj->setData([ @@ -134,10 +116,7 @@ public function testSetData() $this->assertEquals((32 * 1024 * 1024), $this->obj['maxFilesize']); } - /** - * @return void - */ - public function testSetOverwrite() + public function testSetOverwrite(): void { $ret = $this->obj->setOverwrite(true); $this->assertSame($ret, $this->obj); @@ -150,10 +129,7 @@ public function testSetOverwrite() $this->assertTrue($this->obj['overwrite']); } - /** - * @return void - */ - public function testVaidationMethods() + public function testVaidationMethods(): void { $methods = $this->obj->validationMethods(); $this->assertContains('mimetypes', $methods); @@ -163,7 +139,6 @@ public function testVaidationMethods() /** * Test validation file MIME types on property. * - * @dataProvider provideDataForValidateMimetypes * * @param mixed $val The value(s) to be validated. * @param boolean $l10n Whether the property value is multilingual. @@ -171,16 +146,16 @@ public function testVaidationMethods() * @param mixed $acceptedMimetypes The accepted MIME types. * @param boolean $expectedReturn The expected return value of the method. * @param array $expectedResults The expected validation results. - * @return void */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataForValidateMimetypes')] public function testValidateMimetypes( - $val, - $l10n, - $multiple, - $acceptedMimetypes, - $expectedReturn, + string|array|null $val, + bool $l10n, + bool $multiple, + ?array $acceptedMimetypes, + bool $expectedReturn, array $expectedResults = [] - ) { + ): void { $obj = $this->obj; $obj['uploadPath'] = $this->getPathToFixtures().'/files'; @@ -200,7 +175,6 @@ public function testValidateMimetypes( /** * Test validation file sizes on property. * - * @dataProvider provideDataForValidateFilesizes * * @param mixed $val The value(s) to be validated. * @param boolean $l10n Whether the property value is multilingual. @@ -208,16 +182,16 @@ public function testValidateMimetypes( * @param integer $maxFilesize The maximum file size accepted. * @param boolean $expectedReturn The expected return value of the method. * @param array $expectedResults The expected validation results. - * @return void */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataForValidateFilesizes')] public function testValidateFilesizes( - $val, - $l10n, - $multiple, - $maxFilesize, - $expectedReturn, + string|array|null $val, + bool $l10n, + bool $multiple, + int $maxFilesize, + bool $expectedReturn, array $expectedResults = [] - ) { + ): void { $obj = $this->obj; $obj['uploadPath'] = $this->getPathToFixtures().'/files'; @@ -234,10 +208,7 @@ public function testValidateFilesizes( ); } - /** - * @return void - */ - public function testFileExists() + public function testFileExists(): void { $obj = $this->obj; $this->assertTrue($obj->fileExists(__FILE__)); @@ -249,22 +220,18 @@ public function testFileExists() } /** - * @dataProvider providePathsForIsAbsolutePath * * @param string $path A path to test. * @param string $expected Whether the path is absolute (TRUE) or relative (FALSE). - * @return void */ - public function testIsAbsolutePath($path, $expected) + #[\PHPUnit\Framework\Attributes\DataProvider('providePathsForIsAbsolutePath')] + public function testIsAbsolutePath(?string $path, bool $expected): void { $result = $this->callMethodWith($this->obj, 'isAbsolutePath', $path); $this->assertEquals($expected, $result); } - /** - * @return array - */ - public function providePathsForIsAbsolutePath() + public static function providePathsForIsAbsolutePath(): array { return [ [ '/var/lib', true ], @@ -278,22 +245,18 @@ public function providePathsForIsAbsolutePath() } /** - * @dataProvider filenameProvider * * @param string $filename A dirty filename. * @param string $sanitized A clean version of $filename. - * @return void */ - public function testSanitizeFilename($filename, $sanitized) + #[\PHPUnit\Framework\Attributes\DataProvider('filenameProvider')] + public function testSanitizeFilename(string $filename, string $sanitized): void { $obj = $this->obj; $this->assertEquals($sanitized, $obj->sanitizeFilename($filename)); } - /** - * @return array - */ - public function filenameProvider() + public static function filenameProvider(): array { return [ [ 'foobar', 'foobar' ], @@ -303,10 +266,7 @@ public function filenameProvider() ]; } - /** - * @return void - */ - public function testGenerateFilename() + public function testGenerateFilename(): void { $obj = $this->obj; $obj->setIdent('foo'); @@ -319,7 +279,7 @@ public function testGenerateFilename() $this->assertStringContainsString('foobar', $ret); } - public function testGenerateUniqueFilename() + public function testGenerateUniqueFilename(): void { $ret = $this->obj->generateUniqueFilename('foo.png'); $this->assertStringContainsString('foo', $ret); @@ -327,7 +287,7 @@ public function testGenerateUniqueFilename() $this->assertNotEquals($ret, 'foo'); } - public function testFilesystem() + public function testFilesystem(): void { $this->assertEquals('public', $this->obj['filesystem']); @@ -336,18 +296,12 @@ public function testFilesystem() $this->assertEquals('foo', $this->obj['filesystem']); } - /** - * @return void - */ - public function testSqlExtra() + public function testSqlExtra(): void { $this->assertEquals('', $this->obj->sqlExtra()); } - /** - * @return void - */ - public function testSqlType() + public function testSqlType(): void { $this->obj->setMultiple(false); $this->assertEquals('VARCHAR(255)', $this->obj->sqlType()); @@ -356,10 +310,7 @@ public function testSqlType() $this->assertEquals('TEXT', $this->obj->sqlType()); } - /** - * @return void - */ - public function testSqlPdoType() + public function testSqlPdoType(): void { $this->assertEquals(PDO::PARAM_STR, $this->obj->sqlPdoType()); } @@ -368,147 +319,146 @@ public function testSqlPdoType() * Provide property data for {@see FileProperty::validateMimetypes()}. * * @used-by self::testValidateMimetypes() - * @return array */ - public function provideDataForValidateMimetypes() + public function provideDataForValidateMimetypes(): array { $paths = $this->getFileMapOfFixtures(); return [ 'any MIME types, no value' => [ - 'propertyValues' => null, - 'propertyL10n' => false, - 'propertyMultiple' => false, + 'val' => null, + 'l10n' => false, + 'multiple' => false, 'acceptedMimetypes' => null, - 'assertValidationReturn' => true, - 'assertValidationResults' => [], + 'expectedReturn' => true, + 'expectedResults' => [], ], 'any MIME types, text file' => [ - 'propertyValues' => $paths['document.txt'], - 'propertyL10n' => false, - 'propertyMultiple' => false, + 'val' => $paths['document.txt'], + 'l10n' => false, + 'multiple' => false, 'acceptedMimetypes' => null, - 'assertValidationReturn' => true, - 'assertValidationResults' => [], + 'expectedReturn' => true, + 'expectedResults' => [], ], 'any MIME types, image file' => [ - 'propertyValues' => $paths['panda.png'], - 'propertyL10n' => false, - 'propertyMultiple' => false, + 'val' => $paths['panda.png'], + 'l10n' => false, + 'multiple' => false, 'acceptedMimetypes' => null, - 'assertValidationReturn' => true, - 'assertValidationResults' => [], + 'expectedReturn' => true, + 'expectedResults' => [], ], 'text/plain, no value' => [ - 'propertyValues' => null, - 'propertyL10n' => false, - 'propertyMultiple' => false, + 'val' => null, + 'l10n' => false, + 'multiple' => false, 'acceptedMimetypes' => [ 'text/plain' ], - 'assertValidationReturn' => true, - 'assertValidationResults' => [], + 'expectedReturn' => true, + 'expectedResults' => [], ], 'text/plain, single text file' => [ - 'propertyValues' => $paths['document.txt'], - 'propertyL10n' => false, - 'propertyMultiple' => false, + 'val' => $paths['document.txt'], + 'l10n' => false, + 'multiple' => false, 'acceptedMimetypes' => [ 'text/plain' ], - 'assertValidationReturn' => true, - 'assertValidationResults' => [], + 'expectedReturn' => true, + 'expectedResults' => [], ], 'text/plain, single image file' => [ - 'propertyValues' => $paths['panda.png'], - 'propertyL10n' => false, - 'propertyMultiple' => false, + 'val' => $paths['panda.png'], + 'l10n' => false, + 'multiple' => false, 'acceptedMimetypes' => [ 'text/plain' ], - 'assertValidationReturn' => false, - 'assertValidationResults' => [ + 'expectedReturn' => false, + 'expectedResults' => [ Validator::ERROR => [ 'File ['.$paths['panda.png'].'] has unacceptable MIME type [image/png]', ], ], ], 'text/plain, nonexistent file' => [ - 'propertyValues' => $paths['nonexistent.txt'], - 'propertyL10n' => false, - 'propertyMultiple' => false, + 'val' => $paths['nonexistent.txt'], + 'l10n' => false, + 'multiple' => false, 'acceptedMimetypes' => [ 'text/plain' ], - 'assertValidationReturn' => false, - 'assertValidationResults' => [ + 'expectedReturn' => false, + 'expectedResults' => [ Validator::ERROR => [ 'File ['.$paths['nonexistent.txt'].'] not found or MIME type unrecognizable', ], ], ], 'text/plain, l10n, text file' => [ - 'propertyValues' => $paths['document.txt'], - 'propertyL10n' => true, - 'propertyMultiple' => false, + 'val' => $paths['document.txt'], + 'l10n' => true, + 'multiple' => false, 'acceptedMimetypes' => [ 'text/plain' ], - 'assertValidationReturn' => true, - 'assertValidationResults' => [], + 'expectedReturn' => true, + 'expectedResults' => [], ], 'text/plain, l10n, text + image file' => [ - 'propertyValues' => [ + 'val' => [ 'en' => $paths['document.txt'], 'fr' => $paths['panda.png'], ], - 'propertyL10n' => true, - 'propertyMultiple' => false, + 'l10n' => true, + 'multiple' => false, 'acceptedMimetypes' => [ 'text/plain' ], - 'assertValidationReturn' => false, - 'assertValidationResults' => [ + 'expectedReturn' => false, + 'expectedResults' => [ Validator::ERROR => [ 'File ['.$paths['panda.png'].'] has unacceptable MIME type [image/png]', ], ], ], 'text/plain, multiple, text files' => [ - 'propertyValues' => [ + 'val' => [ $paths['document.txt'], $paths['todo.txt'], ], - 'propertyL10n' => false, - 'propertyMultiple' => true, + 'l10n' => false, + 'multiple' => true, 'acceptedMimetypes' => [ 'text/plain' ], - 'assertValidationReturn' => true, - 'assertValidationResults' => [], + 'expectedReturn' => true, + 'expectedResults' => [], ], 'text/plain, multiple, text + image file' => [ - 'propertyValues' => [ + 'val' => [ $paths['document.txt'], $paths['panda.png'], ], - 'propertyL10n' => false, - 'propertyMultiple' => true, + 'l10n' => false, + 'multiple' => true, 'acceptedMimetypes' => [ 'text/plain' ], - 'assertValidationReturn' => false, - 'assertValidationResults' => [ + 'expectedReturn' => false, + 'expectedResults' => [ Validator::ERROR => [ 'File ['.$paths['panda.png'].'] has unacceptable MIME type [image/png]', ], ], ], 'text/plain, l10n + multiple #1' => [ - 'propertyValues' => [ + 'val' => [ 'en' => $paths['document.txt'].','.$paths['todo.txt'], 'fr' => [ $paths['stuff.txt'], $paths['draft.txt'] ], ], - 'propertyL10n' => false, - 'propertyMultiple' => false, + 'l10n' => false, + 'multiple' => false, 'acceptedMimetypes' => [ 'text/plain' ], - 'assertValidationReturn' => true, - 'assertValidationResults' => [], + 'expectedReturn' => true, + 'expectedResults' => [], ], 'text/plain, l10n + multiple #2' => [ - 'propertyValues' => [ + 'val' => [ 'en' => $paths['document.txt'].','.$paths['scream.wav'], 'fr' => [ $paths['stuff.txt'], $paths['cat.jpg'] ], ], - 'propertyL10n' => false, - 'propertyMultiple' => false, + 'l10n' => false, + 'multiple' => false, 'acceptedMimetypes' => [ 'text/plain' ], - 'assertValidationReturn' => false, - 'assertValidationResults' => [ + 'expectedReturn' => false, + 'expectedResults' => [ Validator::ERROR => [ 'File ['.$paths['scream.wav'].'] has unacceptable MIME type [audio/%s]', 'File ['.$paths['cat.jpg'].'] has unacceptable MIME type [image/%s]', @@ -522,139 +472,138 @@ public function provideDataForValidateMimetypes() * Provide property data for {@see FileProperty::validateFilesizes()}. * * @used-by self::testValidateFilesizes() - * @return array */ - public function provideDataForValidateFilesizes() + public function provideDataForValidateFilesizes(): array { $paths = $this->getFileMapOfFixtures(); return [ 'any size, no value' => [ - 'propertyValues' => null, - 'propertyL10n' => false, - 'propertyMultiple' => false, + 'val' => null, + 'l10n' => false, + 'multiple' => false, 'maxFilesize' => 0, - 'assertValidationReturn' => true, - 'assertValidationResults' => [], + 'expectedReturn' => true, + 'expectedResults' => [], ], 'any size, text file' => [ - 'propertyValues' => $paths['document.txt'], - 'propertyL10n' => false, - 'propertyMultiple' => false, + 'val' => $paths['document.txt'], + 'l10n' => false, + 'multiple' => false, 'maxFilesize' => 0, - 'assertValidationReturn' => true, - 'assertValidationResults' => [], + 'expectedReturn' => true, + 'expectedResults' => [], ], 'max 10kB, no value' => [ - 'propertyValues' => null, - 'propertyL10n' => false, - 'propertyMultiple' => false, + 'val' => null, + 'l10n' => false, + 'multiple' => false, 'maxFilesize' => 10240, - 'assertValidationReturn' => true, - 'assertValidationResults' => [], + 'expectedReturn' => true, + 'expectedResults' => [], ], 'max 10kB, single text file' => [ - 'propertyValues' => $paths['document.txt'], - 'propertyL10n' => false, - 'propertyMultiple' => false, + 'val' => $paths['document.txt'], + 'l10n' => false, + 'multiple' => false, 'maxFilesize' => 10240, - 'assertValidationReturn' => true, - 'assertValidationResults' => [], + 'expectedReturn' => true, + 'expectedResults' => [], ], 'max 10kB, single image file' => [ - 'propertyValues' => $paths['panda.png'], - 'propertyL10n' => false, - 'propertyMultiple' => false, + 'val' => $paths['panda.png'], + 'l10n' => false, + 'multiple' => false, 'maxFilesize' => 10240, - 'assertValidationReturn' => false, - 'assertValidationResults' => [ + 'expectedReturn' => false, + 'expectedResults' => [ Validator::ERROR => [ 'File ['.$paths['panda.png'].'] exceeds maximum file size [%s]', ], ], ], 'max 10kB, nonexistent file' => [ - 'propertyValues' => $paths['nonexistent.txt'], - 'propertyL10n' => false, - 'propertyMultiple' => false, + 'val' => $paths['nonexistent.txt'], + 'l10n' => false, + 'multiple' => false, 'maxFilesize' => 10240, - 'assertValidationReturn' => false, - 'assertValidationResults' => [ + 'expectedReturn' => false, + 'expectedResults' => [ Validator::ERROR => [ 'File ['.$paths['nonexistent.txt'].'] not found or size unknown', ], ], ], 'max 10kB, l10n, text file' => [ - 'propertyValues' => $paths['document.txt'], - 'propertyL10n' => true, - 'propertyMultiple' => false, + 'val' => $paths['document.txt'], + 'l10n' => true, + 'multiple' => false, 'maxFilesize' => 10240, - 'assertValidationReturn' => true, - 'assertValidationResults' => [], + 'expectedReturn' => true, + 'expectedResults' => [], ], 'max 10kB, l10n, text + image file' => [ - 'propertyValues' => [ + 'val' => [ 'en' => $paths['document.txt'], 'fr' => $paths['panda.png'], ], - 'propertyL10n' => true, - 'propertyMultiple' => false, + 'l10n' => true, + 'multiple' => false, 'maxFilesize' => 10240, - 'assertValidationReturn' => false, - 'assertValidationResults' => [ + 'expectedReturn' => false, + 'expectedResults' => [ Validator::ERROR => [ 'File ['.$paths['panda.png'].'] exceeds maximum file size [%s]', ], ], ], 'max 10kB, multiple, text files' => [ - 'propertyValues' => [ + 'val' => [ $paths['document.txt'], $paths['todo.txt'], ], - 'propertyL10n' => false, - 'propertyMultiple' => true, + 'l10n' => false, + 'multiple' => true, 'maxFilesize' => 10240, - 'assertValidationReturn' => true, - 'assertValidationResults' => [], + 'expectedReturn' => true, + 'expectedResults' => [], ], 'max 10kB, multiple, text + image file' => [ - 'propertyValues' => [ + 'val' => [ $paths['document.txt'], $paths['panda.png'], ], - 'propertyL10n' => false, - 'propertyMultiple' => true, + 'l10n' => false, + 'multiple' => true, 'maxFilesize' => 10240, - 'assertValidationReturn' => false, - 'assertValidationResults' => [ + 'expectedReturn' => false, + 'expectedResults' => [ Validator::ERROR => [ 'File ['.$paths['panda.png'].'] exceeds maximum file size [%s]', ], ], ], 'max 10kB, l10n + multiple #1' => [ - 'propertyValues' => [ + 'val' => [ 'en' => $paths['document.txt'].','.$paths['todo.txt'], 'fr' => [ $paths['stuff.txt'], $paths['draft.txt'] ], ], - 'propertyL10n' => false, - 'propertyMultiple' => false, + 'l10n' => false, + 'multiple' => false, 'maxFilesize' => 10240, - 'assertValidationReturn' => true, - 'assertValidationResults' => [], + 'expectedReturn' => true, + 'expectedResults' => [], ], 'max 10kB, l10n + multiple #2' => [ - 'propertyValues' => [ + 'val' => [ 'en' => $paths['document.txt'].','.$paths['scream.wav'], 'fr' => [ $paths['stuff.txt'], $paths['panda.png'] ], ], - 'propertyL10n' => false, - 'propertyMultiple' => false, + 'l10n' => false, + 'multiple' => false, 'maxFilesize' => 10240, - 'assertValidationReturn' => false, - 'assertValidationResults' => [ + 'expectedReturn' => false, + 'expectedResults' => [ Validator::ERROR => [ 'File ['.$paths['scream.wav'].'] exceeds maximum file size [%s]', 'File ['.$paths['panda.png'].'] exceeds maximum file size [%s]', @@ -668,9 +617,8 @@ public function provideDataForValidateFilesizes() * Provide property data for {@see ImageProperty::generateExtension()}. * * @used-by AbstractFilePropertyTestCase::testGenerateExtensionFromDataProvider() - * @return array */ - public function provideDataForGenerateExtension() + public static function provideDataForGenerateExtension(): array { return [ [ 'text/plain', 'txt' ], diff --git a/packages/property/tests/Charcoal/Property/FixturesTrait.php b/packages/property/tests/Charcoal/Property/FixturesTrait.php index e04cdbb25..88c2b9db9 100644 --- a/packages/property/tests/Charcoal/Property/FixturesTrait.php +++ b/packages/property/tests/Charcoal/Property/FixturesTrait.php @@ -13,7 +13,7 @@ trait FixturesTrait * @param string $file The file path relative to the Fixture directory. * @return string The file path to the fixture relative to the base directory. */ - public function getPathToFixture($file) + public function getPathToFixture($file): string { return $this->getPathToFixtures().'/'.ltrim($file, '/'); } @@ -23,7 +23,7 @@ public function getPathToFixture($file) * * @return string The path to the fixtures directory relative to the base directory. */ - public function getPathToFixtures() + public function getPathToFixtures(): string { return 'tests/Charcoal/Property/Fixture'; } diff --git a/packages/property/tests/Charcoal/Property/GenericPropertyTest.php b/packages/property/tests/Charcoal/Property/GenericPropertyTest.php index b602aa6c2..5cb7bcbfc 100644 --- a/packages/property/tests/Charcoal/Property/GenericPropertyTest.php +++ b/packages/property/tests/Charcoal/Property/GenericPropertyTest.php @@ -1,5 +1,7 @@ getContainer(); @@ -32,27 +31,24 @@ protected function setUp(): void ]); } - /** - * @return void - */ - public function testType() + public function testType(): void { $this->assertEquals('generic', $this->obj->type()); } - public function testSqlExtra() + public function testSqlExtra(): void { $this->assertEquals('', $this->obj->sqlExtra()); } - public function testSqlType() + public function testSqlType(): void { $this->assertEquals('VARCHAR(255)', $this->obj->sqlType()); $this->obj->setMultiple(true); $this->assertEquals('TEXT', $this->obj->sqlType()); } - public function testSqlPdoType() + public function testSqlPdoType(): void { $this->assertEquals(\PDO::PARAM_STR, $this->obj->sqlPdoType()); } diff --git a/packages/property/tests/Charcoal/Property/HtmlPropertyTest.php b/packages/property/tests/Charcoal/Property/HtmlPropertyTest.php index ef5f162a7..ab53fa4c5 100644 --- a/packages/property/tests/Charcoal/Property/HtmlPropertyTest.php +++ b/packages/property/tests/Charcoal/Property/HtmlPropertyTest.php @@ -18,9 +18,6 @@ class HtmlPropertyTest extends AbstractTestCase */ public $obj; - /** - * @return void - */ protected function setUp(): void { $container = $this->getContainer(); @@ -32,15 +29,12 @@ protected function setUp(): void ]); } - /** - * @return void - */ - public function testType() + public function testType(): void { $this->assertEquals('html', $this->obj->type()); } - public function testDefaults() + public function testDefaults(): void { $this->assertFalse($this->obj['required']); $this->assertFalse($this->obj['unique']); @@ -53,19 +47,13 @@ public function testDefaults() $this->assertTrue($this->obj['long']); } - /** - * @return void - */ - public function testDefaultMaxLength() + public function testDefaultMaxLength(): void { $this->assertEquals(0, $this->obj['maxLength']); $this->assertEquals(0, $this->obj->defaultMaxLength()); } - /** - * @return void - */ - public function testSqlType() + public function testSqlType(): void { $this->obj->setLong(false); $this->assertEquals('TEXT', $this->obj->sqlType()); @@ -74,7 +62,7 @@ public function testSqlType() $this->assertEquals('LONGTEXT', $this->obj->sqlType()); } - public function testFilesystem() + public function testFilesystem(): void { $this->assertEquals('', $this->obj['filesystem']); diff --git a/packages/property/tests/Charcoal/Property/IdPropertyTest.php b/packages/property/tests/Charcoal/Property/IdPropertyTest.php index ff05b7748..2641e649a 100644 --- a/packages/property/tests/Charcoal/Property/IdPropertyTest.php +++ b/packages/property/tests/Charcoal/Property/IdPropertyTest.php @@ -18,14 +18,8 @@ class IdPropertyTest extends AbstractTestCase { use \Charcoal\Tests\Property\ContainerIntegrationTrait; - /** - * @var IdProperty - */ - private $obj; + private \Charcoal\Property\IdProperty|array $obj; - /** - * @return void - */ protected function setUp(): void { $container = $this->getContainer(); @@ -37,18 +31,12 @@ protected function setUp(): void ]); } - /** - * @return void - */ - public function testType() + public function testType(): void { $this->assertEquals('id', $this->obj->type()); } - /** - * @return void - */ - public function testSetData() + public function testSetData(): void { $ret = $this->obj->setData( [ @@ -62,10 +50,8 @@ public function testSetData() /** * Asserts that the default mode: * - Defaults to auto-increment - * - * @return void */ - public function testDefaultMode() + public function testDefaultMode(): void { $this->assertEquals(IdProperty::DEFAULT_MODE, $this->obj['mode']); $this->assertEquals('auto-increment', $this->obj['mode']); @@ -76,10 +62,8 @@ public function testDefaultMode() * - is chainable * - properly sets the mode * - throws an invalid argument exception for any string modes - * - * @return void */ - public function testSetMode() + public function testSetMode(): void { $ret = $this->obj->setMode('uuid'); $this->assertSame($ret, $this->obj); @@ -99,19 +83,14 @@ public function testSetMode() * Asserts that calling the `setMode()` method with a NULL argument: * - is chainable * - properly resets the mode to detault - * - * @return void */ - public function testSetModeNullThrowsException() + public function testSetModeNullThrowsException(): void { $this->expectException(InvalidArgumentException::class); $this->obj->setMode(null); } - /** - * @return void - */ - public function testMultipleCannotBeTrue() + public function testMultipleCannotBeTrue(): void { $this->assertFalse($this->obj['multiple']); @@ -120,10 +99,7 @@ public function testMultipleCannotBeTrue() $this->obj->setMultiple(true); } - /** - * @return void - */ - public function testL10nCannotBeTrue() + public function testL10nCannotBeTrue(): void { $this->assertFalse($this->obj['l10n']); @@ -132,10 +108,7 @@ public function testL10nCannotBeTrue() $this->obj->setL10n(true); } - /** - * @return void - */ - public function testSaveAndAutoGenerateAutoIncrement() + public function testSaveAndAutoGenerateAutoIncrement(): void { $obj = $this->obj; $obj->setMode('auto-increment'); @@ -143,32 +116,23 @@ public function testSaveAndAutoGenerateAutoIncrement() $this->assertEquals('', $id); } - /** - * @return void - */ - public function testSaveAndAutoGenerateUniqid() + public function testSaveAndAutoGenerateUniqid(): void { $obj = $this->obj; $obj->setMode('uniqid'); $id = $obj->save(null); - $this->assertEquals(13, strlen($id)); + $this->assertEquals(13, strlen((string) $id)); } - /** - * @return void - */ - public function testSaveAndAutoGenerateUuid() + public function testSaveAndAutoGenerateUuid(): void { $obj = $this->obj; $obj->setMode('uuid'); $id = $obj->save(null); - $this->assertEquals(36, strlen($id)); + $this->assertEquals(36, strlen((string) $id)); } - /** - * @return void - */ - public function testSqlExtra() + public function testSqlExtra(): void { $obj = $this->obj; $obj->setMode('auto-increment'); @@ -180,12 +144,9 @@ public function testSqlExtra() $this->assertEquals('', $ret); } - /** - * @return void - */ - public function testSqlType() + public function testSqlType(): void { - $container = $this->getContainer(); + $this->getContainer(); $obj = $this->obj; $obj->setMode('auto-increment'); @@ -206,10 +167,7 @@ public function testSqlType() $this->assertEquals('VARCHAR(255)', $ret); } - /** - * @return void - */ - public function testSqlPdoType() + public function testSqlPdoType(): void { $obj = $this->obj; $obj->setMode('auto-increment'); diff --git a/packages/property/tests/Charcoal/Property/ImagePropertyTest.php b/packages/property/tests/Charcoal/Property/ImagePropertyTest.php index 845c5208d..5e2dab3c0 100644 --- a/packages/property/tests/Charcoal/Property/ImagePropertyTest.php +++ b/packages/property/tests/Charcoal/Property/ImagePropertyTest.php @@ -7,17 +7,15 @@ // From 'charcoal-property' use Charcoal\Property\ImageProperty; -/** - * - */ +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Property\ImageProperty::class, 'type()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Property\ImageProperty::class, 'getDefaultAcceptedMimetypes()')] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Charcoal\Property\ImageProperty::class, 'hasAcceptedMimetypes()')] class ImagePropertyTest extends AbstractFilePropertyTestCase { /** * Create a file property instance. - * - * @return ImageProperty */ - public function createProperty() + public function createProperty(): \Charcoal\Property\ImageProperty { $container = $this->getContainer(); @@ -31,21 +29,17 @@ public function createProperty() /** * Asserts that the `type()` method is "file". - * - * @covers \Charcoal\Property\ImageProperty::type() - * @return void */ - public function testPropertyType() + public function testPropertyType(): void { $this->assertEquals('image', $this->obj->type()); } /** * Asserts that the property adheres to file property defaults. - * - * @return void */ - public function testPropertyDefaults() + #[\Override] + public function testPropertyDefaults(): void { parent::testPropertyDefaults(); @@ -58,11 +52,8 @@ public function testPropertyDefaults() /** * Asserts that the property adheres to file property defaults. - * - * @covers \Charcoal\Property\ImageProperty::getDefaultAcceptedMimetypes() - * @return void */ - public function testDefaulAcceptedMimeTypes() + public function testDefaulAcceptedMimeTypes(): void { $this->assertIsArray($this->obj['defaultAcceptedMimetypes']); $this->assertNotEmpty($this->obj['defaultAcceptedMimetypes']); @@ -71,11 +62,8 @@ public function testDefaulAcceptedMimeTypes() /** * Asserts that the property properly checks if * any acceptable MIME types are available. - * - * @covers \Charcoal\Property\ImageProperty::hasAcceptedMimetypes() - * @return void */ - public function testHasAcceptedMimeTypes() + public function testHasAcceptedMimeTypes(): void { $this->assertTrue($this->obj->hasAcceptedMimetypes()); @@ -85,10 +73,8 @@ public function testHasAcceptedMimeTypes() /** * Asserts that the property can resolve a filesize from its value. - * - * @return void */ - public function testFilesizeFromVal() + public function testFilesizeFromVal(): void { $obj = $this->obj; @@ -100,10 +86,8 @@ public function testFilesizeFromVal() /** * Asserts that the property can resolve a MIME type from its value. - * - * @return void */ - public function testMimetypeFromVal() + public function testMimetypeFromVal(): void { $obj = $this->obj; @@ -113,10 +97,7 @@ public function testMimetypeFromVal() $this->assertEquals('image/png', $obj['mimetype']); } - /** - * @return void - */ - public function testSetEffects() + public function testSetEffects(): void { $this->assertEquals([], $this->obj['effects']); $ret = $this->obj->setEffects([['type'=>'blur', 'sigma'=>'1']]); @@ -131,10 +112,7 @@ public function testSetEffects() $this->assertEquals(1, count($this->obj['effects'])); } - /** - * @return void - */ - public function testAddEffect() + public function testAddEffect(): void { $this->assertEquals(0, count($this->obj['effects'])); @@ -146,7 +124,7 @@ public function testAddEffect() $this->assertEquals(2, count($this->obj['effects'])); } - public function testSetApplyEffects() + public function testSetApplyEffects(): void { $this->assertEquals('save', $this->obj['applyEffects']); $this->assertTrue($this->obj->canApplyEffects('save')); @@ -174,7 +152,7 @@ public function testSetApplyEffects() $this->obj->setApplyEffects('foobar'); } - public function testDriverType() + public function testDriverType(): void { $this->assertEquals(ImageProperty::DEFAULT_DRIVER_TYPE, $this->obj['driverType']); $ret = $this->obj->setDriverType('foo'); @@ -185,13 +163,14 @@ public function testDriverType() $this->obj->setDriverType(false); } - public function testProcessEffects() + public function testProcessEffects(): void { $ret = $this->obj->processEffects(null, []); $this->assertNull($ret); } - public function testAcceptedMimetypes() + #[\Override] + public function testAcceptedMimetypes(): void { $ret = $this->obj['acceptedMimetypes']; $this->assertContains('image/png', $ret); @@ -202,9 +181,8 @@ public function testAcceptedMimetypes() * Provide property data for {@see ImageProperty::generateExtension()}. * * @used-by AbstractFilePropertyTestCase::testGenerateExtensionFromDataProvider() - * @return array */ - public function provideDataForGenerateExtension() + public static function provideDataForGenerateExtension(): array { return [ [ 'image/gif', 'gif' ], diff --git a/packages/property/tests/Charcoal/Property/IpPropertyTest.php b/packages/property/tests/Charcoal/Property/IpPropertyTest.php index 63056fe5f..7e6c799c5 100644 --- a/packages/property/tests/Charcoal/Property/IpPropertyTest.php +++ b/packages/property/tests/Charcoal/Property/IpPropertyTest.php @@ -20,9 +20,6 @@ class IpPropertyTest extends AbstractTestCase */ public $obj; - /** - * @return void - */ protected function setUp(): void { $container = $this->getContainer(); @@ -34,26 +31,17 @@ protected function setUp(): void ]); } - /** - * @return void - */ - public function testType() + public function testType(): void { $this->assertEquals('ip', $this->obj->type()); } - /** - * @return void - */ - public function testDefaults() + public function testDefaults(): void { $this->assertEquals('string', $this->obj['storageMode']); } - /** - * @return void - */ - public function testMultipleCannotBeTrue() + public function testMultipleCannotBeTrue(): void { $this->assertFalse($this->obj['multiple']); @@ -62,10 +50,7 @@ public function testMultipleCannotBeTrue() $this->obj->setMultiple(true); } - /** - * @return void - */ - public function testL10nCannotBeTrue() + public function testL10nCannotBeTrue(): void { $this->assertFalse($this->obj['l10n']); @@ -74,10 +59,7 @@ public function testL10nCannotBeTrue() $this->obj->setL10n(true); } - /** - * @return void - */ - public function testSetStorageMode() + public function testSetStorageMode(): void { $this->assertEquals('string', $this->obj['storageMode']); $ret = $this->obj->setStorageMode('int'); @@ -88,10 +70,7 @@ public function testSetStorageMode() $this->obj->setStorageMode('foobar'); } - /** - * @return void - */ - public function testIntVal() + public function testIntVal(): void { $this->assertEquals(0, $this->obj->intVal('0.0.0.0')); $this->assertEquals(2130706433, $this->obj->intVal('127.0.0.1')); @@ -100,10 +79,7 @@ public function testIntVal() $this->assertEquals(3232235777, $this->obj->intVal('3232235777')); } - /** - * @return void - */ - public function testStringVal() + public function testStringVal(): void { $this->assertEquals('0.0.0.0', $this->obj->stringVal(0)); $this->assertEquals('127.0.0.1', $this->obj->stringVal(2130706433)); @@ -111,14 +87,14 @@ public function testStringVal() $this->assertEquals('8.8.8.8', $this->obj->stringVal('8.8.8.8')); } - public function testStorageVal() + public function testStorageVal(): void { $this->assertEquals('0.0.0.0', $this->obj->storageVal('0.0.0.0')); $this->assertEquals('127.0.0.1', $this->obj->storageVal('127.0.0.1')); $this->assertEquals('127.0.0.1', $this->obj->stringVal('127.0.0.1')); } - public function testHostname() + public function testHostname(): void { $this->assertEquals('0.0.0.0', $this->obj->hostname(0)); $this->assertThat($this->obj->hostname('8.8.8.8'), $this->logicalOr( @@ -127,10 +103,7 @@ public function testHostname() )); } - /** - * @return void - */ - public function testSqlExtra() + public function testSqlExtra(): void { $this->assertEquals('', $this->obj->sqlExtra()); } @@ -139,10 +112,8 @@ public function testSqlExtra() * Asserts that the `sqlType()` method: * - returns "VARCHAR(15)" if the storage mode is "string" (default). * - returns "BIGINT" if the storage mode is "int". - * - * @return void */ - public function testSqlType() + public function testSqlType(): void { $this->obj->setStorageMode('string'); $this->assertEquals('VARCHAR(15)', $this->obj->sqlType()); @@ -151,10 +122,7 @@ public function testSqlType() $this->assertEquals('BIGINT', $this->obj->sqlType()); } - /** - * @return void - */ - public function testSqlPdoType() + public function testSqlPdoType(): void { $this->obj->setStorageMode('string'); $this->assertEquals(PDO::PARAM_STR, $this->obj->sqlPdoType()); diff --git a/packages/property/tests/Charcoal/Property/LangPropertyTest.php b/packages/property/tests/Charcoal/Property/LangPropertyTest.php index bdf9ac286..aee766669 100644 --- a/packages/property/tests/Charcoal/Property/LangPropertyTest.php +++ b/packages/property/tests/Charcoal/Property/LangPropertyTest.php @@ -25,8 +25,6 @@ class LangPropertyTest extends AbstractTestCase /** * Set up the test. - * - * @return void */ protected function setUp(): void { @@ -42,26 +40,17 @@ protected function setUp(): void ]); } - /** - * @return void - */ - public function testType() + public function testType(): void { $this->assertEquals('lang', $this->obj->type()); } - /** - * @return void - */ - public function testSqlExtra() + public function testSqlExtra(): void { $this->assertEquals('', $this->obj->sqlExtra()); } - /** - * @return void - */ - public function testSqlType() + public function testSqlType(): void { $this->obj->setMultiple(false); $this->assertEquals('CHAR(2)', $this->obj->sqlType()); @@ -70,18 +59,12 @@ public function testSqlType() $this->assertEquals('TEXT', $this->obj->sqlType()); } - /** - * @return void - */ - public function testSqlPdoType() + public function testSqlPdoType(): void { $this->assertEquals(PDO::PARAM_STR, $this->obj->sqlPdoType()); } - /** - * @return void - */ - public function testChoices() + public function testChoices(): void { $container = $this->getContainer(); $translator = $container['translator']; @@ -99,10 +82,7 @@ public function testChoices() $this->assertEquals(array_keys($locales), array_keys($choices)); } - /** - * @return void - */ - public function testDisplayVal() + public function testDisplayVal(): void { $container = $this->getContainer(); $translator = $container['translator']; diff --git a/packages/property/tests/Charcoal/Property/MapStructurePropertyTest.php b/packages/property/tests/Charcoal/Property/MapStructurePropertyTest.php index 1701e1003..b44e96456 100644 --- a/packages/property/tests/Charcoal/Property/MapStructurePropertyTest.php +++ b/packages/property/tests/Charcoal/Property/MapStructurePropertyTest.php @@ -1,5 +1,7 @@ getContainer(); @@ -33,10 +32,7 @@ protected function setUp(): void ]); } - /** - * @return void - */ - public function testType() + public function testType(): void { $this->assertEquals('map-structure', $this->obj->type()); } diff --git a/packages/property/tests/Charcoal/Property/Mocks/GenericModel.php b/packages/property/tests/Charcoal/Property/Mocks/GenericModel.php index ae900ac45..f3241ed3d 100644 --- a/packages/property/tests/Charcoal/Property/Mocks/GenericModel.php +++ b/packages/property/tests/Charcoal/Property/Mocks/GenericModel.php @@ -34,7 +34,7 @@ class GenericModel extends AbstractModel /** * @param array $data Dependencies. */ - public function __construct(array $data = null) + public function __construct(?array $data = null) { $data['metadata'] = [ 'default_data' => [ @@ -69,18 +69,17 @@ public function __construct(array $data = null) /** * @param Container $container DI Container. - * @return void */ - public function setDependencies(Container $container) + #[\Override] + public function setDependencies(Container $container): void { $this->setTranslator($container['translator']); } /** * @param mixed $name The name of the model. - * @return self */ - public function setName($name) + public function setName($name): static { $this->name = $this->translator()->translation($name); @@ -95,10 +94,7 @@ public function name() return $this->name; } - /** - * @return string - */ - public function icon() + public function icon(): string { return ''; } diff --git a/packages/property/tests/Charcoal/Property/ModelStructurePropertyTest.php b/packages/property/tests/Charcoal/Property/ModelStructurePropertyTest.php index 28990fd29..73d4b0913 100644 --- a/packages/property/tests/Charcoal/Property/ModelStructurePropertyTest.php +++ b/packages/property/tests/Charcoal/Property/ModelStructurePropertyTest.php @@ -18,9 +18,6 @@ class ModelStructurePropertyTest extends AbstractTestCase */ public $obj; - /** - * @return void - */ protected function setUp(): void { $container = $this->getContainer(); @@ -32,15 +29,12 @@ protected function setUp(): void ]); } - /** - * @return void - */ - public function testType() + public function testType(): void { $this->assertEquals('model-structure', $this->obj->type()); } - public function testSetStructureMetadata() + public function testSetStructureMetadata(): void { $ret = $this->obj->setStructureMetadata(null); $this->assertSame($ret, $this->obj); @@ -51,7 +45,7 @@ public function testSetStructureMetadata() $this->obj->setStructureMetadata('foo'); } - public function setStructureInterfaces() + public function setStructureInterfaces(): void { $ret = $this->obj->setStructureInterfaces([]); $this->assertSame($ret, $this->obj); diff --git a/packages/property/tests/Charcoal/Property/NumberPropertyTest.php b/packages/property/tests/Charcoal/Property/NumberPropertyTest.php index be17d444c..b4e1bfd3b 100644 --- a/packages/property/tests/Charcoal/Property/NumberPropertyTest.php +++ b/packages/property/tests/Charcoal/Property/NumberPropertyTest.php @@ -18,9 +18,6 @@ class NumberPropertyTest extends AbstractTestCase */ public $obj; - /** - * @return void - */ protected function setUp(): void { $container = $this->getContainer(); @@ -32,21 +29,18 @@ protected function setUp(): void ]); } - /** - * @return void - */ - public function testType() + public function testType(): void { $this->assertEquals('number', $this->obj->type()); } - public function testDefaults() + public function testDefaults(): void { $this->assertNull($this->obj->getMin()); $this->assertNull($this->obj->getMax()); } - public function testSetData() + public function testSetData(): void { $this->obj->setData([ 'min' => 0, @@ -56,7 +50,7 @@ public function testSetData() $this->assertEquals(100, $this->obj->getMax()); } - public function testValidationMethods() + public function testValidationMethods(): void { $ret = $this->obj->validationMethods(); $this->assertContains('min', $ret); diff --git a/packages/property/tests/Charcoal/Property/ObjectPropertyTest.php b/packages/property/tests/Charcoal/Property/ObjectPropertyTest.php index 43e67c399..88cf3712f 100644 --- a/packages/property/tests/Charcoal/Property/ObjectPropertyTest.php +++ b/packages/property/tests/Charcoal/Property/ObjectPropertyTest.php @@ -48,8 +48,6 @@ class ObjectPropertyTest extends AbstractTestCase /** * Set up the test. - * - * @return void */ protected function setUp(): void { @@ -72,7 +70,7 @@ protected function setUp(): void * collection of models for testing. * @return array Returns the collection of object data for testing. */ - public function setUpObjects(&$models = null) + public function setUpObjects(&$models = null): array { $container = $this->getContainer(); $translator = $container['translator']; @@ -104,13 +102,12 @@ public function setUpObjects(&$models = null) } /** - * @dataProvider provideMissingDependencies * * @param string $method The name of a method accessor. * @param string $expectedException The expected Exception thrown by $method. - * @return void */ - public function testConstructorWithoutDependencies($method, $expectedException) + #[\PHPUnit\Framework\Attributes\DataProvider('provideMissingDependencies')] + public function testConstructorWithoutDependencies(string $method, string $expectedException): void { $container = $this->getContainer(); @@ -124,10 +121,7 @@ public function testConstructorWithoutDependencies($method, $expectedException) $this->callMethod($prop, $method); } - /** - * @return array - */ - public function provideMissingDependencies() + public static function provideMissingDependencies(): array { return [ [ 'modelFactory', RuntimeException::class ], @@ -137,24 +131,20 @@ public function provideMissingDependencies() } /** - * @dataProvider provideSatisfiedDependencies * * @param string $method The name of a method accessor. * @param string $expectedObject The expected instance returned by $method. - * @return void */ - public function testConstructorWithDependencies($method, $expectedObject) + #[\PHPUnit\Framework\Attributes\DataProvider('provideSatisfiedDependencies')] + public function testConstructorWithDependencies(string $method, string $expectedObject): void { - $container = $this->getContainer(); + $this->getContainer(); $dependency = $this->callMethod($this->obj, $method); $this->assertInstanceOf($expectedObject, $dependency); } - /** - * @return array - */ - public function provideSatisfiedDependencies() + public static function provideSatisfiedDependencies(): array { return [ [ 'modelFactory', FactoryInterface::class ], @@ -163,26 +153,17 @@ public function provideSatisfiedDependencies() ]; } - /** - * @return void - */ - public function testType() + public function testType(): void { $this->assertEquals('object', $this->obj->type()); } - /** - * @return void - */ - public function testSqlExtra() + public function testSqlExtra(): void { $this->assertEquals('', $this->obj->sqlExtra()); } - /** - * @return void - */ - public function testSqlType() + public function testSqlType(): void { $this->obj->setObjType(GenericModel::class); $this->assertEquals('CHAR(13)', $this->obj->sqlType()); @@ -191,19 +172,13 @@ public function testSqlType() $this->assertEquals('TEXT', $this->obj->sqlType()); } - /** - * @return void - */ - public function testSqlPdoType() + public function testSqlPdoType(): void { $this->obj->setObjType(GenericModel::class); $this->assertEquals(PDO::PARAM_STR, $this->obj->sqlPdoType()); } - /** - * @return void - */ - public function testSetObjType() + public function testSetObjType(): void { $return = $this->obj->setObjType('foo'); $this->assertSame($return, $this->obj); @@ -213,19 +188,13 @@ public function testSetObjType() $this->obj->setObjType(false); } - /** - * @return void - */ - public function testAccessingObjTypeBeforeSetterThrowsException() + public function testAccessingObjTypeBeforeSetterThrowsException(): void { $this->expectException('\Exception'); $this->obj['objType']; } - /** - * @return void - */ - public function testSetPattern() + public function testSetPattern(): void { $return = $this->obj->setPattern('{{foo}}'); $this->assertSame($return, $this->obj); @@ -235,10 +204,7 @@ public function testSetPattern() $this->obj->setPattern([]); } - /** - * @return void - */ - public function testParseOneWithScalarValue() + public function testParseOneWithScalarValue(): void { $this->assertEquals('foobar', $this->obj->parseOne('foobar')); @@ -252,19 +218,13 @@ public function testParseOneWithScalarValue() $this->assertEquals('foo', $this->obj->parseOne($mock)); } - /** - * @return void - */ - public function testParseOneWithObjectWithoutIdReturnsNull() + public function testParseOneWithObjectWithoutIdReturnsNull(): void { - $mock = $this->createMock(StorableInterface::class); + $mock = $this->createStub(StorableInterface::class); $this->assertNull($this->obj->parseOne($mock)); } - /** - * @return void - */ - public function testParseOneWithObjectWithIdReturnsId() + public function testParseOneWithObjectWithIdReturnsId(): void { $mock = $this->createMock(StorableInterface::class); $mock->expects($this->any()) @@ -273,10 +233,7 @@ public function testParseOneWithObjectWithIdReturnsId() $this->assertEquals('foo', $this->obj->parseOne($mock)); } - /** - * @return void - */ - public function testDisplayVal() + public function testDisplayVal(): void { $objs = $this->setUpObjects($models); $first = $models[self::OBJ_1]; @@ -323,10 +280,7 @@ public function testDisplayVal() $this->assertEquals($expected, $actual); } - /** - * @return void - */ - public function testInputVal() + public function testInputVal(): void { $container = $this->getContainer(); @@ -345,10 +299,7 @@ public function testInputVal() $this->assertEquals('foo,baz,qux', $this->obj->inputVal([ 'foo', 'baz', 'qux' ])); } - /** - * @return void - */ - public function testStorageVal() + public function testStorageVal(): void { $container = $this->getContainer(); @@ -367,12 +318,9 @@ public function testStorageVal() $this->assertEquals('foo,baz,qux', $this->obj->storageVal([ 'foo', 'baz', 'qux' ])); } - /** - * @return void - */ - public function testRenderObjPattern() + public function testRenderObjPattern(): void { - $objs = $this->setUpObjects($models); + $this->setUpObjects($models); $return = $this->callMethod($this->obj, 'renderObjPattern', [ $models[self::OBJ_1], '' ]); $this->assertEmpty($return); @@ -388,10 +336,7 @@ public function testRenderObjPattern() $this->assertEquals($models[self::OBJ_1]['name']['en'], $return); } - /** - * @return void - */ - public function testRenderViewableObjPattern() + public function testRenderViewableObjPattern(): void { $container = $this->getContainer(); $this->getContainerProvider()->registerView($container); @@ -403,43 +348,34 @@ public function testRenderViewableObjPattern() $factory->setArguments($depends); - $objs = $this->setUpObjects($models); + $this->setUpObjects($models); /** Test 'charcoal-view' renderer */ $return = $this->callMethod($this->obj, 'renderObjPattern', [ $models[self::OBJ_1] ]); $this->assertEquals($models[self::OBJ_1]['name']['en'], $return); } - /** - * @return void - */ - public function testRenderObjPatternThrowsExceptionWithBadPattern() + public function testRenderObjPatternThrowsExceptionWithBadPattern(): void { $container = $this->getContainer(); $model = $container['model/factory']->create(GenericModel::class); $this->expectException(InvalidArgumentException::class); - $return = $this->callMethod($this->obj, 'renderObjPattern', [ $model, false ]); + $this->callMethod($this->obj, 'renderObjPattern', [ $model, false ]); } - /** - * @return void - */ - public function testRenderObjPatternThrowsExceptionWithBadLang() + public function testRenderObjPatternThrowsExceptionWithBadLang(): void { $container = $this->getContainer(); $model = $container['model/factory']->create(GenericModel::class); $this->expectException(InvalidArgumentException::class); - $return = $this->callMethod($this->obj, 'renderObjPattern', [ $model, null, false ]); + $this->callMethod($this->obj, 'renderObjPattern', [ $model, null, false ]); } - /** - * @return void - */ - public function testChoices() + public function testChoices(): void { $this->obj->setObjType(GenericModel::class); @@ -448,7 +384,7 @@ public function testChoices() $this->assertEmpty($this->obj->choices()); /** Database table created */ - $objs = $this->setUpObjects($models); + $this->setUpObjects($models); /** Test available choices */ $this->assertTrue($this->obj->hasChoices()); @@ -480,31 +416,21 @@ public function testChoices() $this->assertEquals($fakeId, $this->obj->choiceLabel($fakeId)); } - /** - * @return void - */ - public function testChoiceLabelStructException() + public function testChoiceLabelStructException(): void { $this->expectException(InvalidArgumentException::class); $this->obj->choiceLabel([]); } - /** - * @return void - */ - public function testParseChoicesThrowsException() + public function testParseChoicesThrowsException(): void { $this->expectException(InvalidArgumentException::class); - $return = $this->callMethod($this->obj, 'parseChoices', [ false ]); + $this->callMethod($this->obj, 'parseChoices', [ false ]); } - /** - * @return void - */ - public function testCollectionLoading() + public function testCollectionLoading(): void { - $container = $this->getContainer(); - $translator = $container['translator']; + $this->getContainer(); $this->setUpObjects(); @@ -533,10 +459,7 @@ public function testCollectionLoading() $this->assertEquals($expectedCollection, $collection->keys()); } - /** - * @return void - */ - public function testLoadObject() + public function testLoadObject(): void { $container = $this->getContainer(); @@ -557,12 +480,9 @@ public function testLoadObject() $this->assertNull($return); } - /** - * @return void - */ - public function testModelLoader() + public function testModelLoader(): void { - $objs = $this->setUpObjects(); + $this->setUpObjects(); $this->obj->setObjType(GenericModel::class); @@ -570,14 +490,11 @@ public function testModelLoader() $this->assertInstanceOf(ModelLoader::class, $return); } - /** - * @return void - */ - public function testModelLoaderThrowsException() + public function testModelLoaderThrowsException(): void { $this->obj->setObjType(GenericModel::class); $this->expectException(InvalidArgumentException::class); - $return = $this->callMethod($this->obj, 'modelLoader', [ false ]); + $this->callMethod($this->obj, 'modelLoader', [ false ]); } } diff --git a/packages/property/tests/Charcoal/Property/PasswordPropertyTest.php b/packages/property/tests/Charcoal/Property/PasswordPropertyTest.php index 68c4c878e..fcfe9a56f 100644 --- a/packages/property/tests/Charcoal/Property/PasswordPropertyTest.php +++ b/packages/property/tests/Charcoal/Property/PasswordPropertyTest.php @@ -1,5 +1,7 @@ getContainer(); @@ -31,15 +27,12 @@ protected function setUp(): void 'translator' => $container['translator'] ]); } - /** - * @return void - */ - public function testType() + public function testType(): void { $this->assertEquals('password', $this->obj->type()); } - public function testSave() + public function testSave(): void { $v1 = $this->obj->save('xxx'); $this->assertNotEquals($v1, 'xxx'); diff --git a/packages/property/tests/Charcoal/Property/PhonePropertyTest.php b/packages/property/tests/Charcoal/Property/PhonePropertyTest.php index 42e0ef0e6..d12dff336 100644 --- a/packages/property/tests/Charcoal/Property/PhonePropertyTest.php +++ b/packages/property/tests/Charcoal/Property/PhonePropertyTest.php @@ -1,5 +1,7 @@ getContainer(); @@ -34,35 +33,24 @@ protected function setUp(): void /** * Hello world - * - * @return void */ - public function testDefaultValues() + public function testDefaultValues(): void { $this->assertEquals(0, $this->obj['minLength']); $this->assertEquals(16, $this->obj['maxLength']); } - /** - * @return void - */ - public function testType() + public function testType(): void { $this->assertEquals('phone', $this->obj->type()); } - /** - * @return void - */ - public function testSanitize() + public function testSanitize(): void { $this->assertEquals('5145551234', $this->obj->sanitize('(514) 555-1234')); } - /** - * @return void - */ - public function testDisplayVal() + public function testDisplayVal(): void { $this->assertEquals('(514) 555-1234', $this->obj->displayVal('5145551234')); $this->assertEquals('(514) 555-1234', $this->obj->displayVal('514-555-1234')); diff --git a/packages/property/tests/Charcoal/Property/PropertyFieldTest.php b/packages/property/tests/Charcoal/Property/PropertyFieldTest.php index c97f77728..978a3faf3 100644 --- a/packages/property/tests/Charcoal/Property/PropertyFieldTest.php +++ b/packages/property/tests/Charcoal/Property/PropertyFieldTest.php @@ -21,9 +21,6 @@ class PropertyFieldTest extends AbstractTestCase */ public $obj; - /** - * @return void - */ protected function setUp(): void { $container = $this->getContainer(); @@ -33,10 +30,7 @@ protected function setUp(): void ]); } - /** - * @return void - */ - public function testData() + public function testData(): void { $data = [ 'ident' => 'test', @@ -65,10 +59,7 @@ public function testData() $this->assertEquals($sql, $this->obj->sql()); } - /** - * @return void - */ - public function testIdent() + public function testIdent(): void { $ret = $this->obj->setIdent('title'); $this->assertSame($this->obj, $ret); @@ -79,19 +70,13 @@ public function testIdent() $this->obj->setIdent(null); } - /** - * @return void - */ - public function testSqlReturnsEmptyIfEmptyIdent() + public function testSqlReturnsEmptyIfEmptyIdent(): void { $this->obj->setIdent(''); $this->assertEquals('', $this->obj->sql()); } - /** - * @return void - */ - public function testLabel() + public function testLabel(): void { $this->assertEquals(null, $this->obj->label()); @@ -100,13 +85,10 @@ public function testLabel() $label = $this->obj->label(); $this->assertIsString($label); - $this->assertEquals('Cooking', (string)$label); + $this->assertEquals('Cooking', $label); } - /** - * @return void - */ - public function testPdoType() + public function testPdoType(): void { $this->assertEquals(PDO::PARAM_NULL, $this->obj->sqlPdoType()); @@ -122,10 +104,7 @@ public function testPdoType() $this->obj->setSqlPdoType(null); } - /** - * @return void - */ - public function testSqlType() + public function testSqlType(): void { $ret = $this->obj->setSqlType('INT(10)'); $this->assertSame($this->obj, $ret); @@ -136,10 +115,7 @@ public function testSqlType() $this->obj->setSqlType(0); } - /** - * @return void - */ - public function testSqlExtra() + public function testSqlExtra(): void { $this->assertEquals(null, $this->obj->extra()); @@ -152,10 +128,7 @@ public function testSqlExtra() $this->obj->setExtra(0); } - /** - * @return void - */ - public function testSqlEncoding() + public function testSqlEncoding(): void { $this->assertEquals(null, $this->obj->sqlEncoding()); diff --git a/packages/property/tests/Charcoal/Property/PropertyMetadataTest.php b/packages/property/tests/Charcoal/Property/PropertyMetadataTest.php index f2dc33819..bb91dff5e 100644 --- a/packages/property/tests/Charcoal/Property/PropertyMetadataTest.php +++ b/packages/property/tests/Charcoal/Property/PropertyMetadataTest.php @@ -16,18 +16,12 @@ class PropertyMetadataTest extends AbstractTestCase */ public $obj; - /** - * @return void - */ protected function setUp(): void { $this->obj = new PropertyMetadata(); } - /** - * @return void - */ - public function testSetIdent() + public function testSetIdent(): void { $this->assertEquals('', $this->obj->ident()); $ret = $this->obj->setIdent('foo'); diff --git a/packages/property/tests/Charcoal/Property/SelectablePropertyTraitTest.php b/packages/property/tests/Charcoal/Property/SelectablePropertyTraitTest.php index 162435816..f2b97df0d 100644 --- a/packages/property/tests/Charcoal/Property/SelectablePropertyTraitTest.php +++ b/packages/property/tests/Charcoal/Property/SelectablePropertyTraitTest.php @@ -23,15 +23,11 @@ class SelectablePropertyTraitTest extends AbstractTestCase /** * Tested Class. - * - * @var SelectablePropertyTrait */ - private $obj; + private \Charcoal\Property\SelectablePropertyTrait $obj; /** * Set up the test. - * - * @return void */ protected function setUp(): void { @@ -45,9 +41,8 @@ protected function setUp(): void /** * @param mixed $val The translation string. - * @return Translation */ - public function translation($val) + public function translation($val): \Charcoal\Translator\Translation { $container = $this->getContainer(); $locales = $container['locales/manager']; @@ -55,10 +50,7 @@ public function translation($val) return new Translation($val, $locales); } - /** - * @return void - */ - public function testEmptyChoices() + public function testEmptyChoices(): void { $this->assertEquals([], $this->obj->choices()); @@ -77,10 +69,7 @@ public function testEmptyChoices() $this->assertEquals('qux', $this->obj->choiceLabel('qux')); } - /** - * @return void - */ - public function testChoices() + public function testChoices(): void { $choices = [ 'foo' => 'oof', @@ -116,28 +105,19 @@ public function testChoices() $this->assertEquals($expected['bar']['label'], $this->obj->choiceLabel('bar')); } - /** - * @return void - */ - public function testChoiceLabelStructException() + public function testChoiceLabelStructException(): void { $this->expectException('\InvalidArgumentException'); $this->obj->choiceLabel([]); } - /** - * @return void - */ - public function testChoiceLabelKeyException() + public function testChoiceLabelKeyException(): void { $this->expectException('\InvalidArgumentException'); $this->obj->choiceLabel(0); } - /** - * @return void - */ - public function testParseChoices() + public function testParseChoices(): void { $choices = [ 'foo' => 'oof', @@ -180,19 +160,13 @@ public function testParseChoices() $this->assertEquals($baz, $parsed); } - /** - * @return void - */ - public function testParseChoiceStructException() + public function testParseChoiceStructException(): void { $this->expectException('\InvalidArgumentException'); $this->callMethod($this->obj, 'parseChoice', [ null, 'foo' ]); } - /** - * @return void - */ - public function testParseChoiceKeyException() + public function testParseChoiceKeyException(): void { $this->expectException('\InvalidArgumentException'); $this->callMethod($this->obj, 'parseChoice', [ 'foo', 0 ]); diff --git a/packages/property/tests/Charcoal/Property/SpritePropertyTest.php b/packages/property/tests/Charcoal/Property/SpritePropertyTest.php index 4de3258f2..a55038511 100644 --- a/packages/property/tests/Charcoal/Property/SpritePropertyTest.php +++ b/packages/property/tests/Charcoal/Property/SpritePropertyTest.php @@ -20,9 +20,6 @@ class SpritePropertyTest extends AbstractTestCase */ public $obj; - /** - * @return void - */ protected function setUp(): void { $container = $this->getContainer(); @@ -36,20 +33,17 @@ protected function setUp(): void ]); } - public function testDefaults() + public function testDefaults(): void { $this->assertNull($this->obj['sprite']); } - /** - * @return void - */ - public function testType() + public function testType(): void { $this->assertEquals('sprite', $this->obj->type()); } - public function testSetSprite() + public function testSetSprite(): void { $this->assertNull($this->obj['sprite']); $ret = $this->obj->setSprite('foo'); @@ -60,7 +54,7 @@ public function testSetSprite() $this->obj->setSprite(false); } - public function testBuildChoices() + public function testBuildChoices(): void { $ret = $this->obj->buildChoicesFromSprite(); $this->assertEquals([], $ret); @@ -70,19 +64,19 @@ public function testBuildChoices() $this->assertEquals([], $ret); } - public function testSqlExtra() + public function testSqlExtra(): void { $this->assertEquals('', $this->obj->sqlExtra()); } - public function testSqlType() + public function testSqlType(): void { $this->assertEquals('VARCHAR(255)', $this->obj->sqlType()); $this->obj->setMultiple(true); $this->assertEquals('TEXT', $this->obj->sqlType()); } - public function testSqlPdoType() + public function testSqlPdoType(): void { $this->assertEquals(\PDO::PARAM_STR, $this->obj->sqlPdoType()); } diff --git a/packages/property/tests/Charcoal/Property/StorablePropertyTraitTest.php b/packages/property/tests/Charcoal/Property/StorablePropertyTraitTest.php index 4dcf5a96f..7d1489608 100644 --- a/packages/property/tests/Charcoal/Property/StorablePropertyTraitTest.php +++ b/packages/property/tests/Charcoal/Property/StorablePropertyTraitTest.php @@ -26,9 +26,6 @@ class StorablePropertyTraitTest extends AbstractTestCase */ private $obj; - /** - * @return void - */ protected function setUp(): void { $container = $this->getContainer(); @@ -73,10 +70,7 @@ public function createMultiValueProperty() return $prop; } - /** - * @return void - */ - public function testStorageVal() + public function testStorageVal(): void { $container = $this->getContainer(); @@ -97,10 +91,8 @@ public function testStorageVal() /** * Test Unilingual Property Fields - * - * @return void */ - public function testFields() + public function testFields(): void { $fields = $this->obj->fields('Cooking'); @@ -118,10 +110,7 @@ public function testFields() $this->assertEquals('[]', $field->val()); } - /** - * @return void - */ - public function testUpdateFields() + public function testUpdateFields(): void { $fields = $this->callMethod($this->obj, 'updatedFields', [ [], 'Cooking' ]); $this->assertEquals(['Cooking'], array_map(fn($field) => $field->val(), array_values($fields))); @@ -129,12 +118,10 @@ public function testUpdateFields() /** * Test Multilingual Property Fields - * - * @return void */ - public function testMultilingualFields() + public function testMultilingualFields(): void { - $container = $this->getContainer(); + $this->getContainer(); $obj = $this->createMultilingualProperty(); @@ -163,10 +150,8 @@ public function testMultilingualFields() /** * Test Unilingual Property Field Names - * - * @return void */ - public function testFieldNames() + public function testFieldNames(): void { $names = $this->obj->fieldNames(); @@ -181,10 +166,8 @@ public function testFieldNames() /** * Test Multilingual Property Field Names - * - * @return void */ - public function testMultilingualFieldNames() + public function testMultilingualFieldNames(): void { $obj = $this->createMultilingualProperty(); diff --git a/packages/property/tests/Charcoal/Property/StringPropertyTest.php b/packages/property/tests/Charcoal/Property/StringPropertyTest.php index 18bcc58ce..27c8c6879 100644 --- a/packages/property/tests/Charcoal/Property/StringPropertyTest.php +++ b/packages/property/tests/Charcoal/Property/StringPropertyTest.php @@ -27,8 +27,6 @@ class StringPropertyTest extends AbstractTestCase /** * Set up the test. - * - * @return void */ protected function setUp(): void { @@ -43,15 +41,12 @@ protected function setUp(): void ]); } - /** - * @return void - */ - public function testType() + public function testType(): void { $this->assertEquals('string', $this->obj->type()); } - public function testDefaults() + public function testDefaults(): void { $this->assertFalse($this->obj['required']); $this->assertFalse($this->obj['unique']); @@ -63,18 +58,12 @@ public function testDefaults() $this->assertTrue($this->obj['active']); } - /** - * @return void - */ - public function testSqlExtra() + public function testSqlExtra(): void { $this->assertEquals('', $this->obj->sqlExtra()); } - /** - * @return void - */ - public function testSqlType() + public function testSqlType(): void { $this->obj->setMultiple(false); $this->assertEquals('VARCHAR(255)', $this->obj->sqlType()); @@ -89,23 +78,17 @@ public function testSqlType() $this->assertEquals('TEXT', $this->obj->sqlType()); } - /** - * @return void - */ - public function testSqlPdoType() + public function testSqlPdoType(): void { $this->assertEquals(PDO::PARAM_STR, $this->obj->sqlPdoType()); } - /** - * @return void - */ - public function testSetData() + public function testSetData(): void { $data = [ 'min_length' => 5, 'max_length' => 42, - 'regexp' => '/[0-9]*/', + 'regexp' => '/\d*/', 'allow_empty' => false, 'allow_html' => true ]; @@ -115,15 +98,12 @@ public function testSetData() $this->assertEquals(5, $this->obj['minLength']); $this->assertEquals(42, $this->obj['maxLength']); - $this->assertEquals('/[0-9]*/', $this->obj['regexp']); + $this->assertEquals('/\d*/', $this->obj['regexp']); $this->assertFalse($this->obj['allowEmpty']); $this->assertTrue($this->obj['allowHtml']); } - /** - * @return void - */ - public function testDisplayVal() + public function testDisplayVal(): void { $container = $this->getContainer(); $translator = $container['translator']; @@ -157,10 +137,7 @@ public function testDisplayVal() $this->assertEquals('foo, bar, baz', $this->obj->displayVal([ 'foo', 'bar', 'baz' ])); } - /** - * @return void - */ - public function testDisplayChoices() + public function testDisplayChoices(): void { $choices = $this->getDisplayChoices(); $this->obj->setChoices($choices); @@ -187,9 +164,8 @@ public function testDisplayChoices() /** * @used-by testDisplayChoices() * @used-by testRenderedDisplayChoices() - * @return array */ - public function getDisplayChoices() + public function getDisplayChoices(): array { $container = $this->getContainer(); $translator = $container['translator']; @@ -213,14 +189,13 @@ public function getDisplayChoices() } /** - * @dataProvider getDisplayChoicesProvider * * @param string $expected The displayed $value. * @param mixed $value The value to display. * @param array $options The display options. - * @return void */ - public function testRenderedDisplayChoices($expected, $value, array $options = []) + #[\PHPUnit\Framework\Attributes\DataProvider('getDisplayChoicesProvider')] + public function testRenderedDisplayChoices(string $expected, string|array $value, array $options = []): void { $this->obj->setChoices($this->getDisplayChoices()); $this->obj->setL10n(false); @@ -231,9 +206,8 @@ public function testRenderedDisplayChoices($expected, $value, array $options = [ /** * @used-by testRenderedDisplayChoices() - * @return array */ - public function getDisplayChoicesProvider() + public static function getDisplayChoicesProvider(): array { return [ [ 'Brown fox, Lazy dog, wolf', [ 'fox', 'dog', 'wolf' ] ], @@ -243,10 +217,7 @@ public function getDisplayChoicesProvider() ]; } - /** - * @return void - */ - public function testSetMinLength() + public function testSetMinLength(): void { $ret = $this->obj->setMinLength(5); $this->assertSame($ret, $this->obj); @@ -262,19 +233,13 @@ public function testSetMinLength() $this->obj->setMinLength('foo'); } - /** - * @return void - */ - public function testSetMinLenghtNegativeThrowsException() + public function testSetMinLenghtNegativeThrowsException(): void { $this->expectException('\InvalidArgumentException'); $this->obj->setMinLength(-1); } - /** - * @return void - */ - public function testSetMaxLength() + public function testSetMaxLength(): void { $ret = $this->obj->setMaxLength(5); $this->assertSame($ret, $this->obj); @@ -290,19 +255,13 @@ public function testSetMaxLength() $this->obj->setMaxLength('foo'); } - /** - * @return void - */ - public function testSetMaxLenghtNegativeThrowsException() + public function testSetMaxLenghtNegativeThrowsException(): void { $this->expectException('\InvalidArgumentException'); $this->obj->setMaxLength(-1); } - /** - * @return void - */ - public function testSetRegexp() + public function testSetRegexp(): void { $ret = $this->obj->setRegexp('[a-z]'); $this->assertSame($ret, $this->obj); @@ -318,10 +277,7 @@ public function testSetRegexp() $this->obj->setRegexp(null); } - /** - * @return void - */ - public function testSetAllowEmpty() + public function testSetAllowEmpty(): void { $this->assertEquals(true, $this->obj['allowEmpty']); @@ -336,10 +292,7 @@ public function testSetAllowEmpty() $this->assertFalse($this->obj['allow_empty']); } - /** - * @return void - */ - public function testLength() + public function testLength(): void { $this->obj->setVal('foo'); $this->assertEquals(3, $this->obj->length()); @@ -357,7 +310,7 @@ public function testLength() $this->assertEquals(13, $this->obj->length()); } - public function testParseOne() + public function testParseOne(): void { $this->obj->setAllowHtml(false); $ret = $this->obj->parseOne('

with html

'); @@ -368,18 +321,12 @@ public function testParseOne() $this->assertEquals('

with html

', $ret); } - /** - * @return void - */ - public function testValidationMethods() + public function testValidationMethods(): void { $this->assertIsArray($this->obj->validationMethods()); } - /** - * @return void - */ - public function testValidateMaxLength() + public function testValidateMaxLength(): void { $this->obj->setMaxLength(5); $this->obj->setVal('1234'); @@ -407,10 +354,7 @@ public function testValidateMaxLength() $this->assertNotTrue($this->obj->validateMaxLength()); } - /** - * @return void - */ - public function testValidateMaxLengthWithZeroMaxLengthReturnsTrue() + public function testValidateMaxLengthWithZeroMaxLengthReturnsTrue(): void { $this->obj->setMaxLength(0); @@ -420,10 +364,7 @@ public function testValidateMaxLengthWithZeroMaxLengthReturnsTrue() $this->assertTrue($this->obj->validateMaxLength()); } - /** - * @return void - */ - public function testValidateMinLength() + public function testValidateMinLength(): void { $this->obj->setMinLength(5); @@ -452,10 +393,7 @@ public function testValidateMinLength() $this->assertNotTrue($this->obj->validateMinLength()); } - /** - * @return void - */ - public function testValidateMinLengthAllowEmpty() + public function testValidateMinLengthAllowEmpty(): void { $this->obj->setAllowNull(false); $this->obj->setMinLength(5); @@ -468,10 +406,7 @@ public function testValidateMinLengthAllowEmpty() $this->assertNotTrue($this->obj->validateMinLength()); } - /** - * @return void - */ - public function testValidateMinLengthWithoutValReturnsFalse() + public function testValidateMinLengthWithoutValReturnsFalse(): void { $this->obj->setAllowNull(false); $this->obj->setMinLength(5); @@ -479,10 +414,7 @@ public function testValidateMinLengthWithoutValReturnsFalse() $this->assertNotTrue($this->obj->validateMinLength()); } - /** - * @return void - */ - public function testValidateMinLengthWithoutMinLengthReturnsTrue() + public function testValidateMinLengthWithoutMinLengthReturnsTrue(): void { $this->assertTrue($this->obj->validateMinLength()); @@ -490,10 +422,7 @@ public function testValidateMinLengthWithoutMinLengthReturnsTrue() $this->assertTrue($this->obj->validateMinLength()); } - /** - * @return void - */ - public function testValidateRegexp() + public function testValidateRegexp(): void { /** Without RegExp */ $this->assertTrue($this->obj->validateRegexp()); @@ -502,7 +431,7 @@ public function testValidateRegexp() $this->assertTrue($this->obj->validateRegexp()); /** With RegExp */ - $this->obj->setRegexp('/[0-9]+/'); + $this->obj->setRegexp('/\d+/'); $this->obj->setVal('123'); $this->assertTrue($this->obj->validateRegexp()); @@ -511,10 +440,7 @@ public function testValidateRegexp() $this->assertNotTrue($this->obj->validateRegexp()); } - /** - * @return void - */ - public function testValidateAllowEmpty() + public function testValidateAllowEmpty(): void { $this->obj->setAllowEmpty(false); diff --git a/packages/property/tests/Charcoal/Property/StructurePropertyTest.php b/packages/property/tests/Charcoal/Property/StructurePropertyTest.php index f103fb186..219688a67 100644 --- a/packages/property/tests/Charcoal/Property/StructurePropertyTest.php +++ b/packages/property/tests/Charcoal/Property/StructurePropertyTest.php @@ -21,9 +21,6 @@ class StructurePropertyTest extends AbstractTestCase */ public $obj; - /** - * @return void - */ protected function setUp(): void { $container = $this->getContainer(); @@ -35,15 +32,12 @@ protected function setUp(): void ]); } - /** - * @return void - */ - public function testType() + public function testType(): void { $this->assertEquals('structure', $this->obj->type()); } - public function testParseOneNull() + public function testParseOneNull(): void { $this->obj->setAllowNull(true); $this->assertNull($this->obj->parseOne(null)); @@ -53,7 +47,7 @@ public function testParseOneNull() $this->obj->parseOne(null); } - public function testParseOneString() + public function testParseOneString(): void { $this->assertEquals('', $this->obj->parseOne('')); // $this->assertEquals('foo', $this->obj->parseOne('foo')); @@ -61,7 +55,7 @@ public function testParseOneString() $this->assertEquals(['foo'=>'bar'], $this->obj->parseOne('{"foo":"bar"}')); } - public function testSqlType() + public function testSqlType(): void { $this->assertEquals('TEXT', $this->obj->sqlType()); @@ -85,23 +79,23 @@ public function testSqlType() $this->obj->setSqlType('foobar'); } - public function testSetSqlTypeNullException() + public function testSetSqlTypeNullException(): void { $this->expectException(InvalidArgumentException::class); $this->obj->setSqlType(false); } - public function testSqlPdoType() + public function testSqlPdoType(): void { $this->assertEquals(\PDO::PARAM_STR, $this->obj->sqlPdoType()); } - public function testSqlExtra() + public function testSqlExtra(): void { $this->assertEquals('', $this->obj->sqlExtra()); } - public function testInputVal() + public function testInputVal(): void { $this->assertEquals('', $this->obj->inputVal('')); $this->assertEquals('', $this->obj->inputVal(null)); @@ -109,7 +103,7 @@ public function testInputVal() $this->assertEquals('[]', $this->obj->inputVal([])); } - public function testStorageVal() + public function testStorageVal(): void { $this->assertEquals('', $this->obj->inputVal('')); $this->assertEquals(null, $this->obj->inputVal(null)); diff --git a/packages/property/tests/Charcoal/Property/TextPropertyTest.php b/packages/property/tests/Charcoal/Property/TextPropertyTest.php index 8be63c1f1..624feab63 100644 --- a/packages/property/tests/Charcoal/Property/TextPropertyTest.php +++ b/packages/property/tests/Charcoal/Property/TextPropertyTest.php @@ -1,5 +1,7 @@ getContainer(); @@ -34,15 +33,13 @@ protected function setUp(): void /** * Asserts that the `type()` method returns "text". - * - * @return void */ - public function testType() + public function testType(): void { $this->assertEquals('text', $this->obj->type()); } - public function testDefaults() + public function testDefaults(): void { $this->assertFalse($this->obj['required']); $this->assertFalse($this->obj['unique']); @@ -57,20 +54,16 @@ public function testDefaults() /** * Asserts that the `defaultMaxLength` method returns 0 (no limit). - * - * @return void */ - public function testDefaultMaxLength() + public function testDefaultMaxLength(): void { $this->assertEquals(0, $this->obj->defaultMaxLength()); } /** * Asserts that the `sqlType()` method returns "TEXT". - * - * @return void */ - public function testSqlType() + public function testSqlType(): void { $this->assertEquals('TEXT', $this->obj->sqlType()); diff --git a/packages/property/tests/Charcoal/Property/UrlPropertyTest.php b/packages/property/tests/Charcoal/Property/UrlPropertyTest.php index b7b0a5242..f71c77a3a 100644 --- a/packages/property/tests/Charcoal/Property/UrlPropertyTest.php +++ b/packages/property/tests/Charcoal/Property/UrlPropertyTest.php @@ -1,5 +1,7 @@ getContainer(); @@ -33,15 +32,13 @@ protected function setUp(): void /** * Asserts that the `type()` method returns "url". - * - * @return void */ - public function testType() + public function testType(): void { $this->assertEquals('url', $this->obj->type()); } - public function testParseOne() + public function testParseOne(): void { $this->assertEquals('example.com', $this->obj->parseOne('example.com')); $this->assertEquals('https://example.com:2020', $this->obj->parseOne('https:// example.com:2020 ')); diff --git a/packages/property/tests/Charcoal/ReflectionsTrait.php b/packages/property/tests/Charcoal/ReflectionsTrait.php index 3a63ff2b1..047d2c172 100644 --- a/packages/property/tests/Charcoal/ReflectionsTrait.php +++ b/packages/property/tests/Charcoal/ReflectionsTrait.php @@ -18,13 +18,10 @@ trait ReflectionsTrait * * @param mixed $class The class name or object that contains the method. * @param string $name The method name to reflect. - * @return ReflectionMethod */ - public function getMethod($class, $name) + public function getMethod($class, $name): \ReflectionMethod { - $reflected = new ReflectionMethod($class, $name); - $reflected->setAccessible(true); - return $reflected; + return new ReflectionMethod($class, $name); } /** @@ -38,7 +35,7 @@ public function getMethod($class, $name) public function callMethod($object, $name, array $args = []) { $method = $this->getMethod($object, $name); - if (empty($args)) { + if ($args === []) { return $method->invoke($object); } else { return $method->invokeArgs($object, $args); @@ -65,13 +62,10 @@ public function callMethodWith($object, $name, ...$args) * * @param mixed $class The class name or object that contains the property. * @param string $name The property name to reflect. - * @return ReflectionProperty */ - public function getProperty($class, $name) + public function getProperty($class, $name): \ReflectionProperty { - $reflected = new ReflectionProperty($class, $name); - $reflected->setAccessible(true); - return $reflected; + return new ReflectionProperty($class, $name); } /** @@ -92,9 +86,8 @@ public function getPropertyValue($object, $name) * @param mixed $object The object to access. * @param string $name The property name to affect. * @param mixed $value The new value. - * @return void */ - public function setPropertyValue($object, $name, $value) + public function setPropertyValue($object, $name, $value): void { $this->getProperty($object, $name)->setValue($object, $value); } diff --git a/packages/queue/composer.json b/packages/queue/composer.json index 470c1e8df..ebb9e3d75 100644 --- a/packages/queue/composer.json +++ b/packages/queue/composer.json @@ -1,8 +1,10 @@ { - "type": "library", "name": "charcoal/queue", "description": "Queue, Queue items and Queueable objects for Charcoal", - "keywords": ["charcoal", "queue"], + "keywords": [ + "charcoal", + "queue" + ], "homepage": "https://charcoal.locomotive.ca", "license": "MIT", "authors": [ @@ -15,19 +17,15 @@ "homepage": "https://locomotive.ca" } ], - "extra": { - "branch-alias": { - "dev-main": "5.x-dev" - } - }, + "type": "library", "require": { - "php": "^7.4 || ^8.0", + "php": "^8.3", "psr/log": "^1.0", "charcoal/core": "^5.1", "charcoal/factory": "^5.1" }, "require-dev": { - "phpunit/phpunit": "^9.5", + "phpunit/phpunit": "^12.5", "squizlabs/php_codesniffer": "^3.5", "php-coveralls/php-coveralls": "^2.2" }, @@ -41,8 +39,10 @@ "Charcoal\\Tests\\": "tests/Charcoal/" } }, - "replace": { - "locomotivemtl/charcoal-queue": "*" + "extra": { + "branch-alias": { + "dev-main": "5.x-dev" + } }, "scripts": { "test": [ @@ -58,6 +58,9 @@ "phpcbf": "php vendor/bin/phpcbf -ps --colors src/ tests/", "phpunit": "php vendor/bin/phpunit --coverage-text" }, + "replace": { + "locomotivemtl/charcoal-queue": "*" + }, "minimum-stability": "dev", "prefer-stable": true } diff --git a/packages/queue/src/Charcoal/Queue/AbstractQueueManager.php b/packages/queue/src/Charcoal/Queue/AbstractQueueManager.php index cf51036a4..bb8baab6d 100644 --- a/packages/queue/src/Charcoal/Queue/AbstractQueueManager.php +++ b/packages/queue/src/Charcoal/Queue/AbstractQueueManager.php @@ -47,24 +47,18 @@ abstract class AbstractQueueManager implements /** * The queue processing rate (throttle), in items per second. - * - * @var integer */ - private $rate = 0; + private int $rate = 0; /** * The batch limit. - * - * @var integer */ - private $limit = 0; + private int $limit = 0; /** * The chunk size to batch the queue with. - * - * @var integer */ - private $chunkSize = 0; + private int $chunkSize = 0; /** * The queue ID. @@ -125,10 +119,7 @@ abstract class AbstractQueueManager implements */ private $processedCallback; - /** - * @var FactoryInterface $queueItemFactory - */ - private $queueItemFactory; + private \Charcoal\Factory\FactoryInterface $queueItemFactory; /** * Construct new queue manager. @@ -302,7 +293,7 @@ public function setProcessedCallback(callable $callback) * after all queue items are processed. * @return boolean Success / Failure */ - public function processQueue(callable $callback = null) + public function processQueue(?callable $callback = null) { if (!is_callable($callback)) { $callback = $this->processedCallback; @@ -337,14 +328,14 @@ public function processQueue(callable $callback = null) $queueId, $summary ), [ - 'manager' => get_called_class(), + 'manager' => static::class, ]); } else { $this->logger->notice(sprintf( 'Completed processing of queues: %s', $summary ), [ - 'manager' => get_called_class(), + 'manager' => static::class, ]); } @@ -353,9 +344,8 @@ public function processQueue(callable $callback = null) /** * @param mixed $queuedItems The items to process. - * @return void */ - private function processItems($queuedItems) + private function processItems($queuedItems): void { /** @var QueueItemInterface $q */ foreach ($queuedItems as $q) { @@ -390,7 +380,7 @@ private function processItems($queuedItems) $this->logger->error( sprintf('Could not process a queue item: %s', $e->getMessage()), [ - 'manager' => get_called_class(), + 'manager' => static::class, 'queueId' => $q['queueId'], 'itemId' => $q['id'], ] @@ -405,10 +395,8 @@ private function processItems($queuedItems) /** * Throttle processing of items. - * - * @return void */ - private function throttle() + private function throttle(): void { if ($this->rate > 0) { usleep(1000000 / $this->rate); @@ -422,13 +410,11 @@ private function throttle() */ public function createQueueItemsLoader() { - $loader = new CollectionLoader([ + return new CollectionLoader([ 'logger' => $this->logger, 'factory' => $this->queueItemFactory(), 'model' => $this->queueItemProto(), ]); - - return $loader; } /** @@ -544,9 +530,8 @@ protected function queueItemFactory() /** * @param FactoryInterface $factory The factory used to create queue items. - * @return self */ - private function setQueueItemFactory(FactoryInterface $factory) + private function setQueueItemFactory(FactoryInterface $factory): static { $this->queueItemFactory = $factory; return $this; diff --git a/packages/queue/src/Charcoal/Queue/QueueItemInterface.php b/packages/queue/src/Charcoal/Queue/QueueItemInterface.php index 37c2c587e..1474277f4 100644 --- a/packages/queue/src/Charcoal/Queue/QueueItemInterface.php +++ b/packages/queue/src/Charcoal/Queue/QueueItemInterface.php @@ -1,5 +1,7 @@ logger->error( sprintf('Could not process a queue item: %s', $e->getMessage()), [ - 'manager' => get_called_class(), + 'manager' => static::class, 'queueId' => $this->queueId(), 'itemId' => $this->id(), ] @@ -163,7 +163,7 @@ public function queueId() */ public function setProcessed($processed) { - $this->processed = !!$processed; + $this->processed = (bool) $processed; return $this; } @@ -195,9 +195,7 @@ public function setQueuedDate($ts) try { $ts = new DateTime($ts); } catch (Exception $e) { - throw new InvalidArgumentException( - sprintf('Can not set queued date: %s', $e->getMessage()) - ); + throw new InvalidArgumentException(sprintf('Can not set queued date: %s', $e->getMessage()), $e->getCode(), $e); } } @@ -240,9 +238,7 @@ public function setProcessingDate($ts) try { $ts = new DateTime($ts); } catch (Exception $e) { - throw new InvalidArgumentException( - sprintf('%s (%s)', $e->getMessage(), $ts) - ); + throw new InvalidArgumentException(sprintf('%s (%s)', $e->getMessage(), $ts), $e->getCode(), $e); } } @@ -285,9 +281,7 @@ public function setProcessedDate($ts) try { $ts = new DateTime($ts); } catch (Exception $e) { - throw new InvalidArgumentException( - sprintf('%s (%s)', $e->getMessage(), $ts) - ); + throw new InvalidArgumentException(sprintf('%s (%s)', $e->getMessage(), $ts), $e->getCode(), $e); } } @@ -340,9 +334,7 @@ public function setExpiryDate($ts) try { $ts = new DateTime($ts); } catch (Exception $e) { - throw new InvalidArgumentException( - sprintf('%s (%s)', $e->getMessage(), $ts) - ); + throw new InvalidArgumentException(sprintf('%s (%s)', $e->getMessage(), $ts), $e->getCode(), $e); } } diff --git a/packages/queue/src/Charcoal/Queue/QueueManagerInterface.php b/packages/queue/src/Charcoal/Queue/QueueManagerInterface.php index 8bc749d53..bf1876f90 100644 --- a/packages/queue/src/Charcoal/Queue/QueueManagerInterface.php +++ b/packages/queue/src/Charcoal/Queue/QueueManagerInterface.php @@ -1,5 +1,7 @@ [ @@ -57,7 +45,7 @@ public function defaults() * @param array $languages The languages configuration. * @return LocalesConfig Chainable */ - public function setLanguages(array $languages) + public function setLanguages(array $languages): static { $this->languages = $languages; return $this; @@ -66,7 +54,7 @@ public function setLanguages(array $languages) /** * @return array */ - public function languages() + public function languages(): ?array { return $this->languages; } @@ -76,7 +64,7 @@ public function languages() * @throws InvalidArgumentException If the default language is not a string. * @return LocalesConfig Chainable */ - public function setDefaultLanguage($lang) + public function setDefaultLanguage($lang): static { if (!is_string($lang)) { throw new InvalidArgumentException( @@ -90,7 +78,7 @@ public function setDefaultLanguage($lang) /** * @return string */ - public function defaultLanguage() + public function defaultLanguage(): ?string { return $this->defaultLanguage; } @@ -99,7 +87,7 @@ public function defaultLanguage() * @param array $languages The fallback languages, used when a translation is not set in a language. * @return LocalesConfig Chainable */ - public function setFallbackLanguages(array $languages) + public function setFallbackLanguages(array $languages): static { $this->fallbackLanguages = $languages; return $this; @@ -108,7 +96,7 @@ public function setFallbackLanguages(array $languages) /** * @return array */ - public function fallbackLanguages() + public function fallbackLanguages(): ?array { return $this->fallbackLanguages; } @@ -117,16 +105,16 @@ public function fallbackLanguages() * @param boolean $autoDetect The auto-detect flag. * @return LocalesConfig Chainable */ - public function setAutoDetect($autoDetect) + public function setAutoDetect($autoDetect): static { - $this->autoDetect = !!$autoDetect; + $this->autoDetect = (bool) $autoDetect; return $this; } /** * @return boolean */ - public function autoDetect() + public function autoDetect(): ?bool { return $this->autoDetect; } diff --git a/packages/translator/src/Charcoal/Translator/LocalesManager.php b/packages/translator/src/Charcoal/Translator/LocalesManager.php index 235f5e763..e28946d74 100644 --- a/packages/translator/src/Charcoal/Translator/LocalesManager.php +++ b/packages/translator/src/Charcoal/Translator/LocalesManager.php @@ -31,7 +31,7 @@ class LocalesManager * * @var string[] */ - private $languages; + private array $languages; /** * Language code for the default locale. @@ -66,10 +66,10 @@ public function __construct(array $data) { $this->setLocales($data['locales']); - $default = isset($data['default_language']) ? $data['default_language'] : null; + $default = $data['default_language'] ?? null; $this->setDefaultLocale($default); - $current = isset($data['current_language']) ? $data['current_language'] : null; + $current = $data['current_language'] ?? null; $this->setCurrentLocale($current); } @@ -88,7 +88,7 @@ public function locales() * * @return string[] */ - public function availableLocales() + public function availableLocales(): array { return $this->languages; } @@ -99,9 +99,8 @@ public function availableLocales() * @param string|null $lang The default language code. * If NULL, the first language is assigned. * @throws InvalidArgumentException If the language is invalid. - * @return void */ - private function setDefaultLocale($lang) + private function setDefaultLocale($lang): void { if ($lang === null) { $this->defaultLanguage = $this->languages[0]; @@ -110,7 +109,7 @@ private function setDefaultLocale($lang) if (!$this->hasLocale($lang)) { if (!is_string($lang)) { - $lang = is_object($lang) ? get_class($lang) : gettype($lang); + $lang = get_debug_type($lang); } throw new InvalidArgumentException(sprintf( @@ -139,9 +138,8 @@ public function defaultLocale() * @param string|null $lang The current language code. * If NULL, the current language is unset. * @throws InvalidArgumentException If the language is invalid. - * @return void */ - public function setCurrentLocale($lang) + public function setCurrentLocale($lang): void { if ($lang === null) { $this->currentLanguage = null; @@ -150,7 +148,7 @@ public function setCurrentLocale($lang) if (!$this->hasLocale($lang)) { if (!is_string($lang)) { - $lang = is_object($lang) ? get_class($lang) : gettype($lang); + $lang = get_debug_type($lang); } throw new InvalidArgumentException(sprintf( @@ -180,9 +178,8 @@ public function currentLocale() * Determine if a locale is available. * * @param string $lang The language code to check. - * @return boolean */ - public function hasLocale($lang) + public function hasLocale($lang): bool { return isset($this->locales[$lang]); } @@ -197,12 +194,11 @@ public function hasLocale($lang) * * @param array $locales The locales configuration structure. * @throws InvalidArgumentException If there are no active locales. - * @return void */ - private function setLocales(array $locales) + private function setLocales(array $locales): void { $locales = $this->filterLocales($locales); - uasort($locales, [ $this, 'sortLocalesByPriority' ]); + uasort($locales, $this->sortLocalesByPriority(...)); $this->locales = []; $this->languages = []; @@ -211,7 +207,7 @@ private function setLocales(array $locales) $this->languages[] = $langCode; } - if (empty($this->locales)) { + if ($this->locales === []) { throw new InvalidArgumentException( 'Locales can not be empty.' ); @@ -229,7 +225,7 @@ private function setLocales(array $locales) * @param array $locales The locales configuration structure. * @return array The parsed language structures. */ - private function filterLocales(array $locales) + private function filterLocales(array $locales): array { $z = self::DEFAULT_SORT_PRIORITY; @@ -258,16 +254,11 @@ private function filterLocales(array $locales) * * @param array $a Sortable action A. * @param array $b Sortable action B. - * @return integer */ - private function sortLocalesByPriority(array $a, array $b) + private function sortLocalesByPriority(array $a, array $b): int { - $a = isset($a['priority']) ? $a['priority'] : 0; - $b = isset($b['priority']) ? $b['priority'] : 0; - - if ($a === $b) { - return 0; - } - return ($a < $b) ? (-1) : 1; + $a = $a['priority'] ?? 0; + $b = $b['priority'] ?? 0; + return $a <=> $b; } } diff --git a/packages/translator/src/Charcoal/Translator/Middleware/LanguageMiddleware.php b/packages/translator/src/Charcoal/Translator/Middleware/LanguageMiddleware.php index 388157429..3c489795a 100644 --- a/packages/translator/src/Charcoal/Translator/Middleware/LanguageMiddleware.php +++ b/packages/translator/src/Charcoal/Translator/Middleware/LanguageMiddleware.php @@ -29,60 +29,36 @@ class LanguageMiddleware */ private $browserLanguage; - /** - * @var array - */ - private $excludedPath; + private array $excludedPath; - /** - * @var boolean - */ - private $usePath; + private bool $usePath; /** * @var string */ private $pathRegexp; - /** - * @var boolean - */ - private $useBrowser; + private bool $useBrowser; - /** - * @var boolean - */ - private $useSession; + private bool $useSession; /** * @var string[] */ - private $sessionKey; + private array $sessionKey; - /** - * @var boolean - */ - private $useParams; + private bool $useParams; /** * @var string[] */ - private $paramKey; + private array $paramKey; - /** - * @var boolean - */ - private $useHost; + private bool $useHost; - /** - * @var array - */ - private $hostMap; + private array $hostMap; - /** - * @var boolean - */ - private $setLocale; + private bool $setLocale; /** * @param array $data The middleware options. @@ -96,30 +72,28 @@ public function __construct(array $data) $this->defaultLanguage = $data['default_language']; $this->browserLanguage = $data['browser_language']; - $this->usePath = !!$data['use_path']; + $this->usePath = (bool) $data['use_path']; $this->excludedPath = (array)$data['excluded_path']; $this->pathRegexp = $data['path_regexp']; - $this->useParams = !!$data['use_params']; + $this->useParams = (bool) $data['use_params']; $this->paramKey = (array)$data['param_key']; - $this->useSession = !!$data['use_session']; + $this->useSession = (bool) $data['use_session']; $this->sessionKey = (array)$data['session_key']; - $this->useBrowser = !!$data['use_browser']; + $this->useBrowser = (bool) $data['use_browser']; - $this->useHost = !!$data['use_host']; + $this->useHost = (bool) $data['use_host']; $this->hostMap = (array)$data['host_map']; - $this->setLocale = !!$data['set_locale']; + $this->setLocale = (bool) $data['set_locale']; } /** * Default middleware options. - * - * @return array */ - public function defaults() + public function defaults(): array { return [ 'default_language' => null, @@ -173,35 +147,35 @@ public function __invoke(RequestInterface $request, ResponseInterface $response, */ private function getLanguage(RequestInterface $request) { - if ($this->useHost === true) { + if ($this->useHost) { $lang = $this->getLanguageFromHost($request); if ($lang) { return $lang; } } - if ($this->usePath === true) { + if ($this->usePath) { $lang = $this->getLanguageFromPath($request); - if ($lang) { + if ($lang !== '' && $lang !== '0') { return $lang; } } - if ($this->useParams === true) { + if ($this->useParams) { $lang = $this->getLanguageFromParams($request); if ($lang) { return $lang; } } - if ($this->useSession === true) { + if ($this->useSession) { $lang = $this->getLanguageFromSession(); if ($lang) { return $lang; } } - if ($this->useBrowser === true) { + if ($this->useBrowser) { $lang = $this->getLanguageFromBrowser(); if ($lang) { return $lang; @@ -215,11 +189,11 @@ private function getLanguage(RequestInterface $request) * @param RequestInterface $request The PSR-7 HTTP request. * @return string */ - private function getLanguageFromHost(RequestInterface $request) + private function getLanguageFromHost(RequestInterface $request): int|string { $uriHost = $request->getUri()->getHost(); foreach ($this->hostMap as $lang => $host) { - if (stripos($uriHost, $host) !== false) { + if (stripos($uriHost, (string) $host) !== false) { return $lang; } } @@ -229,9 +203,8 @@ private function getLanguageFromHost(RequestInterface $request) /** * @param RequestInterface $request The PSR-7 HTTP request. - * @return string */ - private function getLanguageFromPath(RequestInterface $request) + private function getLanguageFromPath(RequestInterface $request): string { $path = $request->getRequestTarget(); if (preg_match('@' . $this->pathRegexp . '@', $path, $matches)) { @@ -291,28 +264,26 @@ private function getLanguageFromBrowser() /** * @param string $lang The language code to set. - * @return void */ - private function setLanguage($lang) + private function setLanguage($lang): void { $this->translator()->setLocale($lang); - if ($this->useSession === true) { + if ($this->useSession) { foreach ($this->sessionKey as $key) { $_SESSION[$key] = $this->translator()->getLocale(); } } - if ($this->setLocale === true) { + if ($this->setLocale) { $this->setLocale($lang); } } /** * @param string $lang The language code to set. - * @return void */ - private function setLocale($lang) + private function setLocale($lang): void { $translator = $this->translator(); $available = $translator->locales(); @@ -329,14 +300,14 @@ private function setLocale($lang) $choices = (array)$locale['locales']; array_push($locales, ...$choices); } elseif (isset($locale['locale'])) { - array_push($locales, $locale['locale']); + $locales[] = $locale['locale']; } } } $locales = array_unique($locales); - if (!empty($locales)) { + if ($locales !== []) { setlocale(LC_ALL, $locales); } } diff --git a/packages/translator/src/Charcoal/Translator/Script/TranslationParserScript.php b/packages/translator/src/Charcoal/Translator/Script/TranslationParserScript.php index b671ffdf0..bfcaf829b 100644 --- a/packages/translator/src/Charcoal/Translator/Script/TranslationParserScript.php +++ b/packages/translator/src/Charcoal/Translator/Script/TranslationParserScript.php @@ -66,9 +66,9 @@ class TranslationParserScript extends AdminScript /** * @param Container $container Pimple DI container. - * @return void */ - public function setDependencies(Container $container) + #[\Override] + public function setDependencies(Container $container): void { $this->appConfig = $container['config']; $this->setTranslator($container['translator']); @@ -89,9 +89,9 @@ public function setDependencies(Container $container) * - type : file type (either mustache or php) * * @todo Support php file type. - * @return array */ - public function defaultArguments() + #[\Override] + public function defaultArguments(): array { $arguments = [ 'output' => [ @@ -135,17 +135,14 @@ public function defaultArguments() 'defaultValue' => '_t' ] ]; - - $arguments = array_merge(parent::defaultArguments(), $arguments); - return $arguments; + return array_merge(parent::defaultArguments(), $arguments); } /** * @param RequestInterface $request A PSR-7 compatible Request instance. * @param ResponseInterface $response A PSR-7 compatible Response instance. - * @return ResponseInterface */ - public function run(RequestInterface $request, ResponseInterface $response) + public function run(RequestInterface $request, ResponseInterface $response): ResponseInterface { // Unused unset($request); @@ -173,13 +170,12 @@ public function run(RequestInterface $request, ResponseInterface $response) /** * @param array $trans The translations array. - * @return array */ - protected function parseTranslations(array $trans) + protected function parseTranslations(array $trans): array { // Must be the first occurrence of the the key. - foreach ($trans as $lang => &$value) { - array_walk($value, function (&$val, $key) { + foreach ($trans as &$value) { + array_walk($value, function (&$val, $key): void { // remove key template ident in translation value. if (preg_match('|^\[([^\]]*)\]|', $key, $translationContext)) { $val = str_replace($translationContext[0], '', $val); @@ -199,7 +195,7 @@ protected function parseTranslations(array $trans) * Give feedback about what's going on. * @return self Chainable. */ - protected function displayInformations() + protected function displayInformations(): static { $this->climate()->underline()->out( 'Initializing translations parser script...' @@ -244,7 +240,7 @@ protected function filePath() * Available locales (languages) * @return array Locales. */ - protected function locales() + protected function locales(): array { return $this->translator()->availableLocales(); } @@ -274,7 +270,7 @@ public function output() * Domain which is the csv file name prefix * @return string domain. */ - public function domain() + public function domain(): string { return (string)$this->argOrInput('domain'); } @@ -285,7 +281,7 @@ public function domain() * @param string $type File type (mustache|php). * @return string Regex string. */ - public function regEx($type) + public function regEx($type): string { switch ($type) { case 'php': @@ -326,15 +322,14 @@ public function regEx($type) * ] * @return array Translations. */ - public function getTranslations() + public function getTranslations(): array { $path = $this->path(); if ($path) { $this->climate()->green()->out('Parsing files in ' . $path . ''); $translations = $this->getTranslationsFromPath($path, 'mustache'); - $translations = array_replace($translations, $this->getTranslationsFromPath($path, 'php')); - return $translations; + return array_replace($translations, $this->getTranslationsFromPath($path, 'php')); } $paths = $this->paths(); @@ -355,7 +350,7 @@ public function getTranslations() * @param string $fileType The file extension|type. * @return array Translations. */ - public function getTranslationsFromPath($path, $fileType) + public function getTranslationsFromPath(string $path, string $fileType): array { // Remove vendor/charcoal/app $base = $this->appConfig->get('base_path'); @@ -411,7 +406,7 @@ public function getTranslationsFromPath($path, $fileType) * @return array * @see http://in.php.net/manual/en/function.glob.php#106595 */ - public function globRecursive($pattern, $flags = 0) + public function globRecursive($pattern, $flags = 0): array|false { // $max = $this->maxRecursiveLevel(); $i = 1; @@ -469,9 +464,8 @@ public function fileTypes() /** * @param array $translations The translations to save in CSV. - * @return self */ - public function toCSV(array $translations) + public function toCSV(array $translations): static { if (!count($translations)) { $this->climate()->error(' @@ -502,7 +496,7 @@ public function toCSV(array $translations) foreach ($trans as $key => $translation) { $data = [ $key, $translation ]; - fputcsv($file, $data, $separator, $enclosure); + fputcsv($file, $data, $separator, $enclosure, escape: '\\'); } fclose($file); } @@ -510,26 +504,17 @@ public function toCSV(array $translations) return $this; } - /** - * @return string - */ - public function enclosure() + public function enclosure(): string { return '"'; } - /** - * @return string - */ - public function separator() + public function separator(): string { return ';'; } - /** - * @return integer - */ - public function maxRecursiveLevel() + public function maxRecursiveLevel(): int { if ($this->climate()->arguments->defined('recursive')) { return (int)$this->climate()->arguments->get('recursive'); @@ -540,7 +525,7 @@ public function maxRecursiveLevel() /** * @return string Php function */ - private function phpFunction() + private function phpFunction(): string { if ($this->climate()->arguments->defined('php_function')) { return (string)$this->climate()->arguments->get('php_function'); @@ -552,7 +537,7 @@ private function phpFunction() /** * @return string Mustache tag */ - private function mustacheTag() + private function mustacheTag(): string { if ($this->climate()->arguments->defined('mustache_tag')) { return (string)$this->climate()->arguments->get('mustache_tag'); diff --git a/packages/translator/src/Charcoal/Translator/ServiceProvider/TranslatorServiceProvider.php b/packages/translator/src/Charcoal/Translator/ServiceProvider/TranslatorServiceProvider.php index 273601087..dd0afbc4e 100644 --- a/packages/translator/src/Charcoal/Translator/ServiceProvider/TranslatorServiceProvider.php +++ b/packages/translator/src/Charcoal/Translator/ServiceProvider/TranslatorServiceProvider.php @@ -37,9 +37,8 @@ class TranslatorServiceProvider implements ServiceProviderInterface { /** * @param Container $container Pimple DI container. - * @return void */ - public function register(Container $container) + public function register(Container $container): void { $this->registerLocales($container); $this->registerTranslator($container); @@ -48,9 +47,8 @@ public function register(Container $container) /** * @param Container $container Pimple DI container. - * @return void */ - private function registerLocales(Container $container) + private function registerLocales(Container $container): void { /** * Instance of the Locales Configset. @@ -58,9 +56,9 @@ private function registerLocales(Container $container) * @param Container $container Pimple DI container. * @return LocalesConfig */ - $container['locales/config'] = function (Container $container) { - $appConfig = isset($container['config']) ? $container['config'] : []; - $localesConfig = isset($appConfig['locales']) ? $appConfig['locales'] : null; + $container['locales/config'] = function (Container $container): \Charcoal\Translator\LocalesConfig { + $appConfig = $container['config'] ?? []; + $localesConfig = $appConfig['locales'] ?? null; return new LocalesConfig($localesConfig); }; @@ -72,10 +70,8 @@ private function registerLocales(Container $container) */ $container['locales/default-language'] = function (Container $container) { $localesConfig = $container['locales/config']; - if (isset($localesConfig['auto_detect']) && $localesConfig['auto_detect']) { - if ($container['locales/browser-language'] !== null) { - return $container['locales/browser-language']; - } + if (isset($localesConfig['auto_detect']) && $localesConfig['auto_detect'] && $container['locales/browser-language'] !== null) { + return $container['locales/browser-language']; } return $localesConfig['default_language']; }; @@ -93,7 +89,7 @@ private function registerLocales(Container $container) * @param Container $container Pimple DI container. * @return string|null */ - $container['locales/browser-language'] = function (Container $container) { + $container['locales/browser-language'] = function (Container $container): ?string { if (!isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { return null; } @@ -104,11 +100,9 @@ private function registerLocales(Container $container) * as the default language. */ $localesConfig = $container['locales/config']; - $supportedLocales = array_filter($localesConfig['languages'], function ($locale) { - return !(isset($locale['active']) && !$locale['active']); - }); + $supportedLocales = array_filter($localesConfig['languages'], fn(array $locale): bool => !(isset($locale['active']) && !$locale['active'])); - $acceptableLanguages = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']); + $acceptableLanguages = explode(',', (string) $_SERVER['HTTP_ACCEPT_LANGUAGE']); foreach ($acceptableLanguages as $acceptedLang) { $lang = explode(';', $acceptedLang); $lang = trim($lang[0]); @@ -161,7 +155,7 @@ private function registerLocales(Container $container) * @param Container $container Pimple DI container. * @return LocalesManager */ - $container['locales/manager'] = function (Container $container) { + $container['locales/manager'] = function (Container $container): \Charcoal\Translator\LocalesManager { $localesConfig = $container['locales/config']; return new LocalesManager([ 'locales' => $localesConfig['languages'], @@ -172,9 +166,8 @@ private function registerLocales(Container $container) /** * @param Container $container Pimple DI container. - * @return void */ - private function registerTranslator(Container $container) + private function registerTranslator(Container $container): void { /** * Instance of the Translator Configset. @@ -182,9 +175,9 @@ private function registerTranslator(Container $container) * @param Container $container Pimple DI container. * @return TranslatorConfig */ - $container['translator/config'] = function (Container $container) { - $appConfig = isset($container['config']) ? $container['config'] : []; - $transConfig = isset($appConfig['translator']) ? $appConfig['translator'] : null; + $container['translator/config'] = function (Container $container): \Charcoal\Translator\TranslatorConfig { + $appConfig = $container['config'] ?? []; + $transConfig = $appConfig['translator'] ?? null; if (isset($transConfig['paths'])) { $transConfig['paths'] = $appConfig->resolveValues($transConfig['paths']); @@ -198,7 +191,7 @@ private function registerTranslator(Container $container) $modules = $container['module/classes']; foreach ($modules as $module) { if (defined(sprintf('%s::APP_CONFIG', $module))) { - $configPath = ltrim($module::APP_CONFIG, '/'); + $configPath = ltrim((string) $module::APP_CONFIG, '/'); $configPath = $basePath . DIRECTORY_SEPARATOR . $configPath; $configData = $appConfig->loadFile($configPath); @@ -211,7 +204,7 @@ private function registerTranslator(Container $container) }; } - if ($extraPaths) { + if ($extraPaths !== []) { $transConfig->addPaths($extraPaths); } } @@ -235,9 +228,7 @@ private function registerTranslator(Container $container) * * @return MessageSelector */ - $container['translator/message-selector'] = function () { - return new MessageSelector(); - }; + $container['translator/message-selector'] = (fn(): \Symfony\Component\Translation\MessageSelector => new MessageSelector()); /** * Instance of the Message Formatter, that is used to format a localized message. @@ -245,9 +236,7 @@ private function registerTranslator(Container $container) * @param Container $container Pimple DI container. * @return MessageFormatter */ - $container['translator/message-formatter'] = function (Container $container) { - return new MessageFormatter($container['translator/message-selector']); - }; + $container['translator/message-formatter'] = (fn(Container $container): \Symfony\Component\Translation\Formatter\MessageFormatter => new MessageFormatter($container['translator/message-selector'])); /** * Instance of the Translator, that is used for translation. @@ -256,7 +245,7 @@ private function registerTranslator(Container $container) * @param Container $container Pimple DI container. * @return Translator */ - $container['translator'] = function (Container $container) { + $container['translator'] = function (Container $container): \Charcoal\Translator\Translator { $transConfig = $container['translator/config']; $translator = new Translator([ 'manager' => $container['locales/manager'], @@ -310,106 +299,80 @@ private function registerTranslator(Container $container) /** * @param Container $container Pimple DI container. - * @return void */ - private function registerTranslatorLoaders(Container $container) + private function registerTranslatorLoaders(Container $container): void { /** * @return ArrayLoader */ - $container['translator/loader/array'] = function () { - return new ArrayLoader(); - }; + $container['translator/loader/array'] = (fn(): \Symfony\Component\Translation\Loader\ArrayLoader => new ArrayLoader()); /** * @return CsvFileLoader */ - $container['translator/loader/file/csv'] = function () { - return new CsvFileLoader(); - }; + $container['translator/loader/file/csv'] = (fn(): \Symfony\Component\Translation\Loader\CsvFileLoader => new CsvFileLoader()); /** * @return IcuDatFileLoader */ - $container['translator/loader/file/dat'] = function () { - return new IcuDatFileLoader(); - }; + $container['translator/loader/file/dat'] = (fn(): \Symfony\Component\Translation\Loader\IcuDatFileLoader => new IcuDatFileLoader()); /** * @return IcuResFileLoader */ - $container['translator/loader/file/res'] = function () { - return new IcuResFileLoader(); - }; + $container['translator/loader/file/res'] = (fn(): \Symfony\Component\Translation\Loader\IcuResFileLoader => new IcuResFileLoader()); /** * @return IniFileLoader */ - $container['translator/loader/file/ini'] = function () { - return new IniFileLoader(); - }; + $container['translator/loader/file/ini'] = (fn(): \Symfony\Component\Translation\Loader\IniFileLoader => new IniFileLoader()); /** * @return JsonFileLoader */ - $container['translator/loader/file/json'] = function () { - return new JsonFileLoader(); - }; + $container['translator/loader/file/json'] = (fn(): \Symfony\Component\Translation\Loader\JsonFileLoader => new JsonFileLoader()); /** * @return MoFileLoader */ - $container['translator/loader/file/mo'] = function () { - return new MoFileLoader(); - }; + $container['translator/loader/file/mo'] = (fn(): \Symfony\Component\Translation\Loader\MoFileLoader => new MoFileLoader()); /** * @return PhpFileLoader */ - $container['translator/loader/file/php'] = function () { - return new PhpFileLoader(); - }; + $container['translator/loader/file/php'] = (fn(): \Symfony\Component\Translation\Loader\PhpFileLoader => new PhpFileLoader()); /** * @return PoFileLoader */ - $container['translator/loader/file/po'] = function () { - return new PoFileLoader(); - }; + $container['translator/loader/file/po'] = (fn(): \Symfony\Component\Translation\Loader\PoFileLoader => new PoFileLoader()); /** * @return QtFileLoader */ - $container['translator/loader/file/qt'] = function () { - return new QtFileLoader(); - }; + $container['translator/loader/file/qt'] = (fn(): \Symfony\Component\Translation\Loader\QtFileLoader => new QtFileLoader()); /** * @return XliffFileLoader */ - $container['translator/loader/file/xliff'] = function () { - return new XliffFileLoader(); - }; + $container['translator/loader/file/xliff'] = (fn(): \Symfony\Component\Translation\Loader\XliffFileLoader => new XliffFileLoader()); /** * @return YamlFileLoader */ - $container['translator/loader/file/yaml'] = function () { - return new YamlFileLoader(); - }; + $container['translator/loader/file/yaml'] = (fn(): \Symfony\Component\Translation\Loader\YamlFileLoader => new YamlFileLoader()); } /** * @param Container $container Pimple DI container. - * @return void */ - private function registerMiddleware(Container $container) + private function registerMiddleware(Container $container): void { /** * @param Container $container * @return LanguageMiddleware */ - $container['middlewares/charcoal/translator/middleware/language'] = function (Container $container) { + $container['middlewares/charcoal/translator/middleware/language'] = function (Container $container): \Charcoal\Translator\Middleware\LanguageMiddleware { $middlewareConfig = $container['config']['middlewares']['charcoal/translator/middleware/language']; $middlewareConfig = array_replace( [ diff --git a/packages/translator/src/Charcoal/Translator/TranslatableInterface.php b/packages/translator/src/Charcoal/Translator/TranslatableInterface.php index dd5fb0a69..16b840b78 100644 --- a/packages/translator/src/Charcoal/Translator/TranslatableInterface.php +++ b/packages/translator/src/Charcoal/Translator/TranslatableInterface.php @@ -1,5 +1,7 @@ translations; } - /** - * @return string - */ public function __toString(): string { return $this->toJson(); @@ -94,7 +91,6 @@ public function __toString(): string /** * @param integer $options From {@see \json_encode()} flags. - * @return string */ public function toJson(int $options = 0): string { @@ -128,10 +124,8 @@ public function map(callable $callback): self * This method is to maintain compatibility with {@see Translation}. * * @param (callable(mixed, string): mixed) $callback Function to apply to each value. - * @return self - * - * @deprecated Will be removed in future version in favor of keeping this class Immutable. */ + #[\Deprecated(message: 'Will be removed in future version in favor of keeping this class Immutable.')] public function each(callable $callback): self { foreach ($this->translations as $locale => $translation) { @@ -146,10 +140,8 @@ public function each(callable $callback): self * This method is to maintain compatibility with {@see Translation}. * * @param (callable(mixed): mixed) $callback Function to apply to each value. - * @return self - * - * @deprecated Will be removed in future version in favor of keeping this class Immutable. */ + #[\Deprecated(message: 'Will be removed in future version in favor of keeping this class Immutable.')] public function sanitize(callable $callback): self { foreach ($this->translations as $locale => $translation) { @@ -181,7 +173,6 @@ public function trans(TranslatorInterface $translator, ?string $locale = null) /** * @param string $offset The requested offset to test. - * @return boolean * @throws InvalidArgumentException If array key isn't a string. * @see ArrayAccess::offsetExists() */ @@ -190,7 +181,7 @@ public function offsetExists($offset): bool if (!is_string($offset)) { throw new InvalidArgumentException(sprintf( 'Invalid language; must be a string, received %s', - (is_object($offset) ? get_class($offset) : gettype($offset)) + (get_debug_type($offset)) )); } @@ -209,7 +200,7 @@ public function offsetGet($offset) if (!is_string($offset)) { throw new InvalidArgumentException(sprintf( 'Invalid language; must be a string, received %s', - (is_object($offset) ? get_class($offset) : gettype($offset)) + (get_debug_type($offset)) )); } @@ -226,23 +217,22 @@ public function offsetGet($offset) /** * @param string $offset The lang offset to set. * @param mixed $value The value to store. - * @return void * @throws InvalidArgumentException If array key isn't a string. * @see ArrayAccess::offsetSet() */ - public function offsetSet($offset, $value) + public function offsetSet($offset, $value): void { if (!is_string($offset)) { throw new InvalidArgumentException(sprintf( 'Invalid language; must be a string, received %s', - (is_object($offset) ? get_class($offset) : gettype($offset)) + (get_debug_type($offset)) )); } if (!is_string($value)) { throw new InvalidArgumentException(sprintf( 'Translation must be a string, received %s', - (is_object($value) ? get_class($value) : gettype($value)) + (get_debug_type($value)) )); } @@ -251,15 +241,14 @@ public function offsetSet($offset, $value) /** * @param string $offset The language offset to unset. - * @return void * @throws InvalidArgumentException If array key isn't a string. */ - public function offsetUnset($offset) + public function offsetUnset($offset): void { if (!is_string($offset)) { throw new InvalidArgumentException(sprintf( 'Invalid language; must be a string, received %s', - (is_object($offset) ? get_class($offset) : gettype($offset)) + (get_debug_type($offset)) )); } diff --git a/packages/translator/src/Charcoal/Translator/Translation.php b/packages/translator/src/Charcoal/Translator/Translation.php index 1cf59f6c7..4729039af 100644 --- a/packages/translator/src/Charcoal/Translator/Translation.php +++ b/packages/translator/src/Charcoal/Translator/Translation.php @@ -17,7 +17,8 @@ class Translation implements TranslatableInterface, ArrayAccess, - JsonSerializable + JsonSerializable, + \Stringable { /** * The object's translations. @@ -28,26 +29,18 @@ class Translation implements */ private $val = []; - /** - * @var LocalesManager - */ - private $manager; - /** * @param Translation|array|string $val The translation values. * @param LocalesManager $manager A LocalesManager instance. */ - public function __construct($val, LocalesManager $manager) + public function __construct($val, private readonly LocalesManager $manager) { - $this->manager = $manager; $this->setVal($val); } /** * Output the current language's value, when cast to string. - * - * @return string */ - public function __toString() + public function __toString(): string { $lang = $this->manager->currentLocale(); if (isset($this->val[$lang])) { @@ -78,7 +71,7 @@ public function offsetExists($lang) if (!is_string($lang)) { throw new InvalidArgumentException(sprintf( 'Invalid language; must be a string, received %s', - (is_object($lang) ? get_class($lang) : gettype($lang)) + (get_debug_type($lang)) )); } @@ -97,7 +90,7 @@ public function offsetGet($lang) if (!is_string($lang)) { throw new InvalidArgumentException(sprintf( 'Invalid language; must be a string, received %s', - (is_object($lang) ? get_class($lang) : gettype($lang)) + (get_debug_type($lang)) )); } @@ -114,23 +107,22 @@ public function offsetGet($lang) /** * @param string $lang A language identifier. * @param string $val A translation value. - * @return void * @see ArrayAccess::offsetSet() * @throws InvalidArgumentException If array key isn't a string. */ - public function offsetSet($lang, $val) + public function offsetSet($lang, $val): void { if (!is_string($lang)) { throw new InvalidArgumentException(sprintf( 'Invalid language; must be a string, received %s', - (is_object($lang) ? get_class($lang) : gettype($lang)) + (get_debug_type($lang)) )); } if (!is_string($val)) { throw new InvalidArgumentException(sprintf( 'Translation must be a string, received %s', - (is_object($val) ? get_class($val) : gettype($val)) + (get_debug_type($val)) )); } @@ -139,16 +131,15 @@ public function offsetSet($lang, $val) /** * @param string $lang A language identifier. - * @return void * @see ArrayAccess::offsetUnset() * @throws InvalidArgumentException If array key isn't a string. */ - public function offsetUnset($lang) + public function offsetUnset($lang): void { if (!is_string($lang)) { throw new InvalidArgumentException(sprintf( 'Invalid language; must be a string, received %s', - (is_object($lang) ? get_class($lang) : gettype($lang)) + (get_debug_type($lang)) )); } @@ -171,9 +162,8 @@ public function jsonSerialize() * * @param callable $callback The callback function to run for each value. * The callback takes on the value only. - * @return self */ - public function sanitize(callable $callback) + public function sanitize(callable $callback): static { foreach ($this->val as $lang => $val) { $this->val[$lang] = call_user_func($callback, $val); @@ -186,9 +176,8 @@ public function sanitize(callable $callback) * * @param callable $callback The callback function to run for each value. * The callback takes on two parameters. The value being the first, and the language code second. - * @return self */ - public function each(callable $callback) + public function each(callable $callback): static { foreach ($this->val as $lang => $val) { $this->val[$lang] = call_user_func($callback, $val, $lang); @@ -219,10 +208,9 @@ public function trans(TranslatorInterface $translator, ?string $locale = null) * - array: All languages available in the array. The format of the array should * be a hash in the `lang` => `string` format. * - string: The value will be assigned to the current language. - * @return self * @throws InvalidArgumentException If language or value are invalid. */ - private function setVal($val) + private function setVal($val): static { if ($val instanceof Translation) { $this->val = $val->data(); @@ -232,7 +220,7 @@ private function setVal($val) if (!is_string($lang)) { throw new InvalidArgumentException(sprintf( 'Invalid language; must be a string, received %s', - (is_object($lang) ? get_class($lang) : gettype($lang)) + (get_debug_type($lang)) )); } diff --git a/packages/translator/src/Charcoal/Translator/Translator.php b/packages/translator/src/Charcoal/Translator/Translator.php index 20a86ba54..df8cf509e 100644 --- a/packages/translator/src/Charcoal/Translator/Translator.php +++ b/packages/translator/src/Charcoal/Translator/Translator.php @@ -22,24 +22,18 @@ class Translator extends SymfonyTranslator { /** * The locales manager. - * - * @var LocalesManager */ - private $manager; + private \Charcoal\Translator\LocalesManager $manager; /** * The message selector. - * - * @var MessageSelector */ - private $selector; + private \Symfony\Component\Translation\MessageSelector $selector; /** * The message formatter. - * - * @var MessageFormatterInterface */ - private $formatter; + private \Symfony\Component\Translation\Formatter\MessageFormatterInterface $formatter; /** * The loaded domains. @@ -75,7 +69,7 @@ public function __construct(array $data) $data = array_merge($defaults, $data); // If 'symfony/config' is not installed, DON'T use cache. - if (!class_exists('\Symfony\Component\Config\ConfigCacheFactory', false)) { + if (!class_exists(\Symfony\Component\Config\ConfigCacheFactory::class, false)) { $data['cache_dir'] = null; } @@ -95,9 +89,9 @@ public function __construct(array $data) * @param mixed $resource The resource name. * @param string $locale The locale. * @param string|null $domain The domain. - * @return void */ - public function addResource($format, $resource, $locale, $domain = null) + #[\Override] + public function addResource($format, $resource, $locale, $domain = null): void { if (null !== $domain) { $this->domains[] = $domain; @@ -125,7 +119,7 @@ public function availableDomains() * @param string|null $domain The domain for the message or NULL to use the default. * @return Translation|null The translation object or NULL if the value is not translatable. */ - public function translation($val, array $parameters = [], $domain = null) + public function translation($val, array $parameters = [], $domain = null): ?\Charcoal\Translator\Translation { if ($this->isValidTranslation($val) === false) { return null; @@ -198,7 +192,7 @@ public function translate($val, array $parameters = [], $domain = null, $locale * @param string|null $domain The domain for the message or NULL to use the default. * @return Translation|null The translation object or NULL if the value is not translatable. */ - public function translationChoice($val, $number, array $parameters = [], $domain = null) + public function translationChoice($val, $number, array $parameters = [], $domain = null): ?\Charcoal\Translator\Translation { if ($this->isValidTranslation($val) === false) { return null; @@ -288,7 +282,7 @@ public function locales() * * @return string[] */ - public function availableLocales() + public function availableLocales(): array { return $this->manager()->availableLocales(); } @@ -298,9 +292,9 @@ public function availableLocales() * * @see SymfonyTranslator::setLocale() Ensure that the method also changes the locales manager's language. * @param string $locale The locale. - * @return void */ - public function setLocale($locale) + #[\Override] + public function setLocale($locale): void { parent::setLocale($locale); @@ -311,19 +305,16 @@ public function setLocale($locale) * Set the locales manager. * * @param LocalesManager $manager The locales manager. - * @return void */ - private function setManager(LocalesManager $manager) + private function setManager(LocalesManager $manager): void { $this->manager = $manager; } /** * Retrieve the locales manager. - * - * @return LocalesManager */ - protected function manager() + protected function manager(): \Charcoal\Translator\LocalesManager { return $this->manager; } @@ -335,19 +326,16 @@ protected function manager() * thus we must explicitly require it in this class to guarantee access. * * @param MessageSelector $selector The selector. - * @return void */ - public function setSelector(MessageSelector $selector) + public function setSelector(MessageSelector $selector): void { $this->selector = $selector; } /** * Retrieve the message selector. - * - * @return MessageSelector */ - protected function selector() + protected function selector(): \Symfony\Component\Translation\MessageSelector { return $this->selector; } @@ -359,19 +347,16 @@ protected function selector() * thus we must explicitly require it in this class to guarantee access. * * @param MessageFormatterInterface $formatter The formatter. - * @return void */ - public function setFormatter(MessageFormatterInterface $formatter) + public function setFormatter(MessageFormatterInterface $formatter): void { $this->formatter = $formatter; } /** * Retrieve the message formatter. - * - * @return MessageFormatterInterface */ - protected function formatter() + protected function formatter(): \Symfony\Component\Translation\Formatter\MessageFormatterInterface { return $this->formatter; } @@ -423,7 +408,7 @@ protected function isValidTranslation($val) } if (is_string($val)) { - return !empty(trim($val)); + return !in_array(trim($val), ['', '0'], true); } if ($val instanceof Translation) { @@ -431,17 +416,9 @@ protected function isValidTranslation($val) } if (is_array($val)) { - return !!array_filter( + return (bool) array_filter( $val, - function ($v, $k) { - if (is_string($k) && strlen($k) > 0) { - if (is_string($v) && strlen($v) > 0) { - return true; - } - } - - return false; - }, + fn($v, $k): bool => is_string($k) && $k !== '' && (is_string($v) && $v !== ''), ARRAY_FILTER_USE_BOTH ); } diff --git a/packages/translator/src/Charcoal/Translator/TranslatorConfig.php b/packages/translator/src/Charcoal/Translator/TranslatorConfig.php index 9063debdb..63c98b44d 100644 --- a/packages/translator/src/Charcoal/Translator/TranslatorConfig.php +++ b/packages/translator/src/Charcoal/Translator/TranslatorConfig.php @@ -26,33 +26,25 @@ class TranslatorConfig extends AbstractConfig * * @var string[] */ - private $paths; + private ?array $paths = null; /** * Mapping of domains/locales/messages. - * - * @var array */ - private $translations; + private ?array $translations = null; /** * Debug mode. - * - * @var boolean */ - private $debug; + private ?bool $debug = null; /** * The directory to use for the cache. - * - * @var string */ - private $cacheDir; + private ?string $cacheDir = null; - /** - * @return array - */ - public function defaults() + #[\Override] + public function defaults(): array { return [ 'loaders' => [ @@ -72,7 +64,7 @@ public function defaults() * @throws InvalidArgumentException If the loader is invalid. * @return TranslatorConfig Chainable */ - public function setLoaders(array $loaders) + public function setLoaders(array $loaders): static { $this->loaders = []; foreach ($loaders as $loader) { @@ -99,7 +91,7 @@ public function loaders() * @param string[] $paths The "paths" (search pattern) to look into for translation resources. * @return TranslatorConfig Chainable */ - public function setPaths(array $paths) + public function setPaths(array $paths): static { $this->paths = []; $this->addPaths($paths); @@ -111,7 +103,7 @@ public function setPaths(array $paths) * @throws InvalidArgumentException If the path is not a string. * @return TranslatorConfig Chainable */ - public function addPaths(array $paths) + public function addPaths(array $paths): static { foreach ($paths as $path) { if (!is_string($path)) { @@ -127,7 +119,7 @@ public function addPaths(array $paths) /** * @return string[] */ - public function paths() + public function paths(): ?array { return $this->paths; } @@ -150,17 +142,17 @@ public function paths() * @throws InvalidArgumentException If the path is not a string. * @return TranslatorConfig Chainable */ - public function setTranslations(array $translations) + public function setTranslations(array $translations): static { $this->translations = []; - foreach ($translations as $domain => $data) { + foreach ($translations as $data) { if (!is_array($data)) { throw new InvalidArgumentException( 'Translator translations must be a 3-level array' ); } - foreach ($data as $locale => $messages) { + foreach ($data as $messages) { if (!is_array($messages)) { throw new InvalidArgumentException( 'Translator translations must be a 3-level array' @@ -179,7 +171,7 @@ public function setTranslations(array $translations) * * @return array */ - public function translations() + public function translations(): ?array { return $this->translations; } @@ -188,16 +180,16 @@ public function translations() * @param boolean $debug The debug flag. * @return TranslatorConfig Chainable */ - public function setDebug($debug) + public function setDebug($debug): static { - $this->debug = !!$debug; + $this->debug = (bool) $debug; return $this; } /** * @return boolean */ - public function debug() + public function debug(): ?bool { return $this->debug; } @@ -207,7 +199,7 @@ public function debug() * @throws InvalidArgumentException If the cache dir argument is not a string. * @return TranslatorConfig Chainable */ - public function setCacheDir($cacheDir) + public function setCacheDir($cacheDir): static { if (!is_string($cacheDir)) { throw new InvalidArgumentException( @@ -221,15 +213,12 @@ public function setCacheDir($cacheDir) /** * @return string */ - public function cacheDir() + public function cacheDir(): ?string { return $this->cacheDir; } - /** - * @return array - */ - private function availableLoaders() + private function availableLoaders(): array { return [ 'csv', diff --git a/packages/translator/tests/Charcoal/Translator/AbstractTestCase.php b/packages/translator/tests/Charcoal/Translator/AbstractTestCase.php index 0cb15733f..800e0c5a6 100644 --- a/packages/translator/tests/Charcoal/Translator/AbstractTestCase.php +++ b/packages/translator/tests/Charcoal/Translator/AbstractTestCase.php @@ -1,5 +1,7 @@ registerConfig($container); $this->registerBaseUrl($container); @@ -70,9 +69,8 @@ public function registerBaseServices(Container $container) * Register the unit tests required services. * * @param Container $container A DI container. - * @return void */ - public function registerModelServices(Container $container) + public function registerModelServices(Container $container): void { $this->registerLogger($container); $this->registerTranslator($container); @@ -86,9 +84,8 @@ public function registerModelServices(Container $container) * Register the unit tests required services. * * @param Container $container A DI container. - * @return void */ - public function registerAdminServices(Container $container) + public function registerAdminServices(Container $container): void { $this->registerClimate($container); $this->registerAdminBaseUrl($container); @@ -98,71 +95,62 @@ public function registerAdminServices(Container $container) * Setup the application's base URI. * * @param Container $container A DI container. - * @return void */ - public function registerBaseUrl(Container $container) + public function registerBaseUrl(Container $container): void { - $container['base-url'] = function () { - return Uri::createFromString('https://example.com:8080/foo/bar?abc=123'); - }; + $container['base-url'] = (fn() => Uri::createFromString('https://example.com:8080/foo/bar?abc=123')); } /** * Setup the admin's base URI. * * @param Container $container A DI container. - * @return void */ - public function registerAdminBaseUrl(Container $container) + public function registerAdminBaseUrl(Container $container): void { - $container['admin/base-url'] = function () { - return Uri::createFromString('https://example.com:8080/admin/qux?abc=123'); - }; + $container['admin/base-url'] = (fn() => Uri::createFromString('https://example.com:8080/admin/qux?abc=123')); } /** * Setup the application configset. * * @param Container $container A DI container. - * @return void */ - public function registerConfig(Container $container) + public function registerConfig(Container $container): void { - $container['config'] = function () { - return new AppConfig([ - 'base_path' => realpath(__DIR__ . '/../../..'), - 'locales' => [ - 'languages' => [ - 'en' => [ 'locale' => 'en-US', 'locales' => [ 'en_US.UTF-8', 'en_US.utf8', 'en_US' ] ], - 'fr' => [ 'locale' => 'fr-FR' ] - ], - 'default_language' => 'en', - 'fallback_languages' => [ 'en' ] + $container['config'] = (fn(): \Charcoal\App\AppConfig => new AppConfig([ + 'base_path' => realpath(__DIR__ . '/../../..'), + 'locales' => [ + 'languages' => [ + 'en' => [ 'locale' => 'en-US', 'locales' => [ 'en_US.UTF-8', 'en_US.utf8', 'en_US' ] ], + 'fr' => [ 'locale' => 'fr-FR' ] ], - 'translator' => [ - 'paths' => [ - '/Charcoal/Translator/Fixture/translations' - ], - 'translations' => [ - 'messages' => [ - 'en' => [ - 'foo' => 'FOO' - ], - 'fr' => [ - 'foo' => 'OOF' - ] - ] - ], - 'auto_detect' => true, - 'debug' => false + 'default_language' => 'en', + 'fallback_languages' => [ 'en' ] + ], + 'translator' => [ + 'paths' => [ + '/Charcoal/Translator/Fixture/translations' ], - 'view' => [ - 'paths' => [ - '/Charcoal/Translator/Fixture/views' + 'translations' => [ + 'messages' => [ + 'en' => [ + 'foo' => 'FOO' + ], + 'fr' => [ + 'foo' => 'OOF' + ] ] + ], + 'auto_detect' => true, + 'debug' => false + ], + 'view' => [ + 'paths' => [ + '/Charcoal/Translator/Fixture/views' ] - ]); - }; + ] + ])); } /** @@ -171,11 +159,10 @@ public function registerConfig(Container $container) * Note: Uses SQLite to create a database in memory. * * @param Container $container A DI container. - * @return void */ - public function registerSource(Container $container) + public function registerSource(Container $container): void { - $container['database'] = function () { + $container['database'] = function (): \PDO { $pdo = new PDO('sqlite::memory:'); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); return $pdo; @@ -186,53 +173,40 @@ public function registerSource(Container $container) * Setup the application's logging interface. * * @param Container $container A DI container. - * @return void */ - public function registerLogger(Container $container) + public function registerLogger(Container $container): void { - $container['logger'] = function () { - return new NullLogger(); - }; + $container['logger'] = (fn(): \Psr\Log\NullLogger => new NullLogger()); } /** * Setup the application's caching interface. * * @param Container $container A DI container. - * @return void */ - public function registerCache(Container $container) + public function registerCache(Container $container): void { - $container['cache'] = function () { - return new Pool(new Ephemeral()); - }; + $container['cache'] = (fn(): \Stash\Pool => new Pool(new Ephemeral())); } /** * Setup the application's translator service. * * @param Container $container A DI container. - * @return void */ - public function registerTranslator(Container $container) + public function registerTranslator(Container $container): void { - $container['locales/config'] = function (Container $container) { - return new LocalesConfig($container['config']['locales']); - }; + $container['locales/config'] = (fn(Container $container): \Charcoal\Translator\LocalesConfig => new LocalesConfig($container['config']['locales'])); - $container['locales/manager'] = function (Container $container) { - return new LocalesManager([ - 'locales' => $container['locales/config']['languages'], - 'default_language' => $container['locales/config']['default_language'], - 'fallback_languages' => $container['locales/config']['fallback_languages'] - ]); - }; + $container['locales/manager'] = (fn(Container $container): \Charcoal\Translator\LocalesManager => new LocalesManager([ + 'locales' => $container['locales/config']['languages'], + 'default_language' => $container['locales/config']['default_language'], + 'fallback_languages' => $container['locales/config']['fallback_languages'] + ])); - $container['translator/config'] = function (Container $container) { - return new TranslatorConfig($container['config']['translator']); - }; + $container['translator/config'] = (fn(Container $container): \Charcoal\Translator\TranslatorConfig => new TranslatorConfig($container['config']['translator'])); - $container['translator'] = function (Container $container) { + $container['translator'] = function (Container $container): \Charcoal\Translator\Translator { $translator = new Translator([ 'manager' => $container['locales/manager'], 'message_selector' => new MessageSelector(), @@ -250,100 +224,87 @@ public function registerTranslator(Container $container) * Setup the framework's metadata loader interface. * * @param Container $container A DI container. - * @return void */ - public function registerMetadataLoader(Container $container) + public function registerMetadataLoader(Container $container): void { - $container['metadata/loader'] = function (Container $container) { - return new MetadataLoader([ - 'cache' => $container['cache'], - 'logger' => $container['logger'], - 'base_path' => $container['config']['base_path'], - 'paths' => [ - 'metadata', - // Standalone - 'vendor/charcoal/property/metadata', - // Monorepo - '/../property/metadata' - ] - ]); - }; + $container['metadata/loader'] = (fn(Container $container): \Charcoal\Model\Service\MetadataLoader => new MetadataLoader([ + 'cache' => $container['cache'], + 'logger' => $container['logger'], + 'base_path' => $container['config']['base_path'], + 'paths' => [ + 'metadata', + // Standalone + 'vendor/charcoal/property/metadata', + // Monorepo + '/../property/metadata' + ] + ])); } /** * Setup the framework's data source factory. * * @param Container $container A DI container. - * @return void */ - public function registerSourceFactory(Container $container) + public function registerSourceFactory(Container $container): void { - $container['source/factory'] = function (Container $container) { - return new Factory([ - 'map' => [ - 'database' => DatabaseSource::class - ], - 'arguments' => [[ - 'logger' => $container['logger'], - 'cache' => $container['cache'], - 'pdo' => $container['database'] - ]] - ]); - }; + $container['source/factory'] = (fn(Container $container): \Charcoal\Factory\GenericFactory => new Factory([ + 'map' => [ + 'database' => DatabaseSource::class + ], + 'arguments' => [[ + 'logger' => $container['logger'], + 'cache' => $container['cache'], + 'pdo' => $container['database'] + ]] + ])); } /** * Setup the framework's model factory. * * @param Container $container A DI container. - * @return void */ - public function registerModelFactory(Container $container) + public function registerModelFactory(Container $container): void { - $container['model/factory'] = function (Container $container) { - return new Factory([ - 'arguments' => [[ - 'container' => $container, - 'logger' => $container['logger'], - 'metadata_loader' => $container['metadata/loader'], - 'source_factory' => $container['source/factory'], - 'property_factory' => $container['property/factory'] - ]] - ]); - }; + $container['model/factory'] = (fn(Container $container): \Charcoal\Factory\GenericFactory => new Factory([ + 'arguments' => [[ + 'container' => $container, + 'logger' => $container['logger'], + 'metadata_loader' => $container['metadata/loader'], + 'source_factory' => $container['source/factory'], + 'property_factory' => $container['property/factory'] + ]] + ])); } /** * Setup the framework's property factory. * * @param Container $container A DI container. - * @return void */ - public function registerPropertyFactory(Container $container) + public function registerPropertyFactory(Container $container): void { - $container['property/factory'] = function (Container $container) { - return new Factory([ - 'resolver_options' => [ - 'prefix' => '\\Charcoal\\Property\\', - 'suffix' => 'Property' - ], - 'arguments' => [[ - 'container' => $container, - 'database' => $container['database'], - 'logger' => $container['logger'], - 'translator' => $container['translator'] - ]] - ]); - }; + $container['property/factory'] = (fn(Container $container): \Charcoal\Factory\GenericFactory => new Factory([ + 'resolver_options' => [ + 'prefix' => '\\Charcoal\\Property\\', + 'suffix' => 'Property' + ], + 'arguments' => [[ + 'container' => $container, + 'database' => $container['database'], + 'logger' => $container['logger'], + 'translator' => $container['translator'] + ]] + ])); } /** * Setup the CLImate library. * * @param Container $container A DI container. - * @return void */ - public function registerClimate(Container $container) + public function registerClimate(Container $container): void { $container['climate/system'] = function () { $system = Mockery::mock(Linux::class); @@ -370,11 +331,9 @@ public function registerClimate(Container $container) return $reader; }; - $container['climate/util'] = function (Container $container) { - return new UtilFactory($container['climate/system']); - }; + $container['climate/util'] = (fn(Container $container): \League\CLImate\Util\UtilFactory => new UtilFactory($container['climate/system'])); - $container['climate'] = function (Container $container) { + $container['climate'] = function (Container $container): \League\CLImate\CLImate { $climate = new CLImate(); $climate->setOutput($container['climate/output']); diff --git a/packages/translator/tests/Charcoal/Translator/LocalesConfigTest.php b/packages/translator/tests/Charcoal/Translator/LocalesConfigTest.php index a6490274d..6a7e2f346 100644 --- a/packages/translator/tests/Charcoal/Translator/LocalesConfigTest.php +++ b/packages/translator/tests/Charcoal/Translator/LocalesConfigTest.php @@ -15,25 +15,18 @@ class LocalesConfigTest extends AbstractTestCase { /** * Tested Class. - * - * @var LocalesConfig */ - private $obj; + private \Charcoal\Translator\LocalesConfig|array $obj; /** * Set up the test. - * - * @return void */ protected function setUp(): void { $this->obj = new LocalesConfig(); } - /** - * @return void - */ - public function testDefaultsArrayAccess() + public function testDefaultsArrayAccess(): void { $this->assertArrayHasKey('en', $this->obj['languages']); $this->assertEquals('en', $this->obj['default_language']); @@ -41,10 +34,7 @@ public function testDefaultsArrayAccess() $this->assertFalse($this->obj['auto_detect']); } - /** - * @return void - */ - public function testSetLanguages() + public function testSetLanguages(): void { $langs = [ 'foo' => [ @@ -64,10 +54,7 @@ public function testSetLanguages() $this->assertEquals($langs, $this->obj['languages']); } - /** - * @return void - */ - public function testSetDefaultLanguage() + public function testSetDefaultLanguage(): void { $ret = $this->obj->setDefaultLanguage('foo'); $this->assertSame($ret, $this->obj); @@ -80,10 +67,7 @@ public function testSetDefaultLanguage() $this->obj->setDefaultLanguage(false); } - /** - * @return void - */ - public function testSetFallbackLanguages() + public function testSetFallbackLanguages(): void { $ret = $this->obj->setFallbackLanguages(['foo']); $this->assertSame($ret, $this->obj); diff --git a/packages/translator/tests/Charcoal/Translator/LocalesManagerTest.php b/packages/translator/tests/Charcoal/Translator/LocalesManagerTest.php index 7adad23d6..ab239027f 100644 --- a/packages/translator/tests/Charcoal/Translator/LocalesManagerTest.php +++ b/packages/translator/tests/Charcoal/Translator/LocalesManagerTest.php @@ -15,15 +15,11 @@ class LocalesManagerTest extends AbstractTestCase { /** * Tested Class. - * - * @var LocalesManager */ - private $obj; + private \Charcoal\Translator\LocalesManager $obj; /** * Set up the test. - * - * @return void */ protected function setUp(): void { @@ -37,10 +33,7 @@ protected function setUp(): void ]); } - /** - * @return void - */ - public function testConstructorWithDefaultLanguage() + public function testConstructorWithDefaultLanguage(): void { $this->obj = new LocalesManager([ 'locales' => [ @@ -54,13 +47,10 @@ public function testConstructorWithDefaultLanguage() $this->assertEquals('bar', $this->obj->defaultLocale()); } - /** - * @return void - */ - public function testConstructorDefaultLanguageWithInvalidType() + public function testConstructorDefaultLanguageWithInvalidType(): void { $this->expectException(InvalidArgumentException::class); - $obj = new LocalesManager([ + new LocalesManager([ 'locales' => [ 'foo' => [] ], @@ -68,13 +58,10 @@ public function testConstructorDefaultLanguageWithInvalidType() ]); } - /** - * @return void - */ - public function testConstructorDefaultLanguageWithInvalidLocale() + public function testConstructorDefaultLanguageWithInvalidLocale(): void { $this->expectException(InvalidArgumentException::class); - $obj = new LocalesManager([ + new LocalesManager([ 'locales' => [ 'foo' => [] ], @@ -82,21 +69,15 @@ public function testConstructorDefaultLanguageWithInvalidLocale() ]); } - /** - * @return void - */ - public function testConstructorWithoutActiveLocales() + public function testConstructorWithoutActiveLocales(): void { $this->expectException(InvalidArgumentException::class); - $obj = new LocalesManager([ + new LocalesManager([ 'locales' => [] ]); } - /** - * @return void - */ - public function testLocales() + public function testLocales(): void { $locales = $this->obj->locales(); $this->assertArrayHasKey('foo', $locales); @@ -106,21 +87,15 @@ public function testLocales() $this->assertArrayNotHasKey('baz', $locales); } - /** - * @requires PHP >= 7.0 - * @return void - */ - public function testSortedLocalesInPhp7() + #[\PHPUnit\Framework\Attributes\RequiresPhp('>= 7.0')] + public function testSortedLocalesInPhp7(): void { $obj = $this->getLocalesManagerForSortedLocales(); $this->assertEquals([ 'xyz', 'zyx', 'qux', 'foo', 'bar' ], $obj->availableLocales()); } - /** - * @return LocalesManager - */ - public function getLocalesManagerForSortedLocales() + public function getLocalesManagerForSortedLocales(): \Charcoal\Translator\LocalesManager { return new LocalesManager([ 'locales' => [ @@ -134,18 +109,12 @@ public function getLocalesManagerForSortedLocales() ]); } - /** - * @return void - */ - public function testAvailableLocales() + public function testAvailableLocales(): void { $this->assertEquals([ 'foo', 'bar' ], $this->obj->availableLocales()); } - /** - * @return void - */ - public function testSetCurrentLocale() + public function testSetCurrentLocale(): void { $this->assertEquals('foo', $this->obj->currentLocale()); @@ -156,19 +125,13 @@ public function testSetCurrentLocale() $this->assertEquals('foo', $this->obj->currentLocale()); } - /** - * @return void - */ - public function testSetCurrentLocaleWithInvalidType() + public function testSetCurrentLocaleWithInvalidType(): void { $this->expectException(InvalidArgumentException::class); $this->obj->setCurrentLocale(false); } - /** - * @return void - */ - public function testSetCurrentLocaleWithInvalidLocale() + public function testSetCurrentLocaleWithInvalidLocale(): void { $this->expectException(InvalidArgumentException::class); $this->obj->setCurrentLocale('qux'); diff --git a/packages/translator/tests/Charcoal/Translator/Middleware/LanguageMiddlewareTest.php b/packages/translator/tests/Charcoal/Translator/Middleware/LanguageMiddlewareTest.php index 3e09b5309..8da7de554 100644 --- a/packages/translator/tests/Charcoal/Translator/Middleware/LanguageMiddlewareTest.php +++ b/packages/translator/tests/Charcoal/Translator/Middleware/LanguageMiddlewareTest.php @@ -27,21 +27,14 @@ class LanguageMiddlewareTest extends AbstractTestCase { /** * Tested Class. - * - * @var LanguageMiddleware */ - private $obj; + private \Charcoal\Translator\Middleware\LanguageMiddleware $obj; /** * Service Container. - * - * @var Container */ - private $container; + private \Pimple\Container|array|null $container = null; - /** - * @return void - */ public static function setupBeforeClass(): void { if (!isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { @@ -49,9 +42,6 @@ public static function setupBeforeClass(): void } } - /** - * @return void - */ public static function teardownAfterClass(): void { if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { @@ -61,12 +51,10 @@ public static function teardownAfterClass(): void /** * Set up the test. - * - * @return void */ protected function setUp(): void { - $container = $this->getContainer(); + $this->getContainer(); $this->obj = $this->middlewareFactory([ 'use_params' => true @@ -77,9 +65,8 @@ protected function setUp(): void * Create LanguageMiddleware. * * @param array $data Extra options to pass to the middleare. - * @return LanguageMiddleware */ - protected function middlewareFactory(array $data = []) + protected function middlewareFactory(array $data = []): \Charcoal\Translator\Middleware\LanguageMiddleware { $container = $this->getContainer(); @@ -89,15 +76,13 @@ protected function middlewareFactory(array $data = []) 'default_language' => $container['translator']->getLocale(), ]; - $middleware = new LanguageMiddleware(array_replace($defaults, $data)); - - return $middleware; + return new LanguageMiddleware(array_replace($defaults, $data)); } /** * @return Container */ - private function getContainer() + private function getContainer(): \Pimple\Container|array { if ($this->container === null) { $this->container = new Container(); @@ -142,7 +127,7 @@ private function getContainer() * @param array $params The URI query string parameters. * @return UriInterface */ - private function mockUri($path = '', array $params = []) + private function mockUri(string $path = '', array $params = []): \PHPUnit\Framework\MockObject\MockObject { $uri = $this->createMock(UriInterface::class); @@ -157,7 +142,7 @@ private function mockUri($path = '', array $params = []) * @param array $params The URI query string parameters. * @return ServerRequestInterface */ - private function mockRequest($path = '', array $params = []) + private function mockRequest(string $path = '', array $params = []): \PHPUnit\Framework\MockObject\MockObject { $request = $this->createMock(ServerRequestInterface::class); @@ -171,47 +156,32 @@ private function mockRequest($path = '', array $params = []) /** * @return ResponseInterface */ - private function mockResponse() + private function mockResponse(): \PHPUnit\Framework\MockObject\MockObject { - $response = $this->createMock(ResponseInterface::class); - - return $response; + return $this->createMock(ResponseInterface::class); } - /** - * @return void - */ - public function testInvoke() + public function testInvoke(): void { $request = $this->mockRequest('/fr/foo/bar'); $response = $this->mockResponse(); - $next = function ($request, $response) { - return $response; - }; + $next = (fn($request, $response) => $response); - $return = call_user_func([ $this->obj, '__invoke' ], $request, $response, $next); + $return = call_user_func($this->obj->__invoke(...), $request, $response, $next); $this->assertEquals($response, $return); } - /** - * @return void - */ - public function testInvokeWithExcludedPath() + public function testInvokeWithExcludedPath(): void { $request = $this->mockRequest('/admin/foo/bar'); $response = $this->mockResponse(); - $next = function ($request, $response) { - return $response; - }; + $next = (fn($request, $response) => $response); - $return = call_user_func([ $this->obj, '__invoke' ], $request, $response, $next); + $return = call_user_func($this->obj->__invoke(...), $request, $response, $next); $this->assertEquals($response, $return); } - /** - * @return void - */ - public function testGetLanguageWithServerRequest() + public function testGetLanguageWithServerRequest(): void { $request = $this->mockRequest('/fr/foo/bar'); $return = $this->callMethod($this->obj, 'getLanguage', [ $request ]); @@ -232,10 +202,7 @@ public function testGetLanguageWithServerRequest() $this->assertEquals('fr', $return); } - /** - * @return void - */ - public function testGetLanguageWithClientRequest() + public function testGetLanguageWithClientRequest(): void { $request = $this->createMock(ClientRequestInterface::class); $request->expects($this->any())->method('getUri')->will($this->returnValue($this->mockUri('/jp/foo/bar'))); @@ -245,10 +212,7 @@ public function testGetLanguageWithClientRequest() $this->assertEquals('fr', $return); } - /** - * @return void - */ - public function testGetLanguageUseHost() + public function testGetLanguageUseHost(): void { $this->obj = $this->middlewareFactory([ 'browser_language' => null, @@ -282,10 +246,7 @@ public function testGetLanguageUseHost() $this->assertEquals('en', $return); } - /** - * @return void - */ - public function testGetLanguageUseHostWithBadHost() + public function testGetLanguageUseHostWithBadHost(): void { $this->obj = $this->middlewareFactory([ 'browser_language' => null, @@ -310,10 +271,7 @@ public function testGetLanguageUseHostWithBadHost() $this->assertEquals('en', $return); } - /** - * @return void - */ - public function testGetLanguageUseDefault() + public function testGetLanguageUseDefault(): void { $this->obj = $this->middlewareFactory([ 'browser_language' => null diff --git a/packages/translator/tests/Charcoal/Translator/Mock/StringClass.php b/packages/translator/tests/Charcoal/Translator/Mock/StringClass.php index 441dd117a..ad17231d0 100644 --- a/packages/translator/tests/Charcoal/Translator/Mock/StringClass.php +++ b/packages/translator/tests/Charcoal/Translator/Mock/StringClass.php @@ -1,29 +1,22 @@ str = $str; } - /** - * @return string - */ - public function __toString() + public function __toString(): string { return $this->str; } diff --git a/packages/translator/tests/Charcoal/Translator/ReflectionsTrait.php b/packages/translator/tests/Charcoal/Translator/ReflectionsTrait.php index 5c59e11dc..1d488c6de 100644 --- a/packages/translator/tests/Charcoal/Translator/ReflectionsTrait.php +++ b/packages/translator/tests/Charcoal/Translator/ReflectionsTrait.php @@ -18,13 +18,10 @@ trait ReflectionsTrait * * @param mixed $class The class name or object that contains the method. * @param string $name The method name to reflect. - * @return ReflectionMethod */ - public function getMethod($class, $name) + public function getMethod($class, $name): \ReflectionMethod { - $reflected = new ReflectionMethod($class, $name); - $reflected->setAccessible(true); - return $reflected; + return new ReflectionMethod($class, $name); } /** @@ -38,7 +35,7 @@ public function getMethod($class, $name) public function callMethod($object, $name, array $args = []) { $method = $this->getMethod($object, $name); - if (empty($args)) { + if ($args === []) { return $method->invoke($object); } else { return $method->invokeArgs($object, $args); @@ -65,13 +62,10 @@ public function callMethodWith($object, $name, ...$args) * * @param mixed $class The class name or object that contains the property. * @param string $name The property name to reflect. - * @return ReflectionProperty */ - public function getProperty($class, $name) + public function getProperty($class, $name): \ReflectionProperty { - $reflected = new ReflectionProperty($class, $name); - $reflected->setAccessible(true); - return $reflected; + return new ReflectionProperty($class, $name); } /** @@ -92,9 +86,8 @@ public function getPropertyValue($object, $name) * @param mixed $object The object to access. * @param string $name The property name to affect. * @param mixed $value The new value. - * @return void */ - public function setPropertyValue($object, $name, $value) + public function setPropertyValue($object, $name, $value): void { $this->getProperty($object, $name)->setValue($object, $value); } diff --git a/packages/translator/tests/Charcoal/Translator/Script/TranslationParserScriptTest.php b/packages/translator/tests/Charcoal/Translator/Script/TranslationParserScriptTest.php index 89ff3c06c..f8e63addb 100644 --- a/packages/translator/tests/Charcoal/Translator/Script/TranslationParserScriptTest.php +++ b/packages/translator/tests/Charcoal/Translator/Script/TranslationParserScriptTest.php @@ -23,10 +23,8 @@ class TranslationParserScriptTest extends AbstractTestCase { /** * Tested Class. - * - * @var TranslationParserScript */ - private $obj; + private \Charcoal\Translator\Script\TranslationParserScript $obj; /** * CLImate Output @@ -37,15 +35,11 @@ class TranslationParserScriptTest extends AbstractTestCase /** * Service Container. - * - * @var Container */ - private $container; + private \Pimple\Container $container; /** * Set up the test. - * - * @return void */ protected function setUp(): void { @@ -61,10 +55,7 @@ protected function setUp(): void ]); } - /** - * @return Container - */ - private function getContainer() + private function getContainer(): \Pimple\Container { $container = new Container(); $provider = new ContainerProvider(); @@ -76,10 +67,7 @@ private function getContainer() return $container; } - /** - * @return void - */ - public function testDefaultArguments() + public function testDefaultArguments(): void { $args = $this->obj->defaultArguments(); $this->assertArrayHasKey('domain', $args); diff --git a/packages/translator/tests/Charcoal/Translator/ServiceProvider/TranslatorServiceProviderTest.php b/packages/translator/tests/Charcoal/Translator/ServiceProvider/TranslatorServiceProviderTest.php index cefdd2bf2..31d1b4b0b 100644 --- a/packages/translator/tests/Charcoal/Translator/ServiceProvider/TranslatorServiceProviderTest.php +++ b/packages/translator/tests/Charcoal/Translator/ServiceProvider/TranslatorServiceProviderTest.php @@ -20,22 +20,16 @@ class TranslatorServiceProviderTest extends AbstractTestCase { /** * Tested Class. - * - * @var TranslatorServiceProvider */ - private $obj; + private \Charcoal\Translator\ServiceProvider\TranslatorServiceProvider $obj; /** * Service Container. - * - * @var Container */ - private $container; + private \Pimple\Container $container; /** * Set up the test. - * - * @return void */ protected function setUp(): void { @@ -105,10 +99,7 @@ protected function resetDefaultLanguage() $this->container['locales/default-language'] = $raw; } - /** - * @return void - */ - public function testKeys() + public function testKeys(): void { $this->assertFalse(isset($this->container['foofoobarbarbaz'])); $this->assertTrue(isset($this->container['locales/config'])); @@ -120,66 +111,45 @@ public function testKeys() $this->assertTrue(isset($this->container['middlewares/charcoal/translator/middleware/language'])); } - /** - * @return void - */ - public function testAvailableLanguages() + public function testAvailableLanguages(): void { $languages = $this->container['locales/available-languages']; $this->assertContains('en', $languages); } - /** - * @return void - */ - public function testLanguages() + public function testLanguages(): void { $languages = $this->container['locales/languages']; $this->assertArrayHasKey('en', $languages); } - /** - * @return void - */ - public function testDefaultLanguage() + public function testDefaultLanguage(): void { $defaultLanguage = $this->container['locales/default-language']; $this->assertEquals('en', $defaultLanguage); } - /** - * @return void - */ - public function testBrowserLanguageIsNullWithoutHttp() + public function testBrowserLanguageIsNullWithoutHttp(): void { $browserLanguage = $this->container['locales/browser-language']; $this->assertNull($browserLanguage); } - /** - * @return void - */ - public function testBrowserLanguage() + public function testBrowserLanguage(): void { $_SERVER['HTTP_ACCEPT_LANGUAGE'] = 'fr'; $browserLanguage = $this->container['locales/browser-language']; $this->assertEquals('fr', $browserLanguage); } - /** - * @return void - */ - public function testBrowserLanguageIsNullIfInvalidHttp() + public function testBrowserLanguageIsNullIfInvalidHttp(): void { $_SERVER['HTTP_ACCEPT_LANGUAGE'] = 'baz'; $browserLanguage = $this->container['locales/browser-language']; $this->assertNull($browserLanguage); } - /** - * @return void - */ - public function testDetectedLanguageIsNullWithoutHttp() + public function testDetectedLanguageIsNullWithoutHttp(): void { $this->container['locales/config']->setAutoDetect(true); @@ -191,10 +161,7 @@ public function testDetectedLanguageIsNullWithoutHttp() $this->container['locales/config']->setAutoDetect(false); } - /** - * @return void - */ - public function testDetectedLanguage() + public function testDetectedLanguage(): void { $_SERVER['HTTP_ACCEPT_LANGUAGE'] = 'fr'; $this->container['locales/config']->setAutoDetect(true); @@ -207,37 +174,25 @@ public function testDetectedLanguage() $this->container['locales/config']->setAutoDetect(false); } - /** - * @return void - */ - public function testFallbackLanguages() + public function testFallbackLanguages(): void { $fallbackLanguages = $this->container['locales/fallback-languages']; $this->assertEquals([ 'en' ], $fallbackLanguages); } - /** - * @return void - */ - public function testLanguageManager() + public function testLanguageManager(): void { $manager = $this->container['locales/manager']; $this->assertInstanceOf(LocalesManager::class, $manager); } - /** - * @return void - */ - public function testTranslator() + public function testTranslator(): void { $translator = $this->container['translator']; $this->assertInstanceOf(Translator::class, $translator); } - /** - * @return void - */ - public function testMiddleware() + public function testMiddleware(): void { $middleware = $this->container['middlewares/charcoal/translator/middleware/language']; $this->assertInstanceOf(LanguageMiddleware::class, $middleware); diff --git a/packages/translator/tests/Charcoal/Translator/TranslationTest.php b/packages/translator/tests/Charcoal/Translator/TranslationTest.php index b189a175a..647609056 100644 --- a/packages/translator/tests/Charcoal/Translator/TranslationTest.php +++ b/packages/translator/tests/Charcoal/Translator/TranslationTest.php @@ -15,17 +15,11 @@ */ class TranslationTest extends AbstractTestCase { - /** - * @var LocalesManager - */ - private $localesManager; + private ?\Charcoal\Translator\LocalesManager $localesManager = null; - /** - * @return LocalesManager - */ - private function localesManager() + private function localesManager(): \Charcoal\Translator\LocalesManager { - if ($this->localesManager === null) { + if (!$this->localesManager instanceof \Charcoal\Translator\LocalesManager) { $this->localesManager = new LocalesManager([ 'locales' => [ 'en' => [ @@ -44,10 +38,7 @@ private function localesManager() return $this->localesManager; } - /** - * @return void - */ - public function testConstructorWithStringParam() + public function testConstructorWithStringParam(): void { $obj = new Translation('Hello!', $this->localesManager()); @@ -58,10 +49,7 @@ public function testConstructorWithStringParam() $this->assertFalse(isset($obj['fr'])); } - /** - * @return void - */ - public function testConstructorWithArrayParam() + public function testConstructorWithArrayParam(): void { $obj = new Translation([ 'en' => 'Hello!', 'fr' => 'Bonjour!' ], $this->localesManager()); @@ -74,10 +62,7 @@ public function testConstructorWithArrayParam() $this->assertFalse(isset($obj['es'])); } - /** - * @return void - */ - public function testConstructorWithObjectParam() + public function testConstructorWithObjectParam(): void { $trans = new Translation([ 'en' => 'Hello!', 'fr' => 'Bonjour!' ], $this->localesManager()); $obj = new Translation($trans, $this->localesManager()); @@ -91,19 +76,13 @@ public function testConstructorWithObjectParam() $this->assertFalse(isset($obj['es'])); } - /** - * @return void - */ - public function testConstructorWithInvalidParam() + public function testConstructorWithInvalidParam(): void { $this->expectException(InvalidArgumentException::class); - $obj = new Translation(false, $this->localesManager()); + new Translation(false, $this->localesManager()); } - /** - * @return void - */ - public function testToString() + public function testToString(): void { $manager = $this->localesManager(); @@ -118,10 +97,7 @@ public function testToString() $this->assertEquals('', (string)$obj); } - /** - * @return void - */ - public function testArraySet() + public function testArraySet(): void { $obj = new Translation('Hello!', $this->localesManager()); $this->assertEquals('Hello!', (string)$obj); @@ -130,19 +106,13 @@ public function testArraySet() $this->assertEquals('Charcoal', (string)$obj); } - /** - * @return void - */ - public function testArrayGet() + public function testArrayGet(): void { $obj = new Translation('Charcoal', $this->localesManager()); $this->assertEquals('Charcoal', $obj['en']); } - /** - * @return void - */ - public function testArrayUnset() + public function testArrayUnset(): void { $obj = new Translation('Hello!', $this->localesManager()); $this->assertTrue(isset($obj['en'])); @@ -151,102 +121,70 @@ public function testArrayUnset() $this->assertFalse(isset($obj['en'])); } - /** - * @return void - */ - public function testOffsetGetThrowsException() + public function testOffsetGetThrowsException(): void { $this->expectException(InvalidArgumentException::class); - $obj = new Translation('Hello!', $this->localesManager()); - $ret = $obj[0]; + new Translation('Hello!', $this->localesManager()); } - /** - * @return void - */ - public function testOffsetGetThrowsException2() + public function testOffsetGetThrowsException2(): void { $this->expectException(DomainException::class); - $obj = new Translation('Hello!', $this->localesManager()); - $ret = $obj['fr']; + new Translation('Hello!', $this->localesManager()); } - /** - * @return void - */ - public function testOffsetSetThrowsException() + public function testOffsetSetThrowsException(): void { $this->expectException(InvalidArgumentException::class); $obj = new Translation('Hello!', $this->localesManager()); $obj[0] = 'en'; } - /** - * @return void - */ - public function testOffsetSetThrowsException2() + public function testOffsetSetThrowsException2(): void { $this->expectException(InvalidArgumentException::class); $obj = new Translation('Hello!', $this->localesManager()); $obj['en'] = []; } - /** - * @return void - */ - public function testOffsetExistThrowsException() + public function testOffsetExistThrowsException(): void { $this->expectException(InvalidArgumentException::class); $obj = new Translation('Hello!', $this->localesManager()); - isset($obj[0]); + $obj[0]; } - /** - * @return void - */ - public function testOffsetUnsetThrowsException() + public function testOffsetUnsetThrowsException(): void { $this->expectException(InvalidArgumentException::class); $obj = new Translation('Hello!', $this->localesManager()); unset($obj[0]); } - /** - * @return void - */ - public function testInvalidValueThrowsException() + public function testInvalidValueThrowsException(): void { $this->expectException(InvalidArgumentException::class); - $obj = new Translation([ 'en' ], $this->localesManager()); + new Translation([ 'en' ], $this->localesManager()); } - /** - * @return void - */ - public function testSanitize() + public function testSanitize(): void { $obj = new Translation(' Hello! ', $this->localesManager()); $obj->sanitize('trim'); $this->assertEquals([ 'en' => 'Hello!' ], $obj->data()); } - /** - * @return void - */ - public function testEach() + public function testEach(): void { $obj = new Translation(' Hello! ', $this->localesManager()); - $obj->each(function ($val, $lang) { + $obj->each(function ($val, $lang): string { $this->assertEquals('en', $lang); return trim($val); }); $this->assertEquals([ 'en' => 'Hello!' ], $obj->data()); } - /** - * @return void - */ - public function testJsonSerialize() + public function testJsonSerialize(): void { $obj = new Translation('Hello!', $this->localesManager()); $ret = json_encode($obj); diff --git a/packages/translator/tests/Charcoal/Translator/TranslatorAwareTraitTest.php b/packages/translator/tests/Charcoal/Translator/TranslatorAwareTraitTest.php index 5cb0eb161..32bb7b016 100644 --- a/packages/translator/tests/Charcoal/Translator/TranslatorAwareTraitTest.php +++ b/packages/translator/tests/Charcoal/Translator/TranslatorAwareTraitTest.php @@ -17,34 +17,24 @@ class TranslatorAwareTraitTest extends AbstractTestCase { /** * Tested Class. - * - * @var TranslatorAwareTrait */ - private $obj; + private \Charcoal\Translator\TranslatorAwareTrait $obj; /** * Set up the test. - * - * @return void */ protected function setUp(): void { $this->obj = $this->getMockForTrait(TranslatorAwareTrait::class); } - /** - * @return void - */ - public function testTranslatorWithoutSettingThrowsException() + public function testTranslatorWithoutSettingThrowsException(): void { $this->expectException(Exception::class); $this->callMethod($this->obj, 'translator'); } - /** - * @return void - */ - public function testSetTranslator() + public function testSetTranslator(): void { $translator = $this->getMockBuilder(Translator::class) ->disableOriginalConstructor() diff --git a/packages/translator/tests/Charcoal/Translator/TranslatorConfigTest.php b/packages/translator/tests/Charcoal/Translator/TranslatorConfigTest.php index 54f95853d..2e3c329bf 100644 --- a/packages/translator/tests/Charcoal/Translator/TranslatorConfigTest.php +++ b/packages/translator/tests/Charcoal/Translator/TranslatorConfigTest.php @@ -15,25 +15,18 @@ class TranslatorConfigTest extends AbstractTestCase { /** * Tested Class. - * - * @var TranslatorConfig */ - private $obj; + private \Charcoal\Translator\TranslatorConfig|array $obj; /** * Set up the test. - * - * @return void */ protected function setUp(): void { $this->obj = new TranslatorConfig(); } - /** - * @return void - */ - public function testDefaultsArrayAccess() + public function testDefaultsArrayAccess(): void { $this->assertEquals([ 'csv' ], $this->obj['loaders']); $this->assertContains('translations/', $this->obj['paths']); @@ -41,10 +34,7 @@ public function testDefaultsArrayAccess() $this->assertEquals('../cache/translator', $this->obj['cache_dir']); } - /** - * @return void - */ - public function testSetLoaders() + public function testSetLoaders(): void { $this->assertEquals([ 'csv' ], $this->obj->loaders()); @@ -56,46 +46,31 @@ public function testSetLoaders() $this->assertEquals([ 'php' ], $this->obj['loaders']); } - /** - * @return void - */ - public function testSetUnavailableLoaders() + public function testSetUnavailableLoaders(): void { $this->expectException(InvalidArgumentException::class); $this->obj['loaders'] = [ 'foo' ]; } - /** - * @return void - */ - public function testSetInvalidPaths() + public function testSetInvalidPaths(): void { $this->expectException(InvalidArgumentException::class); $this->obj['paths'] = [ false ]; } - /** - * @return void - */ - public function testSetInvalidDomainTranslations() + public function testSetInvalidDomainTranslations(): void { $this->expectException(InvalidArgumentException::class); $this->obj['translations'] = [ false ]; } - /** - * @return void - */ - public function testSetInvalidMessageTranslations() + public function testSetInvalidMessageTranslations(): void { $this->expectException(InvalidArgumentException::class); $this->obj['translations'] = [ [ false ] ]; } - /** - * @return void - */ - public function testSetDebug() + public function testSetDebug(): void { $this->assertFalse($this->obj->debug()); $ret = $this->obj->setDebug(true); @@ -106,10 +81,7 @@ public function testSetDebug() $this->assertFalse($this->obj['debug']); } - /** - * @return void - */ - public function testSetCacheDir() + public function testSetCacheDir(): void { $this->assertEquals('../cache/translator', $this->obj->cacheDir()); $ret = $this->obj->setCacheDir('foo'); @@ -120,10 +92,7 @@ public function testSetCacheDir() $this->assertEquals('bar', $this->obj['cache_dir']); } - /** - * @return void - */ - public function testSetInvalidCacheDir() + public function testSetInvalidCacheDir(): void { $this->expectException(InvalidArgumentException::class); $this->obj['cache_dir'] = false; diff --git a/packages/translator/tests/Charcoal/Translator/TranslatorTest.php b/packages/translator/tests/Charcoal/Translator/TranslatorTest.php index a99352c8d..c6ebc4ef5 100644 --- a/packages/translator/tests/Charcoal/Translator/TranslatorTest.php +++ b/packages/translator/tests/Charcoal/Translator/TranslatorTest.php @@ -30,22 +30,16 @@ class TranslatorTest extends AbstractTestCase /** * Tested Class. - * - * @var Translator */ - private $obj; + private \Charcoal\Translator\Translator $obj; /** * The language manager. - * - * @var LocalesManager */ - private $localesManager; + private ?\Charcoal\Translator\LocalesManager $localesManager = null; /** * Set up the test. - * - * @return void */ protected function setUp(): void { @@ -64,9 +58,6 @@ protected function setUp(): void $this->obj->addLoader('array', new ArrayLoader()); } - /** - * @return void - */ public static function setUpBeforeClass(): void { $path = realpath(__DIR__.'/../../../'.static::SYMFONY_CACHE_PATH); @@ -75,9 +66,6 @@ public static function setUpBeforeClass(): void } } - /** - * @return void - */ public static function tearDownAfterClass(): void { $path = realpath(__DIR__.'/../../../'.static::SYMFONY_CACHE_PATH.'.txt'); @@ -86,12 +74,9 @@ public static function tearDownAfterClass(): void } } - /** - * @return LocalesManager - */ - private function localesManager() + private function localesManager(): \Charcoal\Translator\LocalesManager { - if ($this->localesManager === null) { + if (!$this->localesManager instanceof \Charcoal\Translator\LocalesManager) { $this->localesManager = new LocalesManager([ 'locales' => [ 'en' => [ @@ -110,10 +95,7 @@ private function localesManager() return $this->localesManager; } - /** - * @return void - */ - public function testConstructorWithMessageSelector() + public function testConstructorWithMessageSelector(): void { $selector = new MessageSelector(); $translator = new Translator([ @@ -127,10 +109,7 @@ public function testConstructorWithMessageSelector() $this->assertSame($selector, $this->callMethod($translator, 'selector')); } - /** - * @return void - */ - public function testConstructorWithoutMessageSelector() + public function testConstructorWithoutMessageSelector(): void { $translator = new Translator([ 'locale' => 'en', @@ -143,10 +122,7 @@ public function testConstructorWithoutMessageSelector() $this->assertInstanceOf(MessageSelector::class, $this->callMethod($translator, 'selector')); } - /** - * @return void - */ - public function testConstructorWithMessageFormatter() + public function testConstructorWithMessageFormatter(): void { $formatter = new MessageFormatter(); $translator = new Translator([ @@ -160,10 +136,7 @@ public function testConstructorWithMessageFormatter() $this->assertSame($formatter, $this->callMethod($translator, 'formatter')); } - /** - * @return void - */ - public function testConstructorWithoutMessageFormatter() + public function testConstructorWithoutMessageFormatter(): void { $translator = new Translator([ 'locale' => 'en', @@ -176,20 +149,14 @@ public function testConstructorWithoutMessageFormatter() $this->assertInstanceOf(MessageFormatter::class, $this->callMethod($translator, 'formatter')); } - /** - * @return void - */ - public function testAvailableDomains() + public function testAvailableDomains(): void { $domains = $this->obj->availableDomains(); $this->assertIsArray($domains); $this->assertEquals([ 'messages' ], $domains); } - /** - * @return void - */ - public function testTranslation() + public function testTranslation(): void { $ret = $this->obj->translation('Hello!'); $this->assertInstanceOf(Translation::class, $ret); @@ -209,18 +176,16 @@ public function testTranslation() } /** - * @dataProvider invalidTransTests * * @param mixed $value The message ID. - * @return void */ - public function testTranslationInvalidValuesReturnNull($value) + #[\PHPUnit\Framework\Attributes\DataProvider('invalidTransTests')] + public function testTranslationInvalidValuesReturnNull(int|bool|string|array|null $value): void { $this->assertNull($this->obj->translation($value)); } /** - * @dataProvider validTransTests * * @param string $expected The expected translation. * @param string $id The message ID. @@ -228,11 +193,11 @@ public function testTranslationInvalidValuesReturnNull($value) * @param string $parameters An array of parameters for the message. * @param string $locale The locale to use. * @param string $domain The domain for the message. - * @return void */ - public function testTranslate($expected, $id, $translation, $parameters, $locale, $domain) + #[\PHPUnit\Framework\Attributes\DataProvider('validTransTests')] + public function testTranslate(string $expected, string|\Charcoal\Translator\Translation|\Charcoal\Tests\Translator\Mock\StringClass|array $id, string $translation, array $parameters, ?string $locale, string $domain): void { - if (!($id instanceof Translation || is_array($id)) && $locale) { + if (!$id instanceof Translation && !is_array($id) && $locale) { $this->obj->addResource('array', [ (string)$id => $translation ], $locale, $domain); } @@ -240,20 +205,16 @@ public function testTranslate($expected, $id, $translation, $parameters, $locale } /** - * @dataProvider invalidTransTests * * @param mixed $value The message ID. - * @return void */ - public function testTranslateInvalidValuesReturnEmptyString($value) + #[\PHPUnit\Framework\Attributes\DataProvider('invalidTransTests')] + public function testTranslateInvalidValuesReturnEmptyString(int|bool|string|array|null $value): void { $this->assertEquals('', $this->obj->translate($value)); } - /** - * @return void - */ - public function testTranslationChoice() + public function testTranslationChoice(): void { $ret = $this->obj->translationChoice('There is one apple|There is %count% apples', 2); $this->assertInstanceOf(Translation::class, $ret); @@ -273,18 +234,16 @@ public function testTranslationChoice() } /** - * @dataProvider invalidTransTests * * @param mixed $value The message ID. - * @return void */ - public function testTranslationChoiceInvalidValuesReturnNull($value) + #[\PHPUnit\Framework\Attributes\DataProvider('invalidTransTests')] + public function testTranslationChoiceInvalidValuesReturnNull(int|bool|string|array|null $value): void { $this->assertNull($this->obj->translationChoice($value, 1)); } /** - * @dataProvider validTransChoiceTests * * @param string $expected The expected translation. * @param string $id The message ID. @@ -293,11 +252,11 @@ public function testTranslationChoiceInvalidValuesReturnNull($value) * @param string $parameters An array of parameters for the message. * @param string $locale The locale to use. * @param string $domain The domain for the message. - * @return void */ - public function testTranslateChoice($expected, $id, $translation, $number, $parameters, $locale, $domain) + #[\PHPUnit\Framework\Attributes\DataProvider('validTransChoiceTests')] + public function testTranslateChoice(string $expected, string|\Charcoal\Translator\Translation|\Charcoal\Tests\Translator\Mock\StringClass|array $id, string $translation, int $number, array $parameters, ?string $locale, string $domain): void { - if (!($id instanceof Translation || is_array($id)) && $locale) { + if (!$id instanceof Translation && !is_array($id) && $locale) { $this->obj->addResource('array', [ (string)$id => $translation ], $locale, $domain); } @@ -305,59 +264,42 @@ public function testTranslateChoice($expected, $id, $translation, $number, $para } /** - * @dataProvider invalidTransTests * * @param mixed $value The message ID. - * @return void */ - public function testTranslateChoiceInvalidValuesReturnEmptyString($value) + #[\PHPUnit\Framework\Attributes\DataProvider('invalidTransTests')] + public function testTranslateChoiceInvalidValuesReturnEmptyString(int|bool|string|array|null $value): void { $this->assertEquals('', $this->obj->translateChoice($value, 1)); } - /** - * @return void - */ - public function testSetLocaleSetLocalesManagerCurrentLanguage() + public function testSetLocaleSetLocalesManagerCurrentLanguage(): void { $this->obj->setLocale('fr'); $this->assertEquals('fr', $this->localesManager()->currentLocale()); } - /** - * @return void - */ - public function testLocales() + public function testLocales(): void { $this->assertArrayHasKey('en', $this->obj->locales()); $this->assertArrayHasKey('fr', $this->obj->locales()); $this->assertArrayNotHasKey('jp', $this->obj->locales()); } - /** - * @return void - */ - public function testAvailableLocales() + public function testAvailableLocales(): void { $this->assertEquals([ 'en', 'fr' ], $this->obj->availableLocales()); } - /** - * @return void - */ - public function testInvalidArrayTranslation() + public function testInvalidArrayTranslation(): void { $method = $this->getMethod($this->obj, 'isValidTranslation'); - $method->setAccessible(true); $this->assertFalse($method->invokeArgs($this->obj, [ [ 0 => 'Hello!' ] ])); $this->assertFalse($method->invokeArgs($this->obj, [ [ 'hello' => 0 ] ])); } - /** - * @return void - */ - public function testHasTranslation() + public function testHasTranslation(): void { $data = [ 'en' => [ @@ -395,10 +337,8 @@ public function testHasTranslation() /** * @link https://github.com/symfony/translation/blob/v3.2.3/Tests/TranslatorTest.php - * - * @return array */ - public function validTransTests() + public function validTransTests(): array { // phpcs:disable Generic.Files.LineLength.TooLong return [ @@ -411,10 +351,7 @@ public function validTransTests() // phpcs:enable } - /** - * @return array - */ - public function invalidTransTests() + public static function invalidTransTests(): array { return [ 'null' => [ null ], @@ -431,10 +368,8 @@ public function invalidTransTests() /** * @link https://github.com/symfony/translation/blob/v3.2.3/Tests/TranslatorTest.php - * - * @return array */ - public function validTransChoiceTests() + public function validTransChoiceTests(): array { // phpcs:disable Generic.Files.LineLength.TooLong return [ diff --git a/packages/ui/composer.json b/packages/ui/composer.json index b1c0ab542..a17157df7 100644 --- a/packages/ui/composer.json +++ b/packages/ui/composer.json @@ -1,8 +1,15 @@ { - "type": "library", "name": "charcoal/ui", "description": "UI tools (Dashboard, Layout, Form and Menu)", - "keywords": ["charcoal", "ui", "dashboard", "layout", "form", "control", "menu"], + "keywords": [ + "charcoal", + "ui", + "dashboard", + "layout", + "form", + "control", + "menu" + ], "homepage": "https://charcoal.locomotive.ca", "license": "MIT", "authors": [ @@ -15,13 +22,9 @@ "homepage": "https://locomotive.ca" } ], - "extra": { - "branch-alias": { - "dev-main": "5.x-dev" - } - }, + "type": "library", "require": { - "php": "^7.4 || ^8.0", + "php": "^8.3", "psr/log": "^1.0", "charcoal/config": "^5.1", "charcoal/factory": "^5.1", @@ -30,7 +33,7 @@ "charcoal/user": "^5.1" }, "require-dev": { - "phpunit/phpunit": "^9.5", + "phpunit/phpunit": "^12.5", "squizlabs/php_codesniffer": "^3.5", "php-coveralls/php-coveralls": "^2.2", "mustache/mustache": "^2.11", @@ -38,9 +41,6 @@ "pimple/pimple": "^3.0", "charcoal/app": "^5.1" }, - "suggest": { - "pimple/pimple": "DI Container to register the various Service Providers." - }, "autoload": { "psr-4": { "Charcoal\\": "src/Charcoal/" @@ -51,8 +51,10 @@ "Charcoal\\Tests\\": "tests/Charcoal" } }, - "replace": { - "locomotivemtl/charcoal-ui": "*" + "extra": { + "branch-alias": { + "dev-main": "5.x-dev" + } }, "scripts": { "test": [ @@ -68,6 +70,12 @@ "phpcbf": "php vendor/bin/phpcbf -ps --colors src/ tests/", "phpunit": "php vendor/bin/phpunit --coverage-text" }, + "suggest": { + "pimple/pimple": "DI Container to register the various Service Providers." + }, + "replace": { + "locomotivemtl/charcoal-ui": "*" + }, "minimum-stability": "dev", "prefer-stable": true } diff --git a/packages/ui/src/Charcoal/Ui/AbstractUiItem.php b/packages/ui/src/Charcoal/Ui/AbstractUiItem.php index 664c1aa00..142537acc 100644 --- a/packages/ui/src/Charcoal/Ui/AbstractUiItem.php +++ b/packages/ui/src/Charcoal/Ui/AbstractUiItem.php @@ -46,7 +46,7 @@ abstract class AbstractUiItem extends AbstractEntity implements * * @param array $data The class depdendencies. */ - public function __construct(array $data = null) + public function __construct(?array $data = null) { if (!isset($data['logger'])) { $data['logger'] = new NullLogger(); diff --git a/packages/ui/src/Charcoal/Ui/ConditionalizableInterface.php b/packages/ui/src/Charcoal/Ui/ConditionalizableInterface.php index a4270cf3d..9f4809adc 100644 --- a/packages/ui/src/Charcoal/Ui/ConditionalizableInterface.php +++ b/packages/ui/src/Charcoal/Ui/ConditionalizableInterface.php @@ -1,5 +1,7 @@ resolvedCondition)) { - if (!isset($this->condition)) { - $this->resolvedCondition = true; - } else { - $this->resolvedCondition = $this->parseConditionalLogic( - $this->condition() - ); - } + $this->resolvedCondition = isset($this->condition) ? $this->parseConditionalLogic( + $this->condition() + ) : true; } return $this->resolvedCondition; @@ -98,7 +94,7 @@ final protected function parseConditionalLogic($condition) $result = $this->resolveConditionalLogic($condition); - return (($not === true) ? !$result : $result); + return (($not) ? !$result : $result); } /** @@ -107,38 +103,37 @@ final protected function parseConditionalLogic($condition) * @todo Simplify logic by moving `form()` method lookup to relevant form widget. * * @param callable|string $condition The callable or renderable condition. - * @return boolean */ - protected function resolveConditionalLogic($condition) + protected function resolveConditionalLogic($condition): bool { if (is_callable([ $this, $condition ])) { - return !!$this->{$condition}(); + return (bool) $this->{$condition}(); } if (is_callable($condition)) { - return !!$condition(); + return (bool) $condition(); } if (is_callable([ $this, 'form' ])) { $form = $this->form(); if (is_callable([ $form, $condition ])) { - return !!$form->{$condition}(); + return (bool) $form->{$condition}(); } if (is_callable([ $form, 'obj' ])) { $obj = $form->obj(); if (is_callable([ $obj, $condition ])) { - return !!$obj->{$condition}(); + return (bool) $obj->{$condition}(); } if (($obj instanceof ViewableInterface) && ($obj->view() instanceof ViewInterface)) { - return !!$obj->renderTemplate($condition); + return (bool) $obj->renderTemplate($condition); } } } - return !!$condition; + return (bool) $condition; } } diff --git a/packages/ui/src/Charcoal/Ui/Dashboard/AbstractDashboard.php b/packages/ui/src/Charcoal/Ui/Dashboard/AbstractDashboard.php index 6ffb025d9..9606fb423 100644 --- a/packages/ui/src/Charcoal/Ui/Dashboard/AbstractDashboard.php +++ b/packages/ui/src/Charcoal/Ui/Dashboard/AbstractDashboard.php @@ -25,7 +25,7 @@ abstract class AbstractDashboard extends AbstractUiItem implements * * @param array|\ArrayAccess $data The class dependencies. */ - public function __construct($data = null) + public function __construct(?array $data = null) { parent::__construct($data); diff --git a/packages/ui/src/Charcoal/Ui/Dashboard/DashboardBuilder.php b/packages/ui/src/Charcoal/Ui/Dashboard/DashboardBuilder.php index 3fe678754..c55112816 100644 --- a/packages/ui/src/Charcoal/Ui/Dashboard/DashboardBuilder.php +++ b/packages/ui/src/Charcoal/Ui/Dashboard/DashboardBuilder.php @@ -18,30 +18,14 @@ class DashboardBuilder */ public const DEFAULT_TYPE = 'charcoal/ui/dashboard/generic'; - /** - * Store the dashboard factory instance. - * - * @var FactoryInterface - */ - protected $factory; - - /** - * Store the dependency-injection container to fulfill the required services. - * - * @var Container $container - */ - protected $container; - /** * Return a new dashboard builder. * * @param FactoryInterface $factory A dashboard factory. * @param Container $container The DI container. */ - public function __construct(FactoryInterface $factory, Container $container) + public function __construct(protected \Charcoal\Factory\FactoryInterface $factory, protected \Pimple\Container $container) { - $this->factory = $factory; - $this->container = $container; } /** @@ -52,7 +36,7 @@ public function __construct(FactoryInterface $factory, Container $container) */ public function build($options) { - $objType = isset($options['type']) ? $options['type'] : self::DEFAULT_TYPE; + $objType = $options['type'] ?? self::DEFAULT_TYPE; $obj = $this->factory->create($objType); $obj->setData($options); diff --git a/packages/ui/src/Charcoal/Ui/Dashboard/DashboardConfig.php b/packages/ui/src/Charcoal/Ui/Dashboard/DashboardConfig.php index ecd2a8d55..245d4b7d6 100644 --- a/packages/ui/src/Charcoal/Ui/Dashboard/DashboardConfig.php +++ b/packages/ui/src/Charcoal/Ui/Dashboard/DashboardConfig.php @@ -1,5 +1,7 @@ widgets; uasort($widgets, [ $this, 'sortItemsByPriority' ]); - $widgetCallback = isset($widgetCallback) ? $widgetCallback : $this->widgetCallback; + $widgetCallback ??= $this->widgetCallback; foreach ($widgets as $widget) { if (isset($widget['permissions']) && $this instanceof AuthAwareInterface) { $widget->setActive($this->hasPermissions($widget['permissions'])); @@ -185,20 +185,16 @@ public function widgets(callable $widgetCallback = null) /** * Determine if the dashboard has any widgets. - * - * @return boolean */ - public function hasWidgets() + public function hasWidgets(): bool { return ($this->numWidgets() > 0); } /** * Count the number of widgets attached to the dashboard. - * - * @return integer */ - public function numWidgets() + public function numWidgets(): int { return count($this->widgets); } diff --git a/packages/ui/src/Charcoal/Ui/Dashboard/GenericDashboard.php b/packages/ui/src/Charcoal/Ui/Dashboard/GenericDashboard.php index 3f9108db4..de3840f77 100644 --- a/packages/ui/src/Charcoal/Ui/Dashboard/GenericDashboard.php +++ b/packages/ui/src/Charcoal/Ui/Dashboard/GenericDashboard.php @@ -1,5 +1,7 @@ factory = $factory; } /** @@ -51,7 +43,7 @@ public function __construct(FactoryInterface $factory) */ public function build($options) { - $objType = isset($options['type']) ? $options['type'] : self::DEFAULT_TYPE; + $objType = $options['type'] ?? self::DEFAULT_TYPE; $obj = $this->factory->create($objType); $obj->setData($options); diff --git a/packages/ui/src/Charcoal/Ui/Form/FormConfig.php b/packages/ui/src/Charcoal/Ui/Form/FormConfig.php index ebcd0f419..106d6b090 100644 --- a/packages/ui/src/Charcoal/Ui/Form/FormConfig.php +++ b/packages/ui/src/Charcoal/Ui/Form/FormConfig.php @@ -1,5 +1,7 @@ defaultGroupType(); - } + $type = $data['type'] ?? $this->defaultGroupType(); $group = $this->formGroupFactory()->create($type); $group->setForm($this->formWidget()); @@ -363,13 +359,12 @@ protected function createFormGroup(array $data = null) * @param FormGroupInterface $group The form group to update. * @param array|null $groupData Optional. The new group data to apply. * @param string|null $groupIdent Optional. The new group identifier. - * @return FormGroupInterface */ protected function updateFormGroup( FormGroupInterface $group, - array $groupData = null, + ?array $groupData = null, $groupIdent = null - ) { + ): FormGroupInterface { $group->setForm($this->formWidget()); if ($groupData !== null) { @@ -385,10 +380,8 @@ protected function updateFormGroup( /** * Retrieve the default form group class name. - * - * @return string */ - public function defaultGroupType() + public function defaultGroupType(): string { return 'charcoal/ui/form-group/generic'; } @@ -399,12 +392,12 @@ public function defaultGroupType() * @param callable $groupCallback Optional callback applied to each form group. * @return FormGroupInterface[]|Generator */ - public function groups(callable $groupCallback = null) + public function groups(?callable $groupCallback = null) { $groups = $this->groups; uasort($groups, [ $this, 'sortItemsByPriority' ]); - $groupCallback = (isset($groupCallback) ? $groupCallback : $this->groupCallback); + $groupCallback ??= $this->groupCallback; $groups = $this->finalizeFormGroups($groups); @@ -432,7 +425,7 @@ public function groups(callable $groupCallback = null) * @param array|FormGroupInterface[] $groups Form groups to finalize. * @return array|FormGroupInterface[] */ - protected function finalizeFormGroups($groups) + protected function finalizeFormGroups($groups): array { $out = []; @@ -441,10 +434,8 @@ protected function finalizeFormGroups($groups) continue; } - if ($group instanceof ConditionalizableInterface) { - if (!$group->resolvedCondition()) { - continue; - } + if ($group instanceof ConditionalizableInterface && !$group->resolvedCondition()) { + continue; } // Test formGroup vs. ACL roles @@ -465,35 +456,33 @@ protected function finalizeFormGroups($groups) } } - if ($group->rawConditionalLogic()) { - if (is_callable([$this, 'obj'])) { - foreach ($group->rawConditionalLogic() as $logic) { - $valid = true; - $value = $this->obj()->get($logic['property']); - - switch ($logic['operator']) { - case '!==': - case '!=': - case '!': - case 'not': - if ($value == $logic['value']) { - $valid = false; - } - break; - default: - case '"==="': - case '"=="': - case '"="': - case '"is"': - if ($value != $logic['value']) { - $valid = false; - } - break; - } - - if (!$valid) { - $group->setConditionalLogicUnmet(true); - } + if ($group->rawConditionalLogic() && is_callable([$this, 'obj'])) { + foreach ($group->rawConditionalLogic() as $logic) { + $valid = true; + $value = $this->obj()->get($logic['property']); + + switch ($logic['operator']) { + case '!==': + case '!=': + case '!': + case 'not': + if ($value == $logic['value']) { + $valid = false; + } + break; + default: + case '"==="': + case '"=="': + case '"="': + case '"is"': + if ($value != $logic['value']) { + $valid = false; + } + break; + } + + if (!$valid) { + $group->setConditionalLogicUnmet(true); } } } @@ -506,10 +495,8 @@ protected function finalizeFormGroups($groups) /** * Determine if the form has any groups. - * - * @return boolean */ - public function hasGroups() + public function hasGroups(): bool { return ($this->numGroups() > 0); } @@ -519,9 +506,8 @@ public function hasGroups() * * @param string $groupIdent The group identifier to look up. * @throws InvalidArgumentException If the group identifier is invalid. - * @return boolean */ - public function hasGroup($groupIdent) + public function hasGroup($groupIdent): bool { if (!is_string($groupIdent)) { throw new InvalidArgumentException( @@ -534,10 +520,8 @@ public function hasGroup($groupIdent) /** * Count the number of form groups. - * - * @return integer */ - public function numGroups() + public function numGroups(): int { return count($this->groups); } @@ -584,10 +568,8 @@ public function groupDisplayMode() /** * Determine if content groups are to be displayed as tabbable panes. - * - * @return boolean */ - public function isTabbable() + public function isTabbable(): bool { return ($this->groupDisplayMode() === 'tab'); } diff --git a/packages/ui/src/Charcoal/Ui/Form/GenericForm.php b/packages/ui/src/Charcoal/Ui/Form/GenericForm.php index 4341ad9d7..3b218cb8d 100644 --- a/packages/ui/src/Charcoal/Ui/Form/GenericForm.php +++ b/packages/ui/src/Charcoal/Ui/Form/GenericForm.php @@ -1,5 +1,7 @@ groups; uasort($groups, [ $this, 'sortItemsByPriority' ]); - $inputCallback = isset($inputCallback) ? $inputCallback : $this->inputCallback; + $inputCallback ??= $this->inputCallback; foreach ($inputs as $input) { if (!$input->l10nMode()) { $input->setL10nMode($this->l10nMode()); @@ -234,20 +234,16 @@ public function inputs(callable $inputCallback = null) /** * Wether this group contains any inputs. - * - * @return boolean */ - public function hasInputs() + public function hasInputs(): bool { return (count($this->inputs) > 0); } /** * Get the number of inputs in this group. - * - * @return integer */ - public function numInputs() + public function numInputs(): int { return count($this->inputs); } diff --git a/packages/ui/src/Charcoal/Ui/FormGroup/GenericFormGroup.php b/packages/ui/src/Charcoal/Ui/FormGroup/GenericFormGroup.php index ab12311bf..3501bc5fe 100644 --- a/packages/ui/src/Charcoal/Ui/FormGroup/GenericFormGroup.php +++ b/packages/ui/src/Charcoal/Ui/FormGroup/GenericFormGroup.php @@ -1,5 +1,7 @@ factory = $factory; - $this->container = $container; } /** @@ -53,7 +37,7 @@ public function __construct(FactoryInterface $factory, Container $container) public function build($options) { $container = $this->container; - $objType = isset($options['type']) ? $options['type'] : self::DEFAULT_TYPE; + $objType = $options['type'] ?? self::DEFAULT_TYPE; $obj = $this->factory->create($objType, [ 'logger' => $container['logger'], diff --git a/packages/ui/src/Charcoal/Ui/FormInput/FormInputConfig.php b/packages/ui/src/Charcoal/Ui/FormInput/FormInputConfig.php index 8cfbdec53..abd820524 100644 --- a/packages/ui/src/Charcoal/Ui/FormInput/FormInputConfig.php +++ b/packages/ui/src/Charcoal/Ui/FormInput/FormInputConfig.php @@ -1,5 +1,7 @@ factory = $factory; - $this->container = $container; } /** @@ -54,7 +38,7 @@ public function __construct(FactoryInterface $factory, Container $container) public function build($options) { $container = $this->container; - $objType = isset($options['type']) ? $options['type'] : self::DEFAULT_TYPE; + $objType = $options['type'] ?? self::DEFAULT_TYPE; $obj = $this->factory->create($objType, [ 'logger' => $container['logger'], diff --git a/packages/ui/src/Charcoal/Ui/Layout/LayoutConfig.php b/packages/ui/src/Charcoal/Ui/Layout/LayoutConfig.php index d438062e0..9f860cc8c 100644 --- a/packages/ui/src/Charcoal/Ui/Layout/LayoutConfig.php +++ b/packages/ui/src/Charcoal/Ui/Layout/LayoutConfig.php @@ -1,5 +1,7 @@ structure(); return count($structure); @@ -90,9 +88,8 @@ public function numRows() * Get the row index at a certain position * * @param integer $position Optional. Forced position. - * @return integer|null */ - public function rowIndex($position = null) + public function rowIndex($position = null): ?int { if ($position === null) { $position = $this->position(); @@ -100,7 +97,7 @@ public function rowIndex($position = null) $i = 0; $p = 0; - foreach ($this->structure as $row_ident => $row) { + foreach ($this->structure as $row) { $numCells = count($row['columns']); $p += $numCells; if ($p > $position) { @@ -139,7 +136,7 @@ public function rowData($position = null) * @param integer $position Optional. Forced position. * @return integer|null */ - public function rowNumColumns($position = null) + public function rowNumColumns($position = null): null|float|int { if ($position === null) { $position = $this->position(); @@ -161,7 +158,7 @@ public function rowNumColumns($position = null) * @param integer $position Optional. Forced position. * @return integer */ - public function rowNumCells($position = null) + public function rowNumCells($position = null): ?int { if ($position === null) { $position = $this->position(); @@ -169,8 +166,7 @@ public function rowNumCells($position = null) // Get the data ta position $row = $this->rowData($position); - $numCells = isset($row['columns']) ? count($row['columns']) : null; - return $numCells; + return isset($row['columns']) ? count($row['columns']) : null; } /** @@ -179,7 +175,7 @@ public function rowNumCells($position = null) * @param integer $position Optional. Forced position. * @return integer */ - public function rowFirstCellIndex($position = null) + public function rowFirstCellIndex($position = null): ?int { if ($position === null) { $position = $this->position(); @@ -214,7 +210,7 @@ public function rowFirstCellIndex($position = null) * @param integer $position Optional. Forced position. * @return integer */ - public function cellRowIndex($position = null) + public function cellRowIndex($position = null): int|float { if ($position === null) { $position = $this->position(); @@ -226,10 +222,8 @@ public function cellRowIndex($position = null) /** * Get the total number of cells, in all rows - * - * @return integer */ - public function numCellsTotal() + public function numCellsTotal(): int { $numCells = 0; foreach ($this->structure as $row) { @@ -243,9 +237,8 @@ public function numCellsTotal() * Get the span number (in # of columns) of the current cell * * @param integer $position Optional. Forced position. - * @return integer|null */ - public function cellSpan($position = null) + public function cellSpan($position = null): ?int { $row = $this->rowData($position); $cellIndex = $this->cellRowIndex($position); @@ -260,7 +253,7 @@ public function cellSpan($position = null) * @param integer $position Optional. Forced position. * @return integer */ - public function cellSpanBy12($position = null) + public function cellSpanBy12($position = null): null|float|int { $numColumns = $this->rowNumColumns($position); if (!$numColumns) { @@ -273,9 +266,8 @@ public function cellSpanBy12($position = null) * Get wether or not the current cell starts a row (is the first one on the row) * * @param integer $position Optional. Forced position. - * @return boolean */ - public function cellStartsRow($position = null) + public function cellStartsRow($position = null): bool { if ($position === null) { $position = $this->position(); @@ -288,9 +280,8 @@ public function cellStartsRow($position = null) * Get wether or not the current cell ends a row (is the last one on the row) * * @param integer $position Optional. Forced position. - * @return boolean */ - public function cellEndsRow($position = null) + public function cellEndsRow($position = null): bool { if ($position === null) { $position = $this->position(); @@ -302,18 +293,12 @@ public function cellEndsRow($position = null) return ($cellNum >= ($numCells - 1)); } - /** - * @return string - */ - public function start() + public function start(): string { return ''; } - /** - * @return string - */ - public function end() + public function end(): string { $this->position++; return ''; diff --git a/packages/ui/src/Charcoal/Ui/Menu/AbstractMenu.php b/packages/ui/src/Charcoal/Ui/Menu/AbstractMenu.php index c2cb632dc..d78b589ed 100644 --- a/packages/ui/src/Charcoal/Ui/Menu/AbstractMenu.php +++ b/packages/ui/src/Charcoal/Ui/Menu/AbstractMenu.php @@ -34,17 +34,15 @@ abstract class AbstractMenu extends AbstractUiItem implements /** * Store a menu builder instance. - * - * @var MenuItemBuilder $menuItemBuilder */ - private $menuItemBuilder; + private \Charcoal\Ui\MenuItem\MenuItemBuilder $menuItemBuilder; /** * Return a new menu. * * @param array|\ArrayAccess $data Class dependencies. */ - public function __construct($data) + public function __construct(?array $data) { parent::__construct($data); @@ -126,12 +124,12 @@ public function addItem($item, $ident = null) * @param callable $itemCallback Optional. Item callback. * @return MenuItemInterface[] */ - public function items(callable $itemCallback = null) + public function items(?callable $itemCallback = null) { $items = $this->items; - uasort($items, [ $this, 'sortItemsByPriority' ]); + uasort($items, $this->sortItemsByPriority(...)); - $itemCallback = isset($itemCallback) ? $itemCallback : $this->itemCallback; + $itemCallback ??= $this->itemCallback; foreach ($items as $item) { if ($itemCallback) { $itemCallback($item); diff --git a/packages/ui/src/Charcoal/Ui/Menu/GenericMenu.php b/packages/ui/src/Charcoal/Ui/Menu/GenericMenu.php index d9efbc67e..a31c2dd63 100644 --- a/packages/ui/src/Charcoal/Ui/Menu/GenericMenu.php +++ b/packages/ui/src/Charcoal/Ui/Menu/GenericMenu.php @@ -1,5 +1,7 @@ factory = $factory; - $this->container = $container; } /** @@ -53,8 +37,7 @@ public function __construct(FactoryInterface $factory, Container $container) */ public function build($options) { - $container = $this->container; - $objType = isset($options['type']) ? $options['type'] : self::DEFAULT_TYPE; + $objType = $options['type'] ?? self::DEFAULT_TYPE; $obj = $this->factory->create($objType); $obj->setData($options); diff --git a/packages/ui/src/Charcoal/Ui/Menu/MenuConfig.php b/packages/ui/src/Charcoal/Ui/Menu/MenuConfig.php index 33ce4a324..63c8c85a7 100644 --- a/packages/ui/src/Charcoal/Ui/Menu/MenuConfig.php +++ b/packages/ui/src/Charcoal/Ui/Menu/MenuConfig.php @@ -1,5 +1,7 @@ url()); + return (bool) $this->url(); } /** @@ -205,12 +205,12 @@ public function addChild($child) * @param callable $childCallback Optional callback. * @return MenuItemInterface[] */ - public function children(callable $childCallback = null) + public function children(?callable $childCallback = null) { $children = $this->children; uasort($children, ['self', 'sortChildrenByPrioriy']); - $childCallback = isset($childCallback) ? $childCallback : $this->childCallback; + $childCallback ??= $this->childCallback; foreach ($children as $child) { if ($childCallback) { $childCallback($child); diff --git a/packages/ui/src/Charcoal/Ui/MenuItem/GenericMenuItem.php b/packages/ui/src/Charcoal/Ui/MenuItem/GenericMenuItem.php index 075073bf8..b0abcca16 100644 --- a/packages/ui/src/Charcoal/Ui/MenuItem/GenericMenuItem.php +++ b/packages/ui/src/Charcoal/Ui/MenuItem/GenericMenuItem.php @@ -1,5 +1,7 @@ factory = $factory; - $this->container = $container; } /** @@ -53,8 +37,7 @@ public function __construct(FactoryInterface $factory, Container $container) */ public function build($options) { - $container = $this->container; - $objType = isset($options['type']) ? $options['type'] : self::DEFAULT_TYPE; + $objType = $options['type'] ?? self::DEFAULT_TYPE; $obj = $this->factory->create($objType); $obj->setData($options); diff --git a/packages/ui/src/Charcoal/Ui/MenuItem/MenuItemConfig.php b/packages/ui/src/Charcoal/Ui/MenuItem/MenuItemConfig.php index e56673828..d00cb9af9 100644 --- a/packages/ui/src/Charcoal/Ui/MenuItem/MenuItemConfig.php +++ b/packages/ui/src/Charcoal/Ui/MenuItem/MenuItemConfig.php @@ -1,5 +1,7 @@ registerDashboardServices($container); } /** * @param Container $container A Pimple DI container. - * @return void */ - private function registerDashboardServices(Container $container) + private function registerDashboardServices(Container $container): void { /** * @param Container $container A Pimple DI container. * @return LayoutFactory */ - $container['dashboard/factory'] = function (Container $container) { - return new Factory([ - 'base_class' => DashboardInterface::class, - 'default_class' => GenericDashboard::class, - 'arguments' => [ - [ - 'container' => $container, - 'logger' => $container['logger'], - 'view' => $container['view'], - 'widget_builder' => $container['widget/builder'], - 'layout_builder' => $container['layout/builder'], - ], + $container['dashboard/factory'] = (fn(Container $container): \Charcoal\Factory\GenericFactory => new Factory([ + 'base_class' => DashboardInterface::class, + 'default_class' => GenericDashboard::class, + 'arguments' => [ + [ + 'container' => $container, + 'logger' => $container['logger'], + 'view' => $container['view'], + 'widget_builder' => $container['widget/builder'], + 'layout_builder' => $container['layout/builder'], ], - 'resolver_options' => [ - 'suffix' => 'Dashboard', - ], - ]); - }; + ], + 'resolver_options' => [ + 'suffix' => 'Dashboard', + ], + ])); /** * @param Container $container A Pimple DI container. * @return LayoutBuilder */ - $container['dashboard/builder'] = function (Container $container) { + $container['dashboard/builder'] = function (Container $container): \Charcoal\Ui\Dashboard\DashboardBuilder { $dashboardFactory = $container['dashboard/factory']; - $dashboardBuilder = new DashboardBuilder($dashboardFactory, $container); - return $dashboardBuilder; + return new DashboardBuilder($dashboardFactory, $container); }; } } diff --git a/packages/ui/src/Charcoal/Ui/ServiceProvider/FormServiceProvider.php b/packages/ui/src/Charcoal/Ui/ServiceProvider/FormServiceProvider.php index 1498d42bf..cf495dda4 100644 --- a/packages/ui/src/Charcoal/Ui/ServiceProvider/FormServiceProvider.php +++ b/packages/ui/src/Charcoal/Ui/ServiceProvider/FormServiceProvider.php @@ -25,9 +25,8 @@ class FormServiceProvider implements ServiceProviderInterface { /** * @param Container $container A Pimple DI container. - * @return void */ - public function register(Container $container) + public function register(Container $container): void { $this->registerFormServices($container); $this->registerFormGroupServices($container); @@ -36,99 +35,88 @@ public function register(Container $container) /** * @param Container $container A Pimple DI container. - * @return void */ - public function registerFormServices(Container $container) + public function registerFormServices(Container $container): void { /** * @param Container $container A Pimple DI container. * @return \Charcoal\Factory\FactoryInterface */ - $container['form/factory'] = function (Container $container) { - return new Factory([ - 'base_class' => FormInterface::class, - 'default_class' => GenericForm::class, - 'arguments' => [ - [ - 'container' => $container, - 'logger' => $container['logger'], - 'view' => $container['view'], - 'layout_builder' => $container['layout/builder'], - 'form_group_factory' => $container['form/group/factory'], - ], + $container['form/factory'] = (fn(Container $container): \Charcoal\Factory\GenericFactory => new Factory([ + 'base_class' => FormInterface::class, + 'default_class' => GenericForm::class, + 'arguments' => [ + [ + 'container' => $container, + 'logger' => $container['logger'], + 'view' => $container['view'], + 'layout_builder' => $container['layout/builder'], + 'form_group_factory' => $container['form/group/factory'], ], - ]); - }; + ], + ])); /** * @param Container $container A Pimple DI container. * @return FormBuilder */ - $container['form/builder'] = function (Container $container) { + $container['form/builder'] = function (Container $container): \Charcoal\Ui\Form\FormBuilder { $formFactory = $container['form/factory']; - $formBuilder = new FormBuilder($formFactory); - return $formBuilder; + return new FormBuilder($formFactory); }; } /** * @param Container $container A Pimple DI container. - * @return void */ - public function registerFormGroupServices(Container $container) + public function registerFormGroupServices(Container $container): void { /** * @param Container $container A Pimple DI container. * @return \Charcoal\Factory\FactoryInterface */ - $container['form/group/factory'] = function (Container $container) { - return new Factory([ - 'base_class' => FormGroupInterface::class, - 'default_class' => GenericFormGroup::class, - 'arguments' => [ - [ - 'container' => $container, - 'logger' => $container['logger'], - 'view' => $container['view'], - 'layout_builder' => $container['layout/builder'], - 'form_input_builder' => $container['form/input/builder'], - ], - ], - 'resolver_options' => [ - 'suffix' => 'FormGroup', + $container['form/group/factory'] = (fn(Container $container): \Charcoal\Factory\GenericFactory => new Factory([ + 'base_class' => FormGroupInterface::class, + 'default_class' => GenericFormGroup::class, + 'arguments' => [ + [ + 'container' => $container, + 'logger' => $container['logger'], + 'view' => $container['view'], + 'layout_builder' => $container['layout/builder'], + 'form_input_builder' => $container['form/input/builder'], ], - ]); - }; + ], + 'resolver_options' => [ + 'suffix' => 'FormGroup', + ], + ])); } /** * @param Container $container A Pimple DI container. - * @return void */ - public function registerFormInputServices(Container $container) + public function registerFormInputServices(Container $container): void { /** * @param Container $container A Pimple DI container. * @return \Charcoal\Factory\FactoryInterface */ - $container['form/input/factory'] = function () { - return new Factory([ - 'base_class' => FormInputInterface::class, - 'default_class' => GenericFormInput::class, - 'resolver_options' => [ - 'suffix' => 'FormInput', - ], - ]); - }; + $container['form/input/factory'] = (fn(): \Charcoal\Factory\GenericFactory => new Factory([ + 'base_class' => FormInputInterface::class, + 'default_class' => GenericFormInput::class, + 'resolver_options' => [ + 'suffix' => 'FormInput', + ], + ])); /** * @param Container $container A Pimple DI container. * @return FormInputBuilder */ - $container['form/input/builder'] = function (Container $container) { + $container['form/input/builder'] = function (Container $container): \Charcoal\Ui\FormInput\FormInputBuilder { $formInputFactory = $container['form/input/factory']; - $formInputBuilder = new FormInputBuilder($formInputFactory, $container); - return $formInputBuilder; + return new FormInputBuilder($formInputFactory, $container); }; } } diff --git a/packages/ui/src/Charcoal/Ui/ServiceProvider/LayoutServiceProvider.php b/packages/ui/src/Charcoal/Ui/ServiceProvider/LayoutServiceProvider.php index c01b972bf..df98a5195 100644 --- a/packages/ui/src/Charcoal/Ui/ServiceProvider/LayoutServiceProvider.php +++ b/packages/ui/src/Charcoal/Ui/ServiceProvider/LayoutServiceProvider.php @@ -16,37 +16,30 @@ class LayoutServiceProvider implements ServiceProviderInterface { /** * @param Container $container A Pimple DI container. - * @return void */ - public function register(Container $container) + public function register(Container $container): void { $this->registerLayoutServices($container); } /** * @param Container $container A Pimple DI container. - * @return void */ - private function registerLayoutServices(Container $container) + private function registerLayoutServices(Container $container): void { /** * @param Container $container A Pimple DI container. * @return LayoutFactory */ - $container['layout/factory'] = function () { - - $layoutFactory = new LayoutFactory(); - return $layoutFactory; - }; + $container['layout/factory'] = (fn(): \Charcoal\Ui\Layout\LayoutFactory => new LayoutFactory()); /** * @param Container $container A Pimple DI container. * @return LayoutBuilder */ - $container['layout/builder'] = function (Container $container) { + $container['layout/builder'] = function (Container $container): \Charcoal\Ui\Layout\LayoutBuilder { $layoutFactory = $container['layout/factory']; - $layoutBuilder = new LayoutBuilder($layoutFactory, $container); - return $layoutBuilder; + return new LayoutBuilder($layoutFactory, $container); }; } } diff --git a/packages/ui/src/Charcoal/Ui/ServiceProvider/MenuServiceProvider.php b/packages/ui/src/Charcoal/Ui/ServiceProvider/MenuServiceProvider.php index eb2a08c18..0d8f910de 100644 --- a/packages/ui/src/Charcoal/Ui/ServiceProvider/MenuServiceProvider.php +++ b/packages/ui/src/Charcoal/Ui/ServiceProvider/MenuServiceProvider.php @@ -18,9 +18,8 @@ class MenuServiceProvider implements ServiceProviderInterface { /** * @param Container $container A Pimple DI container. - * @return void */ - public function register(Container $container) + public function register(Container $container): void { $this->registerMenuServices($container); $this->registerMenuItemServices($container); @@ -28,15 +27,14 @@ public function register(Container $container) /** * @param Container $container A Pimple DI container. - * @return void */ - public function registerMenuServices(Container $container) + public function registerMenuServices(Container $container): void { /** * @param Container $container A Pimple DI container. * @return MenuFactory */ - $container['menu/factory'] = function (Container $container) { + $container['menu/factory'] = function (Container $container): \Charcoal\Ui\Menu\MenuFactory { $menuFactory = new MenuFactory(); $menuFactory->setArguments([ 'container' => $container, @@ -51,10 +49,9 @@ public function registerMenuServices(Container $container) * @param Container $container A Pimple DI container. * @return MenuBuilder */ - $container['menu/builder'] = function (Container $container) { + $container['menu/builder'] = function (Container $container): \Charcoal\Ui\Menu\MenuBuilder { $menuFactory = $container['menu/factory']; - $menuBuilder = new MenuBuilder($menuFactory, $container); - return $menuBuilder; + return new MenuBuilder($menuFactory, $container); }; } @@ -68,14 +65,13 @@ public function registerMenuServices(Container $container) * instantiated at the same time. * * @param Container $container A Pimple DI container. - * @return void */ - public function registerMenuItemServices(Container $container) + public function registerMenuItemServices(Container $container): void { /** * @var callable */ - $delegate = function (Container $container) { + $delegate = function (Container $container): array { $args = [ 'container' => $container, 'logger' => $container['logger'], diff --git a/packages/ui/src/Charcoal/Ui/ServiceProvider/UiServiceProvider.php b/packages/ui/src/Charcoal/Ui/ServiceProvider/UiServiceProvider.php index 2c4e01da7..bb0cf1900 100644 --- a/packages/ui/src/Charcoal/Ui/ServiceProvider/UiServiceProvider.php +++ b/packages/ui/src/Charcoal/Ui/ServiceProvider/UiServiceProvider.php @@ -20,9 +20,8 @@ class UiServiceProvider implements ServiceProviderInterface { /** * @param Container $container A Pimple DI container. - * @return void */ - public function register(Container $container) + public function register(Container $container): void { $container->register(new AuthServiceProvider()); $container->register(new DashboardServiceProvider()); diff --git a/packages/ui/src/Charcoal/Ui/UiGroupingInterface.php b/packages/ui/src/Charcoal/Ui/UiGroupingInterface.php index 77aa467c1..68639f820 100644 --- a/packages/ui/src/Charcoal/Ui/UiGroupingInterface.php +++ b/packages/ui/src/Charcoal/Ui/UiGroupingInterface.php @@ -1,5 +1,7 @@ type = $type; @@ -60,7 +56,7 @@ public function setType($type) * * @return string */ - public function type() + public function type(): ?string { return $this->type; } @@ -74,7 +70,7 @@ public function type() * @throws InvalidArgumentException If the template is not a string. * @return UiItemInterface Chainable */ - public function setTemplate($template) + public function setTemplate($template): static { if (!is_string($template)) { throw new InvalidArgumentException( @@ -92,7 +88,7 @@ public function setTemplate($template) * * @return string If unset, returns the UI item type. */ - public function template() + public function template(): ?string { if ($this->template === null) { return $this->type(); @@ -108,7 +104,7 @@ public function template() * @throws InvalidArgumentException If the controller is not a string. * @return UiItemInterface Chainable */ - public function setController($controller) + public function setController($controller): static { if (!is_string($controller)) { throw new InvalidArgumentException( @@ -126,7 +122,7 @@ public function setController($controller) * * @return string If unset, returns the UI item type. */ - public function controller() + public function controller(): ?string { if ($this->controller === null) { return $this->type(); diff --git a/packages/ui/src/Charcoal/Ui/UiItemInterface.php b/packages/ui/src/Charcoal/Ui/UiItemInterface.php index ef4e58824..01ad3d4ae 100644 --- a/packages/ui/src/Charcoal/Ui/UiItemInterface.php +++ b/packages/ui/src/Charcoal/Ui/UiItemInterface.php @@ -1,5 +1,7 @@ active = !!$active; + $this->active = (bool) $active; return $this; } @@ -266,7 +266,7 @@ public function setTabTitle($title) */ public function tabTitle() { - return ($this->tabTitle) ? $this->tabTitle : $this->title(); + return $this->tabTitle ?: $this->title(); } /** @@ -366,7 +366,7 @@ public function setIcon($icon) */ public function setShowTitle($show) { - $this->showTitle = !!$show; + $this->showTitle = (bool) $show; return $this; } @@ -381,7 +381,7 @@ public function showTitle() if ($this->showTitle === false) { return false; } else { - return !!$this->title(); + return (bool) $this->title(); } } @@ -393,7 +393,7 @@ public function showTitle() */ public function setShowSubtitle($show) { - $this->showSubtitle = !!$show; + $this->showSubtitle = (bool) $show; return $this; } @@ -408,7 +408,7 @@ public function showSubtitle() if ($this->showSubtitle === false) { return false; } else { - return !!$this->subtitle(); + return (bool) $this->subtitle(); } } @@ -420,7 +420,7 @@ public function showSubtitle() */ public function setShowDescription($show) { - $this->showDescription = !!$show; + $this->showDescription = (bool) $show; return $this; } @@ -435,7 +435,7 @@ public function showDescription() if ($this->showDescription === false) { return false; } else { - return !!$this->description(); + return (bool) $this->description(); } } @@ -447,7 +447,7 @@ public function showDescription() */ public function setShowNotes($show) { - $this->showNotes = !!$show; + $this->showNotes = (bool) $show; return $this; } @@ -462,7 +462,7 @@ public function showNotes() if ($this->showNotes === false) { return false; } else { - return !!$this->notes(); + return (bool) $this->notes(); } } @@ -474,7 +474,7 @@ public function showNotes() */ public function setShowIcon($show) { - $this->showIcon = !!$show; + $this->showIcon = (bool) $show; return $this; } @@ -489,7 +489,7 @@ public function showIcon() if ($this->showIcon === false) { return false; } else { - return !!$this->icon(); + return (bool) $this->icon(); } } @@ -501,7 +501,7 @@ public function showIcon() */ public function setShowHeader($show) { - $this->showHeader = !!$show; + $this->showHeader = (bool) $show; return $this; } @@ -528,7 +528,7 @@ public function showHeader() */ public function setShowFooter($show) { - $this->showFooter = !!$show; + $this->showFooter = (bool) $show; return $this; } @@ -553,7 +553,7 @@ public function showFooter() */ public function setShowTabTitle($showTabTitle) { - $this->showTabTitle = !!$showTabTitle; + $this->showTabTitle = (bool) $showTabTitle; return $this; } @@ -580,14 +580,10 @@ public function showTabTitle() protected function sortItemsByPriority( PrioritizableInterface $a, PrioritizableInterface $b - ) { + ): int { $priorityA = $a->priority(); $priorityB = $b->priority(); - - if ($priorityA === $priorityB) { - return 0; - } - return ($priorityA < $priorityB) ? (-1) : 1; + return $priorityA <=> $priorityB; } /** diff --git a/packages/ui/tests/Charcoal/AbstractTestCase.php b/packages/ui/tests/Charcoal/AbstractTestCase.php index 59ba12ea0..80f1772c4 100644 --- a/packages/ui/tests/Charcoal/AbstractTestCase.php +++ b/packages/ui/tests/Charcoal/AbstractTestCase.php @@ -1,5 +1,7 @@ getContainer(); @@ -32,14 +29,10 @@ protected function setUp(): void ]]); $method = new ReflectionMethod($this->obj, 'setAuthDependencies'); - $method->setAccessible(true); $method->invoke($this->obj, $container); } - /** - * @return void - */ - public function testDefaults() + public function testDefaults(): void { $this->assertTrue($this->obj->active()); $this->assertEquals(0, $this->obj->priority()); @@ -52,40 +45,28 @@ public function testDefaults() $this->assertEquals('', $this->obj->notes()); } - /** - * @return void - */ - public function testSetType() + public function testSetType(): void { $ret = $this->obj->setType('foobar'); $this->assertSame($ret, $this->obj); $this->assertEquals('foobar', $this->obj->type()); } - /** - * @return void - */ - public function testSetAcive() + public function testSetAcive(): void { $ret = $this->obj->setActive(false); $this->assertSame($ret, $this->obj); $this->assertEquals(false, $this->obj->active()); } - /** - * @return void - */ - public function testSetPriority() + public function testSetPriority(): void { $ret = $this->obj->setPriority(42); $this->assertSame($ret, $this->obj); $this->assertEquals(42, $this->obj->priority()); } - /** - * @return void - */ - public function testSetTemplate() + public function testSetTemplate(): void { $ret = $this->obj->setTemplate('foo/bar'); $this->assertSame($ret, $this->obj); @@ -95,60 +76,42 @@ public function testSetTemplate() $this->obj->setTemplate(false); } - /** - * @return void - */ - public function testNoTemplateReturnsType() + public function testNoTemplateReturnsType(): void { $ret = $this->obj->setType('foobar/baz'); $this->assertSame($ret, $this->obj); $this->assertEquals('foobar/baz', $this->obj->template()); } - /** - * @return void - */ - public function testSetTitle() + public function testSetTitle(): void { $ret = $this->obj->setTitle('Hello'); $this->assertSame($ret, $this->obj); $this->assertEquals('Hello', (string)$this->obj->title()); } - /** - * @return void - */ - public function testSetSubtitle() + public function testSetSubtitle(): void { $ret = $this->obj->setSubtitle('Hello'); $this->assertSame($ret, $this->obj); $this->assertEquals('Hello', (string)$this->obj->subtitle()); } - /** - * @return void - */ - public function testSetDescription() + public function testSetDescription(): void { $ret = $this->obj->setDescription('Hello'); $this->assertSame($ret, $this->obj); $this->assertEquals('Hello', (string)$this->obj->description()); } - /** - * @return void - */ - public function testSetNotes() + public function testSetNotes(): void { $ret = $this->obj->setNotes('Hello'); $this->assertSame($ret, $this->obj); $this->assertEquals('Hello', (string)$this->obj->notes()); } - /** - * @return void - */ - public function testShowTitle() + public function testShowTitle(): void { $this->assertFalse($this->obj->showTitle()); $this->obj->setTitle('Foo'); @@ -158,10 +121,7 @@ public function testShowTitle() $this->assertFalse($this->obj->showTitle()); } - /** - * @return void - */ - public function testShowSubtitle() + public function testShowSubtitle(): void { $this->assertFalse($this->obj->showSubtitle()); $this->obj->setSubtitle('Foo'); @@ -171,10 +131,7 @@ public function testShowSubtitle() $this->assertFalse($this->obj->showSubtitle()); } - /** - * @return void - */ - public function testShowDescription() + public function testShowDescription(): void { $this->assertFalse($this->obj->showDescription()); $this->obj->setDescription('Foo'); @@ -184,10 +141,7 @@ public function testShowDescription() $this->assertFalse($this->obj->showDescription()); } - /** - * @return void - */ - public function testShowNotes() + public function testShowNotes(): void { $this->assertFalse($this->obj->showNotes()); $this->obj->setNotes('Foo'); diff --git a/packages/ui/tests/Charcoal/Ui/ContainerIntegrationTrait.php b/packages/ui/tests/Charcoal/Ui/ContainerIntegrationTrait.php index f71d0133c..c7fd41807 100644 --- a/packages/ui/tests/Charcoal/Ui/ContainerIntegrationTrait.php +++ b/packages/ui/tests/Charcoal/Ui/ContainerIntegrationTrait.php @@ -51,9 +51,8 @@ protected function getContainerProvider() /** * @see ContainerProvider - * @return void */ - private function setupContainer() + private function setupContainer(): void { $provider = new ContainerProvider(); $container = new Container(); diff --git a/packages/ui/tests/Charcoal/Ui/ContainerProvider.php b/packages/ui/tests/Charcoal/Ui/ContainerProvider.php index 7e8dc0f08..5ef792d2f 100644 --- a/packages/ui/tests/Charcoal/Ui/ContainerProvider.php +++ b/packages/ui/tests/Charcoal/Ui/ContainerProvider.php @@ -36,9 +36,8 @@ class ContainerProvider * Register the unit tests required services. * * @param Container $container A DI container. - * @return void */ - public function registerBaseServices(Container $container) + public function registerBaseServices(Container $container): void { $this->registerConfig($container); $this->registerSource($container); @@ -50,9 +49,8 @@ public function registerBaseServices(Container $container) * Register the admin services. * * @param Container $container A DI container. - * @return void */ - public function registerConfig(Container $container) + public function registerConfig(Container $container): void { $container['config'] = new AppConfig([ 'base_path' => realpath(__DIR__ . '/../../..'), @@ -71,8 +69,6 @@ public function registerConfig(Container $container) * * Explicitly defined in case of a version mismatch with dependencies. This parameter * is normally defined by {@see \Charcoal\App\ServiceProvider\AppServiceProvider}. - * - * @var array */ $container['module/classes'] = []; } @@ -83,11 +79,10 @@ public function registerConfig(Container $container) * Note: Uses SQLite to create a database in memory. * * @param Container $container A DI container. - * @return void */ - public function registerSource(Container $container) + public function registerSource(Container $container): void { - $container['database'] = function () { + $container['database'] = function (): \PDO { $pdo = new PDO('sqlite::memory:'); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); return $pdo; @@ -98,9 +93,8 @@ public function registerSource(Container $container) * Register the admin services. * * @param Container $container A DI container. - * @return void */ - public function registerModelServices(Container $container) + public function registerModelServices(Container $container): void { static $provider = null; @@ -115,9 +109,8 @@ public function registerModelServices(Container $container) * Register the admin services. * * @param Container $container A DI container. - * @return void */ - public function registerAuthServices(Container $container) + public function registerAuthServices(Container $container): void { static $provider = null; @@ -132,9 +125,8 @@ public function registerAuthServices(Container $container) * Setup the application's translator service. * * @param Container $container A DI container. - * @return void */ - public function registerTranslatorServices(Container $container) + public function registerTranslatorServices(Container $container): void { static $provider = null; @@ -149,9 +141,8 @@ public function registerTranslatorServices(Container $container) * Setup the framework's view renderer. * * @param Container $container A DI container. - * @return void */ - public function registerViewServices(Container $container) + public function registerViewServices(Container $container): void { static $provider = null; @@ -166,25 +157,19 @@ public function registerViewServices(Container $container) * Setup the application's logging interface. * * @param Container $container A DI container. - * @return void */ - public function registerLogger(Container $container) + public function registerLogger(Container $container): void { - $container['logger'] = function () { - return new NullLogger(); - }; + $container['logger'] = (fn(): \Psr\Log\NullLogger => new NullLogger()); } /** * Setup the application's caching interface. * * @param Container $container A DI container. - * @return void */ - public function registerCache(Container $container) + public function registerCache(Container $container): void { - $container['cache'] = function () { - return new Pool(); - }; + $container['cache'] = (fn(): \Stash\Pool => new Pool()); } } diff --git a/packages/ui/tests/Charcoal/Ui/Dashboard/AbstractDashboardTest.php b/packages/ui/tests/Charcoal/Ui/Dashboard/AbstractDashboardTest.php index 4851b6527..6013771d4 100644 --- a/packages/ui/tests/Charcoal/Ui/Dashboard/AbstractDashboardTest.php +++ b/packages/ui/tests/Charcoal/Ui/Dashboard/AbstractDashboardTest.php @@ -23,9 +23,6 @@ class AbstractDashboardTest extends AbstractTestCase */ public $obj; - /** - * @return void - */ protected function setUp(): void { $container = $this->getContainer(); @@ -45,10 +42,8 @@ protected function setUp(): void /** * Helper method, example layout for tests. - * - * @return array */ - protected function exampleLayout() + protected function exampleLayout(): array { return [ 'structure' => [ @@ -57,15 +52,10 @@ protected function exampleLayout() ]; } - /** - * @return void - */ - public function testSetWidgetCallback() + public function testSetWidgetCallback(): void { $obj = $this->obj; - $cb = function($o) { - return 'foo'; - }; + $cb = (fn($o): string => 'foo'); $ret = $obj->setWidgetCallback($cb); $this->assertSame($ret, $obj); } @@ -77,10 +67,8 @@ public function testSetWidgetCallback() * - calling `setLayout()` with an Layout objects set the layout * - calling `setLayout()` with an array sets the same layout * - `setLayout()` throws an exception if not an array / Layout object - * - * @return void */ - public function testSetLayout() + public function testSetLayout(): void { $container = $this->getContainer(); @@ -101,10 +89,7 @@ public function testSetLayout() $obj->setLayout('foobar'); } - /** - * @return void - */ - public function testSetWidgets() + public function testSetWidgets(): void { $obj = $this->obj; $ret = $obj->setWidgets([ @@ -113,10 +98,7 @@ public function testSetWidgets() $this->assertSame($ret, $obj); } - /** - * @return void - */ - public function testAddWidgetInvalidIdentThrowsException() + public function testAddWidgetInvalidIdentThrowsException(): void { $obj = $this->obj; @@ -124,10 +106,7 @@ public function testAddWidgetInvalidIdentThrowsException() $obj->addWidget([], []); } - /** - * @return void - */ - public function testAddWidgetInvalidWidgetThrowsException() + public function testAddWidgetInvalidWidgetThrowsException(): void { $obj = $this->obj; @@ -135,36 +114,29 @@ public function testAddWidgetInvalidWidgetThrowsException() $obj->addWidget('foo', false); } - /** - * @return void - */ - public function testWidgets() + public function testWidgets(): void { $obj = $this->obj; - $ret = $obj->setWidgets([ + $obj->setWidgets([ 'test' => [] ]); $widgets = $obj->widgets(); - $num = 0; foreach ($widgets as $w) { $this->assertInstanceOf(UiItemInterface::class, $w); } } - /** - * @return void - */ - public function testWidgetsCallback() + public function testWidgetsCallback(): void { $obj = $this->obj; $obj->setWidgets([ 'test' => [] ]); - $cb = function(UiItemInterface $widget) { + $cb = function(UiItemInterface $widget): void { $widget['foo'] = 'bar'; }; @@ -175,30 +147,24 @@ public function testWidgetsCallback() } } - /** - * @return void - */ - public function testHasWidgets() + public function testHasWidgets(): void { $obj = $this->obj; $this->assertFalse($obj->hasWidgets()); - $ret = $obj->setWidgets([ + $obj->setWidgets([ 'test'=>[] ]); $this->assertTrue($obj->hasWidgets()); } - /** - * @return void - */ - public function testNumWidgets() + public function testNumWidgets(): void { $obj = $this->obj; $this->assertEquals(0, $obj->numWidgets()); - $ret = $obj->setWidgets([ + $obj->setWidgets([ 'test'=>[], 'foobar'=>[] ]); diff --git a/packages/ui/tests/Charcoal/Ui/Dashboard/GenericDashboardTest.php b/packages/ui/tests/Charcoal/Ui/Dashboard/GenericDashboardTest.php index eb6bd34cb..73df2edd2 100644 --- a/packages/ui/tests/Charcoal/Ui/Dashboard/GenericDashboardTest.php +++ b/packages/ui/tests/Charcoal/Ui/Dashboard/GenericDashboardTest.php @@ -20,9 +20,6 @@ class GenericDashboardTest extends AbstractTestCase */ public $obj; - /** - * @return void - */ protected function setUp(): void { $container = $this->getContainer(); @@ -37,10 +34,7 @@ protected function setUp(): void ]); } - /** - * @return void - */ - public function testType() + public function testType(): void { $this->assertEquals('charcoal/ui/dashboard/generic', $this->obj->type()); } diff --git a/packages/ui/tests/Charcoal/Ui/Form/AbstractFormTest.php b/packages/ui/tests/Charcoal/Ui/Form/AbstractFormTest.php index d2cbbf327..c1d23ea35 100644 --- a/packages/ui/tests/Charcoal/Ui/Form/AbstractFormTest.php +++ b/packages/ui/tests/Charcoal/Ui/Form/AbstractFormTest.php @@ -20,9 +20,6 @@ class AbstractFormTest extends AbstractTestCase */ public $obj; - /** - * @return void - */ protected function setUp(): void { $container = $this->getContainer(); @@ -40,22 +37,14 @@ protected function setUp(): void ]); } - /** - * @return void - */ - public function testSetGroupCallback() + public function testSetGroupCallback(): void { - $cb = function($o) { - return 'foo'; - }; + $cb = (fn($o): string => 'foo'); $ret = $this->obj->setGroupCallback($cb); $this->assertSame($ret, $this->obj); } - /** - * @return void - */ - public function testSetAction() + public function testSetAction(): void { $this->assertEquals('', $this->obj->action()); $ret = $this->obj->setAction('foo/bar'); @@ -66,10 +55,7 @@ public function testSetAction() $this->obj->setAction(false); } - /** - * @return void - */ - public function testSetMethod() + public function testSetMethod(): void { //$this->assertEquals('post', $obj->method()); $ret = $this->obj->setMethod('get'); @@ -83,20 +69,14 @@ public function testSetMethod() $this->obj->setMethod('foobar'); } - /** - * @return void - */ - public function testSetL10nMode() + public function testSetL10nMode(): void { $ret = $this->obj->setL10nMode('loop'); $this->assertSame($ret, $this->obj); $this->assertEquals('loop', $this->obj->l10nMode()); } - /** - * @return void - */ - public function testSetGroup() + public function testSetGroup(): void { $ret = $this->obj->setGroups([ 'test' => [] @@ -106,37 +86,28 @@ public function testSetGroup() $this->assertEquals(1, $this->obj->numGroups()); } - /** - * @return void - */ - public function testAddGroup() + public function testAddGroup(): void { $ret = $this->obj->addGroup('ident', []); $this->assertSame($ret, $this->obj); } - /** - * @return void - */ - public function testHasGroups() + public function testHasGroups(): void { $this->assertFalse($this->obj->hasGroups()); - $ret = $this->obj->setGroups([ + $this->obj->setGroups([ 'test' => [] ]); $this->assertTrue($this->obj->hasGroups()); } - /** - * @return void - */ - public function testNumGroups() + public function testNumGroups(): void { $this->assertEquals(0, $this->obj->numGroups()); - $ret = $this->obj->setGroups([ + $this->obj->setGroups([ 'test' => [], 'foobar' => [] ]); @@ -144,10 +115,7 @@ public function testNumGroups() $this->assertEquals(2, $this->obj->numGroups()); } - /** - * @return void - */ - public function testSetFormData() + public function testSetFormData(): void { $this->assertEquals([], $this->obj->formData()); $ret = $this->obj->setFormData([ 'foo' => 'bar' ]); @@ -158,10 +126,7 @@ public function testSetFormData() $this->assertEquals([ 'baz' => 42 ], $this->obj->formData()); } - /** - * @return void - */ - public function testAddData() + public function testAddData(): void { $ret = $this->obj->addFormData('foo', 'bar'); $this->assertSame($ret, $this->obj); diff --git a/packages/ui/tests/Charcoal/Ui/Form/GenericFormTest.php b/packages/ui/tests/Charcoal/Ui/Form/GenericFormTest.php index 616c37a63..ebe7dab77 100644 --- a/packages/ui/tests/Charcoal/Ui/Form/GenericFormTest.php +++ b/packages/ui/tests/Charcoal/Ui/Form/GenericFormTest.php @@ -20,9 +20,6 @@ class GenericFormTest extends AbstractTestCase */ public $obj; - /** - * @return void - */ protected function setUp(): void { $container = $this->getContainer(); @@ -37,10 +34,7 @@ protected function setUp(): void ]); } - /** - * @return void - */ - public function testType() + public function testType(): void { $this->assertEquals('charcoal/ui/form/generic', $this->obj->type()); } diff --git a/packages/ui/tests/Charcoal/Ui/FormGroup/AbstractFormGroupTest.php b/packages/ui/tests/Charcoal/Ui/FormGroup/AbstractFormGroupTest.php index 4483c6321..26ab9b596 100644 --- a/packages/ui/tests/Charcoal/Ui/FormGroup/AbstractFormGroupTest.php +++ b/packages/ui/tests/Charcoal/Ui/FormGroup/AbstractFormGroupTest.php @@ -20,9 +20,6 @@ class AbstractFormGroupTest extends AbstractTestCase */ public $obj; - /** - * @return void - */ protected function setUp(): void { $container = $this->getContainer(); @@ -44,23 +41,15 @@ protected function setUp(): void ]); } - /** - * @return void - */ - public function testSetInputCallback() + public function testSetInputCallback(): void { $obj = $this->obj; - $cb = function($o) { - return 'foo'; - }; + $cb = (fn($o): string => 'foo'); $ret = $obj->setInputCallback($cb); $this->assertSame($ret, $obj); } - /** - * @return void - */ - public function testSetInputs() + public function testSetInputs(): void { $obj = $this->obj; $ret = $obj->setInputs([ @@ -69,10 +58,7 @@ public function testSetInputs() $this->assertSame($ret, $obj); } - /** - * @return void - */ - public function testSetPriority() + public function testSetPriority(): void { $this->assertEquals(0, $this->obj->priority()); @@ -86,40 +72,31 @@ public function testSetPriority() $this->obj->setPriority('foobar'); } - /** - * @return void - */ - public function testSetL10nMode() + public function testSetL10nMode(): void { $ret = $this->obj->setL10nMode('loop'); $this->assertSame($ret, $this->obj); $this->assertEquals('loop', $this->obj->l10nMode()); } - /** - * @return void - */ - public function testHasInputs() + public function testHasInputs(): void { $obj = $this->obj; $this->assertFalse($obj->hasInputs()); - $ret = $obj->setInputs([ + $obj->setInputs([ 'test' => [] ]); $this->assertTrue($obj->hasInputs()); } - /** - * @return void - */ - public function testNumInput() + public function testNumInput(): void { $obj = $this->obj; $this->assertEquals(0, $obj->numInputs()); - $ret = $obj->setInputs([ + $obj->setInputs([ 'test' => [], 'foobar' => [] ]); diff --git a/packages/ui/tests/Charcoal/Ui/FormGroup/GenericFormGroupTest.php b/packages/ui/tests/Charcoal/Ui/FormGroup/GenericFormGroupTest.php index 9d8791a0b..9ab9f31c6 100644 --- a/packages/ui/tests/Charcoal/Ui/FormGroup/GenericFormGroupTest.php +++ b/packages/ui/tests/Charcoal/Ui/FormGroup/GenericFormGroupTest.php @@ -21,9 +21,6 @@ class GenericFormGroupTest extends AbstractTestCase */ public $obj; - /** - * @return void - */ protected function setUp(): void { $container = $this->getContainer(); @@ -46,10 +43,7 @@ protected function setUp(): void ]); } - /** - * @return void - */ - public function testConstructor() + public function testConstructor(): void { $this->assertInstanceOf(GenericFormGroup::class, $this->obj); } diff --git a/packages/ui/tests/Charcoal/Ui/FormInput/GenericFormInputTest.php b/packages/ui/tests/Charcoal/Ui/FormInput/GenericFormInputTest.php index f422ff496..42eacdcef 100644 --- a/packages/ui/tests/Charcoal/Ui/FormInput/GenericFormInputTest.php +++ b/packages/ui/tests/Charcoal/Ui/FormInput/GenericFormInputTest.php @@ -20,9 +20,6 @@ class GenericFormInputTest extends AbstractTestCase */ public $obj; - /** - * @return void - */ protected function setUp(): void { $container = $this->getContainer(); @@ -38,10 +35,7 @@ protected function setUp(): void ]); } - /** - * @return void - */ - public function testType() + public function testType(): void { $this->assertEquals('charcoal/ui/form-input/generic', $this->obj->type()); } diff --git a/packages/ui/tests/Charcoal/Ui/Layout/AbstractLayoutTest.php b/packages/ui/tests/Charcoal/Ui/Layout/AbstractLayoutTest.php index 0ef2b90e3..67d8f60cf 100644 --- a/packages/ui/tests/Charcoal/Ui/Layout/AbstractLayoutTest.php +++ b/packages/ui/tests/Charcoal/Ui/Layout/AbstractLayoutTest.php @@ -16,9 +16,6 @@ class AbstractLayoutTest extends AbstractTestCase */ public $obj; - /** - * @return void - */ protected function setUp(): void { $this->obj = $this->getMockForAbstractClass(AbstractLayout::class); @@ -30,10 +27,8 @@ protected function setUp(): void * - Is chainable * - Sets the position (retrievable with `position()`) * - Throws an exception when setting non-numeric (integer) values. - * - * @return void */ - public function testSetPosition() + public function testSetPosition(): void { $obj = $this->obj; $this->assertEquals(0, $obj->position()); @@ -46,26 +41,17 @@ public function testSetPosition() $obj->setPosition('foo'); } - /** - * @return void - */ - public function testDefaultPosition() + public function testDefaultPosition(): void { $obj = $this->obj; $this->assertEquals(0, $obj->position()); } - /** - * @return void - */ - public function testSetData() + public function testSetData(): void { $struct = [[ 'columns' => [ 1 ] ]]; - $computed = [ - 'columns' => [ 1 ] - ]; $obj = $this->obj; $ret = $obj->setData([ @@ -75,10 +61,7 @@ public function testSetData() //$this->assertEquals($computed, $obj->structure()); } - /** - * @return void - */ - public function testSetStructure() + public function testSetStructure(): void { $obj = $this->obj; $this->assertEquals([], $obj->structure()); @@ -121,10 +104,7 @@ public function testSetStructure() $this->assertEquals($res, $obj->structure()); } - /** - * @return void - */ - public function testNumRows() + public function testNumRows(): void { $obj = $this->obj; $this->assertEquals(0, $obj->numRows()); @@ -136,10 +116,7 @@ public function testNumRows() $this->assertEquals(3, $obj->numRows()); } - /** - * @return void - */ - public function testRowIndex() + public function testRowIndex(): void { $obj = $this->obj; $this->assertNull($obj->rowIndex()); @@ -156,10 +133,7 @@ public function testRowIndex() $this->assertEquals(0, $obj->rowIndex(5)); } - /** - * @return void - */ - public function testRowData() + public function testRowData(): void { $obj = $this->obj; $this->assertNull($obj->rowData()); @@ -176,10 +150,7 @@ public function testRowData() $this->assertNull($obj->rowData(5)); } - /** - * @return void - */ - public function testRowNumColumns() + public function testRowNumColumns(): void { $obj = $this->obj; $this->assertNull($obj->rowNumColumns()); @@ -196,10 +167,7 @@ public function testRowNumColumns() $this->assertNull($obj->rowNumColumns(5)); } - /** - * @return void - */ - public function testRowNumCells() + public function testRowNumCells(): void { $obj = $this->obj; $this->assertNull($obj->rowNumCells()); @@ -216,10 +184,7 @@ public function testRowNumCells() $this->assertNull($obj->rowNumCells(5)); } - /** - * @return void - */ - public function testRowFirstCellIndex() + public function testRowFirstCellIndex(): void { $obj = $this->obj; $this->assertNull($obj->rowFirstCellIndex()); @@ -236,10 +201,7 @@ public function testRowFirstCellIndex() //$this->assertNull($obj->rowFirstCellIndex(5)); } - /** - * @return void - */ - public function testCellRowIndex() + public function testCellRowIndex(): void { $obj = $this->obj; //$this->assertNull($obj->cellRowIndex()); @@ -256,10 +218,7 @@ public function testCellRowIndex() //$this->assertNull($obj->cellRowIndex(5)); } - /** - * @return void - */ - public function testNumCellsTotal() + public function testNumCellsTotal(): void { $obj = $this->obj; $this->assertEquals(0, $obj->numCellsTotal()); @@ -271,10 +230,7 @@ public function testNumCellsTotal() $this->assertEquals(5, $obj->numCellsTotal()); } - /** - * @return void - */ - public function testNumCellSpan() + public function testNumCellSpan(): void { $obj = $this->obj; $this->assertNull($obj->cellSpan()); @@ -291,10 +247,7 @@ public function testNumCellSpan() $this->assertNull($obj->cellSpan(5)); } - /** - * @return void - */ - public function testNumCellSpanBy12() + public function testNumCellSpanBy12(): void { $obj = $this->obj; $this->assertNull($obj->cellSpanBy12()); @@ -311,10 +264,7 @@ public function testNumCellSpanBy12() $this->assertNull($obj->cellSpanBy12(5)); } - /** - * @return void - */ - public function testCellStartsRow() + public function testCellStartsRow(): void { $obj = $this->obj; //$this->assertNull($obj->cellStartsRow()); @@ -331,10 +281,7 @@ public function testCellStartsRow() //$this->assertNull($obj->cellStartsRow(5)); } - /** - * @return void - */ - public function testCellEndsRow() + public function testCellEndsRow(): void { $obj = $this->obj; //$this->assertNull($obj->cellStartsRow()); @@ -351,19 +298,13 @@ public function testCellEndsRow() //$this->assertNull($obj->cellEndsRow(5)); } - /** - * @return void - */ - public function testStart() + public function testStart(): void { $obj = $this->obj; $this->assertEquals('', $obj->start()); } - /** - * @return void - */ - public function testEnd() + public function testEnd(): void { $obj = $this->obj; $this->assertEquals(0, $obj->position()); diff --git a/packages/ui/tests/Charcoal/Ui/Layout/GenericLayoutTest.php b/packages/ui/tests/Charcoal/Ui/Layout/GenericLayoutTest.php index d5e2443ab..a8dec5e92 100644 --- a/packages/ui/tests/Charcoal/Ui/Layout/GenericLayoutTest.php +++ b/packages/ui/tests/Charcoal/Ui/Layout/GenericLayoutTest.php @@ -19,9 +19,6 @@ class GenericLayoutTest extends AbstractTestCase */ public $obj; - /** - * @return void - */ protected function setUp(): void { $container = $this->getContainer(); @@ -32,10 +29,7 @@ protected function setUp(): void $this->obj = new GenericLayout(); } - /** - * @return void - */ - public function testType() + public function testType(): void { $this->assertEquals('charcoal/ui/layout/generic', $this->obj->type()); } diff --git a/packages/ui/tests/Charcoal/Ui/Menu/AbstractMenuTest.php b/packages/ui/tests/Charcoal/Ui/Menu/AbstractMenuTest.php index 81e3dfbc5..78543cb77 100644 --- a/packages/ui/tests/Charcoal/Ui/Menu/AbstractMenuTest.php +++ b/packages/ui/tests/Charcoal/Ui/Menu/AbstractMenuTest.php @@ -20,9 +20,6 @@ class AbstractMenuTest extends AbstractTestCase */ public $obj; - /** - * @return void - */ protected function setUp(): void { $container = $this->getContainer(); @@ -38,12 +35,8 @@ protected function setUp(): void ]); } - /** - * @return void - */ - public function testHasItems() + public function testHasItems(): void { - $obj = $this->obj; $this->assertFalse($this->obj->hasItems()); $this->obj->setItems([ @@ -53,10 +46,7 @@ public function testHasItems() $this->assertTrue($this->obj->hasItems()); } - /** - * @return void - */ - public function testNumItems() + public function testNumItems(): void { $obj = $this->obj; $this->assertEquals(0, $obj->numItems()); @@ -69,10 +59,7 @@ public function testNumItems() $this->assertEquals(2, $obj->numItems()); } - /** - * @return void - */ - public function testItems() + public function testItems(): void { $ret = iterator_to_array($this->obj->items()); $this->assertEmpty($ret); @@ -92,12 +79,9 @@ public function testItems() $this->assertInstanceOf(MenuItemInterface::class, $ret['foobar']); } - /** - * @return void - */ - public function testItemCallback() + public function testItemCallback(): void { - $cb = function($item) { + $cb = function(array $item): void { $item['property_from_callback'] = 'yes'; }; $ret = $this->obj->setItemCallback($cb); @@ -113,10 +97,7 @@ public function testItemCallback() $this->assertEquals('yes', $ret['foobar']['property_from_callback']); } - /** - * @return void - */ - public function testItemsPriority() + public function testItemsPriority(): void { $ret = iterator_to_array($this->obj->items()); $this->assertEmpty($ret); diff --git a/packages/ui/tests/Charcoal/Ui/Menu/GenericMenuTest.php b/packages/ui/tests/Charcoal/Ui/Menu/GenericMenuTest.php index 7ce03f389..7a853bb0f 100644 --- a/packages/ui/tests/Charcoal/Ui/Menu/GenericMenuTest.php +++ b/packages/ui/tests/Charcoal/Ui/Menu/GenericMenuTest.php @@ -19,9 +19,6 @@ class GenericMenuTest extends AbstractTestCase */ public $obj; - /** - * @return void - */ protected function setUp(): void { $container = $this->getContainer(); @@ -35,10 +32,7 @@ protected function setUp(): void ]); } - /** - * @return void - */ - public function testType() + public function testType(): void { $this->assertEquals('charcoal/ui/menu/generic', $this->obj->type()); } diff --git a/packages/ui/tests/Charcoal/Ui/MenuItem/AbstractMenuItemTest.php b/packages/ui/tests/Charcoal/Ui/MenuItem/AbstractMenuItemTest.php index 120079305..9d15b2a0b 100644 --- a/packages/ui/tests/Charcoal/Ui/MenuItem/AbstractMenuItemTest.php +++ b/packages/ui/tests/Charcoal/Ui/MenuItem/AbstractMenuItemTest.php @@ -19,9 +19,6 @@ class AbstractMenuItemTest extends AbstractTestCase */ public $obj; - /** - * @return void - */ protected function setUp(): void { $container = $this->getContainer(); @@ -39,30 +36,24 @@ protected function setUp(): void ]); } - /** - * @return void - */ - public function testHasChildren() + public function testHasChildren(): void { $obj = $this->obj; $this->assertFalse($obj->hasChildren()); - $ret = $obj->setChildren([ + $obj->setChildren([ 'test' => [] ]); $this->assertTrue($obj->hasChildren()); } - /** - * @return void - */ - public function testNumChildren() + public function testNumChildren(): void { $obj = $this->obj; $this->assertEquals(0, $obj->numChildren()); - $ret = $obj->setChildren([ + $obj->setChildren([ 'test' => [], 'foobar' => [] ]); diff --git a/packages/ui/tests/Charcoal/Ui/MenuItem/GenericMenuItemTest.php b/packages/ui/tests/Charcoal/Ui/MenuItem/GenericMenuItemTest.php index 41afcf827..66a075ad9 100644 --- a/packages/ui/tests/Charcoal/Ui/MenuItem/GenericMenuItemTest.php +++ b/packages/ui/tests/Charcoal/Ui/MenuItem/GenericMenuItemTest.php @@ -19,9 +19,6 @@ class GenericMenuItemTest extends AbstractTestCase */ public $obj; - /** - * @return void - */ protected function setUp(): void { $container = $this->getContainer(); @@ -37,10 +34,7 @@ protected function setUp(): void ]); } - /** - * @return void - */ - public function testType() + public function testType(): void { $this->assertEquals('charcoal/ui/menu-item/generic', $this->obj->type()); } diff --git a/packages/ui/tests/Charcoal/Ui/ServiceProvider/DashboardServiceProviderTest.php b/packages/ui/tests/Charcoal/Ui/ServiceProvider/DashboardServiceProviderTest.php index 13da4385c..61bcabf69 100644 --- a/packages/ui/tests/Charcoal/Ui/ServiceProvider/DashboardServiceProviderTest.php +++ b/packages/ui/tests/Charcoal/Ui/ServiceProvider/DashboardServiceProviderTest.php @@ -27,37 +27,24 @@ class DashboardServiceProviderTest extends AbstractTestCase */ public $container; - /** - * @return void - */ protected function setUp(): void { $this->obj = new DashboardServiceProvider(); $this->container = new Container(); - $this->container['logger'] = function () { - return new NullLogger(); - }; + $this->container['logger'] = (fn(): \Psr\Log\NullLogger => new NullLogger()); // Required depdendencies (stub) - $this->container['view'] = function () { - return null; - }; - $this->container['widget/builder'] = function () { - return null; - }; - $this->container['layout/builder'] = function () { - return null; - }; + $this->container['view'] = (fn(): null => null); + $this->container['widget/builder'] = (fn(): null => null); + $this->container['layout/builder'] = (fn(): null => null); } /** * Asserts that the `register()` method * - Registers all services on the container - * - * @return void */ - public function testRegisterRegistersAllProviders() + public function testRegisterRegistersAllProviders(): void { $this->container->register($this->obj); @@ -65,23 +52,17 @@ public function testRegisterRegistersAllProviders() $this->assertTrue(isset($this->container['dashboard/builder'])); } - /** - * @return void - */ - public function testDashboardFactory() + public function testDashboardFactory(): void { $this->container->register($this->obj); $factory = $this->container['dashboard/factory']; - $this->assertInstanceOf('\Charcoal\Factory\GenericFactory', $factory); + $this->assertInstanceOf(\Charcoal\Factory\GenericFactory::class, $factory); } - /** - * @return void - */ - public function testDashboardBuilder() + public function testDashboardBuilder(): void { $this->container->register($this->obj); $factory = $this->container['dashboard/builder']; - $this->assertInstanceOf('\Charcoal\Ui\Dashboard\DashboardBuilder', $factory); + $this->assertInstanceOf(\Charcoal\Ui\Dashboard\DashboardBuilder::class, $factory); } } diff --git a/packages/ui/tests/Charcoal/Ui/ServiceProvider/UiServiceProviderTest.php b/packages/ui/tests/Charcoal/Ui/ServiceProvider/UiServiceProviderTest.php index a4da40ef0..a55f1b665 100644 --- a/packages/ui/tests/Charcoal/Ui/ServiceProvider/UiServiceProviderTest.php +++ b/packages/ui/tests/Charcoal/Ui/ServiceProvider/UiServiceProviderTest.php @@ -24,9 +24,6 @@ class UiServiceProviderTest extends AbstractTestCase */ public $container; - /** - * @return void - */ protected function setUp(): void { $this->obj = new UiServiceProvider(); @@ -36,10 +33,8 @@ protected function setUp(): void /** * Asserts that the `register()` method * - Registers all services on the container - * - * @return void */ - public function testRegisterRegistersAllProviders() + public function testRegisterRegistersAllProviders(): void { $this->container->register($this->obj); diff --git a/packages/user/composer.json b/packages/user/composer.json index 5b4ce1606..eb6381ed3 100644 --- a/packages/user/composer.json +++ b/packages/user/composer.json @@ -1,5 +1,4 @@ { - "type": "library", "name": "charcoal/user", "description": "User definition, authentication and authorization.", "license": "MIT", @@ -9,13 +8,9 @@ "email": "mat@locomotive.ca" } ], - "extra": { - "branch-alias": { - "dev-main": "5.x-dev" - } - }, + "type": "library", "require": { - "php": "^7.4 || ^8.0", + "php": "^8.3", "psr/log": "^1.0", "laminas/laminas-permissions-acl": "^2.8", "charcoal/object": "^5.1", @@ -25,7 +20,7 @@ "charcoal/translator": "^5.1" }, "require-dev": { - "phpunit/phpunit": "^9.5", + "phpunit/phpunit": "^12.5", "squizlabs/php_codesniffer": "^3.5", "php-coveralls/php-coveralls": "^2.2", "cache/void-adapter": "^1.0", @@ -42,8 +37,10 @@ "Charcoal\\Tests\\": "tests/Charcoal/" } }, - "replace": { - "locomotivemtl/charcoal-user": "*" + "extra": { + "branch-alias": { + "dev-main": "5.x-dev" + } }, "scripts": { "test": [ @@ -61,6 +58,9 @@ "phpcbf": "php vendor/bin/phpcbf -ps --colors src/ tests/", "phpunit": "php vendor/bin/phpunit --coverage-text" }, + "replace": { + "locomotivemtl/charcoal-user": "*" + }, "minimum-stability": "dev", "prefer-stable": true } diff --git a/packages/user/src/Charcoal/User/AbstractAuthToken.php b/packages/user/src/Charcoal/User/AbstractAuthToken.php index 5c9b26503..cd77759b9 100644 --- a/packages/user/src/Charcoal/User/AbstractAuthToken.php +++ b/packages/user/src/Charcoal/User/AbstractAuthToken.php @@ -38,21 +38,18 @@ abstract class AbstractAuthToken extends AbstractModel implements /** * The related user ID. - * - * @var string */ - private $userId; + private ?string $userId = null; /** * The token's expiration date. - * - * @var DateTimeInterface|null */ - private $expiry; + private ?\DateTimeInterface $expiry = null; /** * @return string */ + #[\Override] public function key() { return 'ident'; @@ -238,7 +235,7 @@ public function getUserIdFromToken($ident, $token) } // Validate encrypted token - if (password_verify($token, $this['token']) !== true) { + if (!password_verify($token, (string) $this['token'])) { $this->panic(); $this->delete(); return null; @@ -250,10 +247,8 @@ public function getUserIdFromToken($ident, $token) /** * Delete all auth tokens from storage for the current user. - * - * @return void */ - public function deleteUserAuthTokens() + public function deleteUserAuthTokens(): void { $userId = $this['userId']; if (isset($userId)) { @@ -292,6 +287,7 @@ protected function panic() * * @return boolean */ + #[\Override] protected function preSave() { $result = parent::preSave(); @@ -310,7 +306,8 @@ protected function preSave() * @param array $properties The properties (ident) set for update. * @return boolean */ - protected function preUpdate(array $properties = null) + #[\Override] + protected function preUpdate(?array $properties = null) { $result = parent::preUpdate($properties); @@ -326,7 +323,7 @@ protected function touchToken() { $token = $this['token']; if (password_needs_rehash($token, PASSWORD_DEFAULT)) { - $this['token'] = password_hash($token, PASSWORD_DEFAULT); + $this['token'] = password_hash((string) $token, PASSWORD_DEFAULT); } } @@ -336,7 +333,8 @@ protected function touchToken() * @param array $data Optional metadata to merge on the object. * @return AuthTokenMetadata */ - protected function createMetadata(array $data = null) + #[\Override] + protected function createMetadata(?array $data = null) { $class = $this->metadataClass(); return new $class($data); @@ -347,6 +345,7 @@ protected function createMetadata(array $data = null) * * @return string */ + #[\Override] protected function metadataClass() { return AuthTokenMetadata::class; diff --git a/packages/user/src/Charcoal/User/AbstractAuthenticator.php b/packages/user/src/Charcoal/User/AbstractAuthenticator.php index 3e4076194..4006d9e08 100644 --- a/packages/user/src/Charcoal/User/AbstractAuthenticator.php +++ b/packages/user/src/Charcoal/User/AbstractAuthenticator.php @@ -68,31 +68,23 @@ abstract class AbstractAuthenticator implements /** * The user object type. - * - * @var string */ - private $userType; + private string $userType; /** * Store the user model factory instance for the current class. - * - * @var FactoryInterface */ - private $userFactory; + private \Charcoal\Factory\FactoryInterface $userFactory; /** * The auth-token object type. - * - * @var string */ - private $tokenType; + private string $tokenType; /** * Store the auth-token model factory instance for the current class. - * - * @var FactoryInterface */ - private $tokenFactory; + private \Charcoal\Factory\FactoryInterface $tokenFactory; /** * @param array $data Authenticator dependencies. @@ -298,9 +290,8 @@ public function getUserId() * Log a user into the application without sessions or cookies. * * @param AuthenticatableInterface $user The authenticated user. - * @return void */ - public function setUser(AuthenticatableInterface $user) + public function setUser(AuthenticatableInterface $user): void { $this->authenticatedUser = $user; $this->isLoggedOut = false; @@ -312,9 +303,8 @@ public function setUser(AuthenticatableInterface $user) * Log a user into the application without sessions or cookies. * * @param mixed $userId The authenticated user ID. - * @return void */ - public function setUserById($userId) + public function setUserById($userId): void { $user = $this->createUser(); $user->loadFrom($user->getAuthIdKey(), $userId); @@ -376,9 +366,8 @@ public function getAuthenticationToken() * * @param AuthenticatableInterface $user The authenticated user to log in. * @param boolean $remember Whether to "remember" the user or not. - * @return void */ - public function login(AuthenticatableInterface $user, $remember = false) + public function login(AuthenticatableInterface $user, $remember = false): void { if (!$user->getAuthId()) { return; @@ -395,10 +384,8 @@ public function login(AuthenticatableInterface $user, $remember = false) /** * Log the user out of the application. - * - * @return void */ - public function logout() + public function logout(): void { $user = $this->user(); @@ -462,7 +449,7 @@ public function authenticateByPassword($identifier, $password, $remember = false $this->logger->warning(sprintf( '[Authenticator] Invalid login attempt for user "%s" (%s): The table "%s" does not exist.', $identifier, - get_class($user), + $user::class, $user->source()->table() )); return null; @@ -478,7 +465,7 @@ public function authenticateByPassword($identifier, $password, $remember = false // Validate password $hashedPassword = $user->getAuthPassword(); - if (password_verify($password, $hashedPassword)) { + if (password_verify($password, (string) $hashedPassword)) { if (password_needs_rehash($hashedPassword, PASSWORD_DEFAULT)) { $this->rehashUserPassword($user, $password); } @@ -492,7 +479,7 @@ public function authenticateByPassword($identifier, $password, $remember = false $this->logger->warning(sprintf( '[Authenticator] Invalid login attempt for user "%s" (%s): invalid password.', $identifier, - get_class($user) + $user::class )); return null; @@ -510,7 +497,7 @@ protected function authenticateBySession() if (!$user->source()->tableExists()) { $this->logger->warning(sprintf( '[Authenticator] Invalid login attempt by session for a user (%s): The table "%s" does not exist.', - get_class($user), + $user::class, $user->source()->table() )); return null; @@ -573,7 +560,7 @@ protected function authenticateByToken() if (!$user->source()->tableExists()) { $this->logger->warning(sprintf( '[Authenticator] Invalid login attempt by token for a user (%s): The table "%s" does not exist.', - get_class($user), + $user::class, $user->source()->table() )); return null; @@ -601,9 +588,9 @@ protected function authenticateByToken() * @param AuthenticatableInterface|null $user The authenticated user to forget. * @return void */ - protected function deleteUserSession(AuthenticatableInterface $user = null) + protected function deleteUserSession(?AuthenticatableInterface $user = null) { - if ($user === null) { + if (!$user instanceof \Charcoal\User\Access\AuthenticatableInterface) { $user = $this->userFactory()->get($this->userType()); } @@ -619,7 +606,7 @@ protected function deleteUserSession(AuthenticatableInterface $user = null) * @param AuthenticatableInterface|null $user The authenticated user to forget. * @return void */ - protected function deleteUserTokens(AuthenticatableInterface $user = null) + protected function deleteUserTokens(?AuthenticatableInterface $user = null) { $authToken = $this->createToken(); if (!$authToken->isEnabled()) { @@ -628,7 +615,7 @@ protected function deleteUserTokens(AuthenticatableInterface $user = null) $authToken->deleteCookie(); - if ($user === null) { + if (!$user instanceof \Charcoal\User\Access\AuthenticatableInterface) { return; } @@ -732,7 +719,7 @@ public function validateLogin($identifier, $password) */ public function validateAuthIdentifier($identifier) { - return (is_string($identifier) && !empty($identifier)); + return (is_string($identifier) && ($identifier !== '' && $identifier !== '0')); } /** @@ -743,7 +730,7 @@ public function validateAuthIdentifier($identifier) */ public function validateAuthPassword($password) { - return (is_string($password) && !empty($password)); + return (is_string($password) && ($password !== '' && $password !== '0')); } /** @@ -781,7 +768,7 @@ public function rehashUserPassword(AuthenticatableInterface $user, $password, $u $userId = $user->getAuthId(); if ($update && $userId) { - $userClass = get_class($user); + $userClass = $user::class; $this->logger->info(sprintf( '[Authenticator] Rehashing password for user "%s" (%s)', @@ -839,7 +826,7 @@ public function changeUserPassword(AuthenticatableInterface $user, $password, $u $userId = $user->getAuthId(); if ($update && $userId) { - $userClass = get_class($user); + $userClass = $user::class; $this->logger->info(sprintf( '[Authenticator] Changing password for user "%s" (%s)', diff --git a/packages/user/src/Charcoal/User/AbstractAuthorizer.php b/packages/user/src/Charcoal/User/AbstractAuthorizer.php index 6d2361b6d..b9c1da52c 100644 --- a/packages/user/src/Charcoal/User/AbstractAuthorizer.php +++ b/packages/user/src/Charcoal/User/AbstractAuthorizer.php @@ -41,10 +41,8 @@ abstract class AbstractAuthorizer implements /** * The ACL service. - * - * @var Acl */ - private $acl; + private \Laminas\Permissions\Acl\Acl $acl; /** * @param array $data Class dependencies. @@ -334,9 +332,8 @@ protected function getAcl() /** * @param Acl $acl The ACL service. - * @return void */ - private function setAcl(Acl $acl) + private function setAcl(Acl $acl): void { $this->acl = $acl; } diff --git a/packages/user/src/Charcoal/User/AbstractUser.php b/packages/user/src/Charcoal/User/AbstractUser.php index c048b1825..b8f4163d6 100644 --- a/packages/user/src/Charcoal/User/AbstractUser.php +++ b/packages/user/src/Charcoal/User/AbstractUser.php @@ -29,17 +29,13 @@ abstract class AbstractUser extends Content implements * The email address should be unique and mandatory. * * It is also used as the login name. - * - * @var string */ - private $email; + private ?string $email = null; /** * The password is stored encrypted in the (database) storage. - * - * @var string|null */ - private $password; + private ?string $password = null; /** * The display name serves as a human-readable identifier for the user. @@ -53,42 +49,32 @@ abstract class AbstractUser extends Content implements * * @var string[] */ - private $roles = []; + private array $roles = []; /** * The timestamp of the latest (successful) login. - * - * @var DateTimeInterface|null */ - private $lastLoginDate; + private ?\DateTimeInterface $lastLoginDate = null; /** * The IP address during the latest (successful) login. - * - * @var string|null */ - private $lastLoginIp; + private ?string $lastLoginIp = null; /** * The timestamp of the latest password change. - * - * @var DateTimeInterface|null */ - private $lastPasswordDate; + private ?\DateTimeInterface $lastPasswordDate = null; /** * The IP address during the latest password change. - * - * @var string|null */ - private $lastPasswordIp; + private ?string $lastPasswordIp = null; /** * The token value for the "remember me" session. - * - * @var string|null */ - private $loginToken; + private ?string $loginToken = null; /** * The user preferences. @@ -192,7 +178,7 @@ public function setRoles($roles) ); } - $this->roles = array_filter(array_map('trim', $roles), 'strlen'); + $this->roles = array_filter(array_map(trim(...), $roles), strlen(...)); return $this; } @@ -468,7 +454,8 @@ public function getAuthLoginTokenKey() * @param ValidatorInterface $v Optional. A custom validator object to use for validation. If null, use object's. * @return boolean */ - public function validate(ValidatorInterface &$v = null) + #[\Override] + public function validate(?ValidatorInterface &$v = null) { $result = parent::validate($v); @@ -501,7 +488,7 @@ protected function validateLoginRequired() return false; } - if (strpos($userKey, 'email') !== false && !filter_var($userLogin, FILTER_VALIDATE_EMAIL)) { + if (str_contains($userKey, 'email') && !filter_var($userLogin, FILTER_VALIDATE_EMAIL)) { $this->validator()->error( 'User Credentials: Email format is incorrect.', $userKey @@ -527,7 +514,7 @@ protected function validateLoginUnique() $originalUser = $factory->create($objType)->load($this->getAuthId()); - if (mb_strtolower($originalUser->getAuthIdentifier()) !== mb_strtolower($userLogin)) { + if (mb_strtolower((string) $originalUser->getAuthIdentifier()) !== mb_strtolower((string) $userLogin)) { $existingUser = $factory->create($objType)->loadFrom($userKey, $userLogin); /** Check for existing user with given email. */ if (!empty($existingUser->getAuthId())) { diff --git a/packages/user/src/Charcoal/User/Access/AuthenticatableInterface.php b/packages/user/src/Charcoal/User/Access/AuthenticatableInterface.php index 874ba4c46..e8078d049 100644 --- a/packages/user/src/Charcoal/User/Access/AuthenticatableInterface.php +++ b/packages/user/src/Charcoal/User/Access/AuthenticatableInterface.php @@ -1,5 +1,7 @@ getAuthLoginTokenKey(); $this[$key] = $value; diff --git a/packages/user/src/Charcoal/User/Acl/Manager.php b/packages/user/src/Charcoal/User/Acl/Manager.php index 5f2b8961a..a10503fe1 100644 --- a/packages/user/src/Charcoal/User/Acl/Manager.php +++ b/packages/user/src/Charcoal/User/Acl/Manager.php @@ -33,9 +33,8 @@ public function __construct(array $data) * @param Acl $acl The Laminas Acl instant to load permissions to. * @param array $permissions The array of permissions, in [role=>details] array. * @param string $resource The Acl resource (string identifier) to load roles and permissions into. - * @return void */ - public function loadPermissions(Acl &$acl, array $permissions, $resource = '') + public function loadPermissions(Acl &$acl, array $permissions, $resource = ''): void { foreach ($permissions as $role => $rolePermissions) { $this->addRoleAndPermissions($acl, $role, $rolePermissions, $resource); @@ -47,9 +46,8 @@ public function loadPermissions(Acl &$acl, array $permissions, $resource = '') * @param PDO $dbh The PDO database instance. * @param string $table The table where to fetch the roles and permissions. * @param string $resource The Acl resource (string identifier) to load roles and permissions into. - * @return void */ - public function loadDatabasePermissions(Acl &$acl, PDO $dbh, $table, $resource = '') + public function loadDatabasePermissions(Acl &$acl, PDO $dbh, $table, $resource = ''): void { // Quick-and-dirty sanitization $table = preg_replace('/[^A-Za-z0-9_]/', '', $table); @@ -77,13 +75,12 @@ public function loadDatabasePermissions(Acl &$acl, PDO $dbh, $table, $resource = * @param string $role The role (string identifier) to add. * @param array $permissions The permissions details (array) to add. * @param string $resource The Acl resource (string identifier) to add roles and permissions into. - * @return void */ - private function addRoleAndPermissions(Acl &$acl, $role, array $permissions, $resource) + private function addRoleAndPermissions(Acl &$acl, $role, array $permissions, $resource): void { if (!$acl->hasRole($role)) { // Add role - $parentRole = isset($permissions['parent']) ? $permissions['parent'] : null; + $parentRole = $permissions['parent'] ?? null; $parentRole = $parentRole ?: null; $newRole = new GenericRole($role); $acl->addRole($newRole, $parentRole); @@ -107,11 +104,7 @@ private function addRoleAndPermissions(Acl &$acl, $role, array $permissions, $re } if (isset($permissions['denied'])) { - if (is_string($permissions['denied'])) { - $deniedPermissions = explode(',', $permissions['denied']); - } else { - $deniedPermissions = $permissions['denied']; - } + $deniedPermissions = is_string($permissions['denied']) ? explode(',', $permissions['denied']) : $permissions['denied']; foreach ($deniedPermissions as $denied) { $acl->deny($role, $resource, $denied); } diff --git a/packages/user/src/Charcoal/User/Acl/Permission.php b/packages/user/src/Charcoal/User/Acl/Permission.php index 69e76b8d1..4b2ce3b1a 100644 --- a/packages/user/src/Charcoal/User/Acl/Permission.php +++ b/packages/user/src/Charcoal/User/Acl/Permission.php @@ -21,10 +21,7 @@ class Permission extends AbstractModel implements CategorizableInterface use CategorizableTrait; use TranslatorAwareTrait; - /** - * @var string|null - */ - private $ident; + private ?string $ident = null; /** * @var \Charcoal\Translator\Translation|null @@ -33,9 +30,8 @@ class Permission extends AbstractModel implements CategorizableInterface /** * Permission can be used as a string (ident). - * - * @return string */ + #[\Override] public function __toString(): string { if ($this->ident === null) { @@ -44,10 +40,8 @@ public function __toString(): string return $this->ident; } - /** - * @return string - */ - public function key() + #[\Override] + public function key(): string { return 'ident'; } @@ -55,9 +49,8 @@ public function key() /** * @param string $ident The permission identifier. * @throws InvalidArgumentException If the ident is not a string. - * @return self */ - public function setIdent($ident) + public function setIdent($ident): static { if (!is_string($ident)) { throw new InvalidArgumentException( @@ -68,19 +61,15 @@ public function setIdent($ident) return $this; } - /** - * @return string|null - */ - public function getIdent() + public function getIdent(): ?string { return $this->ident; } /** * @param mixed $name The permission name / label. - * @return self */ - public function setName($name) + public function setName($name): static { $this->name = $this->translator()->translation($name); return $this; @@ -98,6 +87,7 @@ public function getName() * @param Container $container Pimple DI container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); diff --git a/packages/user/src/Charcoal/User/Acl/PermissionCategory.php b/packages/user/src/Charcoal/User/Acl/PermissionCategory.php index e502fbe93..fc50e719f 100644 --- a/packages/user/src/Charcoal/User/Acl/PermissionCategory.php +++ b/packages/user/src/Charcoal/User/Acl/PermissionCategory.php @@ -1,5 +1,7 @@ name = $this->translator()->translation($name); return $this; @@ -37,10 +38,7 @@ public function getName() return $this->name; } - /** - * @return array - */ - public function loadCategoryItems() + public function loadCategoryItems(): array { return []; } diff --git a/packages/user/src/Charcoal/User/Acl/Role.php b/packages/user/src/Charcoal/User/Acl/Role.php index d564aacd1..8339c021c 100644 --- a/packages/user/src/Charcoal/User/Acl/Role.php +++ b/packages/user/src/Charcoal/User/Acl/Role.php @@ -54,21 +54,14 @@ class Role extends AbstractModel */ public $denied; - /** - * @var boolean - */ - private $superuser = false; + private bool $superuser = false; - /** - * @var integer - */ - private $position; + private ?int $position = null; /** * ACL Role can be used as a string (ident). - * - * @return string */ + #[\Override] public function __toString(): string { if ($this->ident === null) { @@ -77,19 +70,16 @@ public function __toString(): string return $this->ident; } - /** - * @return string - */ - public function key() + #[\Override] + public function key(): string { return 'ident'; } /** * @param string|Role $parent Role's parent. - * @return self */ - public function setParent($parent) + public function setParent($parent): static { if ($parent instanceof self) { $parent = $parent['ident']; @@ -109,9 +99,8 @@ public function getParent() /** * @param mixed $name The user-friendly name of this role. - * @return self */ - public function setName($name) + public function setName($name): static { $this->name = $this->p('name')->parseVal($name); return $this; @@ -127,13 +116,12 @@ public function getName() /** * @param mixed $allowed The allowed permissions for this role. - * @return self */ - public function setAllowed($allowed) + public function setAllowed($allowed): static { $allowed = $this->p('allowed')->parseVal($allowed); if (is_array($allowed)) { - $allowed = array_filter(array_map('trim', $allowed)); + $allowed = array_filter(array_map(trim(...), $allowed)); } $this->allowed = $allowed; @@ -150,13 +138,12 @@ public function getAllowed() /** * @param mixed $denied The denied permissions for this role. - * @return self */ - public function setDenied($denied) + public function setDenied($denied): static { $denied = $this->p('denied')->parseVal($denied); if (is_array($denied)) { - $denied = array_filter(array_map('trim', $denied)); + $denied = array_filter(array_map(trim(...), $denied)); } $this->denied = $denied; @@ -173,27 +160,22 @@ public function getDenied() /** * @param boolean $isSuper The superuser flag. - * @return self */ - public function setSuperuser($isSuper) + public function setSuperuser($isSuper): static { - $this->superuser = !!$isSuper; + $this->superuser = (bool) $isSuper; return $this; } - /** - * @return boolean - */ - public function getSuperuser() + public function getSuperuser(): bool { return $this->superuser; } /** * @param integer|string|null $position The role's ordering position. - * @return self */ - public function setPosition($position) + public function setPosition($position): static { $this->position = (int)$position; return $this; @@ -202,7 +184,7 @@ public function setPosition($position) /** * @return integer */ - public function getPosition() + public function getPosition(): ?int { return $this->position; } @@ -211,6 +193,7 @@ public function getPosition() * @param Container $container Pimple DI container. * @return void */ + #[\Override] protected function setDependencies(Container $container) { parent::setDependencies($container); diff --git a/packages/user/src/Charcoal/User/AclAwareTrait.php b/packages/user/src/Charcoal/User/AclAwareTrait.php index 8b3121dd9..7d84309ff 100644 --- a/packages/user/src/Charcoal/User/AclAwareTrait.php +++ b/packages/user/src/Charcoal/User/AclAwareTrait.php @@ -40,7 +40,7 @@ protected function acl() if (!$this->acl) { throw new RuntimeException(sprintf( 'ACL service is not defined for "%s"', - get_class($this) + $this::class )); } diff --git a/packages/user/src/Charcoal/User/AuthAwareInterface.php b/packages/user/src/Charcoal/User/AuthAwareInterface.php index d913d76aa..0b5e21c64 100644 --- a/packages/user/src/Charcoal/User/AuthAwareInterface.php +++ b/packages/user/src/Charcoal/User/AuthAwareInterface.php @@ -1,5 +1,7 @@ authorizer()->userAllowed($authUser, $permissions); - return $authorized; + return $this->authorizer()->userAllowed($authUser, $permissions); } /** @@ -84,7 +83,7 @@ protected function authenticator() if (!$this->authenticator) { throw new RuntimeException(sprintf( 'Authenticator service is not defined for "%s"', - get_class($this) + $this::class )); } @@ -113,7 +112,7 @@ protected function authorizer() if (!$this->authorizer) { throw new RuntimeException(sprintf( 'Authorizer service is not defined for "%s"', - get_class($this) + $this::class )); } @@ -133,7 +132,7 @@ protected function setRequiredAclPermissions($permissions) } if (is_string($permissions)) { $permissions = explode(',', $permissions); - $permissions = array_map('trim', $permissions); + $permissions = array_map(trim(...), $permissions); } if (!is_array($permissions)) { throw new InvalidArgumentException( diff --git a/packages/user/src/Charcoal/User/AuthToken.php b/packages/user/src/Charcoal/User/AuthToken.php index eb0de07b4..e5aa16655 100644 --- a/packages/user/src/Charcoal/User/AuthToken.php +++ b/packages/user/src/Charcoal/User/AuthToken.php @@ -1,5 +1,7 @@ $expiry, 'path' => $path, 'domain' => '', 'secure' => $secure]); } /** @@ -43,13 +43,13 @@ public function deleteCookie() $path = $metadata['tokenPath']; $secure = $metadata['httpsOnly']; - return setcookie($name, '', $expiry, $path, '', $secure); + return setcookie($name, '', ['expires' => $expiry, 'path' => $path, 'domain' => '', 'secure' => $secure]); } /** * @return array|null `[ 'ident' => '', 'token' => '' ] */ - public function getTokenDataFromCookie() + public function getTokenDataFromCookie(): ?array { if (!$this->isEnabled()) { return null; @@ -63,7 +63,7 @@ public function getTokenDataFromCookie() } $cookie = $_COOKIE[$name]; - $data = array_pad(explode(';', $cookie), 2, null); + $data = array_pad(explode(';', (string) $cookie), 2, null); if (!isset($data[0]) || !isset($data[1])) { return null; } diff --git a/packages/user/src/Charcoal/User/AuthTokenInterface.php b/packages/user/src/Charcoal/User/AuthTokenInterface.php index 2fa04d731..b88813d78 100644 --- a/packages/user/src/Charcoal/User/AuthTokenInterface.php +++ b/packages/user/src/Charcoal/User/AuthTokenInterface.php @@ -1,5 +1,7 @@ true, 'token_name' => 'charcoal_user_login', 'token_duration' => '15 days', 'token_path' => '', 'https_only' => false, ]); - return $defaults; } /** * @param boolean $enabled The enabled flag. - * @return self */ - public function setEnabled($enabled) + public function setEnabled($enabled): static { - $this->enabled = !!$enabled; + $this->enabled = (bool) $enabled; return $this; } /** * @return boolean */ - public function getEnabled() + public function getEnabled(): ?bool { return $this->enabled; } /** * @param boolean $httpsOnly The "HTTPS only" flag. - * @return self */ - public function setHttpsOnly($httpsOnly) + public function setHttpsOnly($httpsOnly): static { - $this->httpsOnly = !!$httpsOnly; + $this->httpsOnly = (bool) $httpsOnly; return $this; } /** * @return boolean */ - public function getHttpsOnly() + public function getHttpsOnly(): ?bool { return $this->httpsOnly; } @@ -89,9 +72,8 @@ public function getHttpsOnly() /** * @param string $name The token name. * @throws InvalidArgumentException If the token name is not a string. - * @return self */ - public function setTokenName($name) + public function setTokenName($name): static { if (!is_string($name)) { throw new InvalidArgumentException( @@ -105,7 +87,7 @@ public function setTokenName($name) /** * @return string */ - public function getTokenName() + public function getTokenName(): ?string { return $this->tokenName; } @@ -113,9 +95,8 @@ public function getTokenName() /** * @param string $duration The token duration, or duration. Ex: "15 days". * @throws InvalidArgumentException If the token name is not a string. - * @return self */ - public function setTokenDuration($duration) + public function setTokenDuration($duration): static { if (!is_string($duration)) { throw new InvalidArgumentException( @@ -129,18 +110,17 @@ public function setTokenDuration($duration) /** * @return string */ - public function getTokenDuration() + public function getTokenDuration(): ?string { return $this->tokenDuration; } /** - * @deprecated In favour of {@see self::setTokenName()}. * * @param string $name The cookie name. - * @return self */ - public function setCookieName($name) + #[\Deprecated(message: 'In favour of {@see self::setTokenName()}.')] + public function setCookieName($name): static { trigger_error( 'Auth token option "cookie_name" is deprecated in favour of "token_name"', @@ -152,11 +132,10 @@ public function setCookieName($name) } /** - * @deprecated In favour of {@see self::getTokenName()}. - * * @return string */ - public function getCookieName() + #[\Deprecated(message: 'In favour of {@see self::getTokenName()}.')] + public function getCookieName(): ?string { trigger_error( 'Auth token option "cookie_duration" is deprecated in favour of "token_duration"', @@ -167,12 +146,11 @@ public function getCookieName() } /** - * @deprecated In favour of {@see self::setTokenDuration()}. * * @param string $duration The cookie duration, or duration. Ex: "15 days". - * @return self */ - public function setCookieDuration($duration) + #[\Deprecated(message: 'In favour of {@see self::setTokenDuration()}.')] + public function setCookieDuration($duration): static { trigger_error( 'Auth token option "cookie_duration" is deprecated in favour of "token_duration"', @@ -184,11 +162,10 @@ public function setCookieDuration($duration) } /** - * @deprecated In favour of {@see self::getTokenDuration()}. - * * @return string */ - public function getCookieDuration() + #[\Deprecated(message: 'In favour of {@see self::getTokenDuration()}.')] + public function getCookieDuration(): ?string { trigger_error( 'Auth token option "cookie_duration" is deprecated in favour of "token_duration"', diff --git a/packages/user/src/Charcoal/User/Authenticator.php b/packages/user/src/Charcoal/User/Authenticator.php index 8e2b643c8..c1990340b 100644 --- a/packages/user/src/Charcoal/User/Authenticator.php +++ b/packages/user/src/Charcoal/User/Authenticator.php @@ -19,9 +19,9 @@ class Authenticator extends AbstractAuthenticator * * @param AuthenticatableInterface $user The authenticated user to log in. * @param boolean $remember Whether to "remember" the user or not. - * @return void */ - public function login(AuthenticatableInterface $user, $remember = false) + #[\Override] + public function login(AuthenticatableInterface $user, $remember = false): void { parent::login($user, $remember); @@ -36,12 +36,11 @@ public function login(AuthenticatableInterface $user, $remember = false) * @param AuthenticatableInterface $user The user to validate. * @return boolean */ + #[\Override] public function validateAuthentication(AuthenticatableInterface $user) { - if ($user instanceof ContentInterface) { - if (!$user['active']) { - return false; - } + if ($user instanceof ContentInterface && !$user['active']) { + return false; } return parent::validateAuthentication($user); @@ -70,7 +69,7 @@ public function touchUserLogin(AuthenticatableInterface $user, $update = true) $userId = $user->getAuthId(); if ($update && $userId) { - $userClass = get_class($user); + $userClass = $user::class; $this->logger->info(sprintf( 'Updating last login fields for user "%s" (%s)', @@ -80,7 +79,7 @@ public function touchUserLogin(AuthenticatableInterface $user, $update = true) } $user['lastLoginDate'] = 'now'; - $user['lastLoginIp'] = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null; + $user['lastLoginIp'] = $_SERVER['REMOTE_ADDR'] ?? null; if ($update && $userId) { $result = $user->update([ @@ -115,6 +114,7 @@ public function touchUserLogin(AuthenticatableInterface $user, $update = true) * @throws InvalidArgumentException If the password is invalid. * @return boolean Returns TRUE if the password was changed, or FALSE otherwise. */ + #[\Override] public function changeUserPassword(AuthenticatableInterface $user, $password, $update = true) { if (!($user instanceof UserInterface)) { @@ -130,7 +130,7 @@ public function changeUserPassword(AuthenticatableInterface $user, $password, $u $userId = $user->getAuthId(); if ($update && $userId) { - $userClass = get_class($user); + $userClass = $user::class; $this->logger->info(sprintf( '[Authenticator] Changing password for user "%s" (%s)', @@ -143,7 +143,7 @@ public function changeUserPassword(AuthenticatableInterface $user, $password, $u $user[$passwordKey] = password_hash($password, PASSWORD_DEFAULT); $user['lastPasswordDate'] = 'now'; - $user['lastPasswordIp'] = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null; + $user['lastPasswordIp'] = $_SERVER['REMOTE_ADDR'] ?? null; if ($update && $userId) { $result = $user->update([ diff --git a/packages/user/src/Charcoal/User/AuthenticatorInterface.php b/packages/user/src/Charcoal/User/AuthenticatorInterface.php index 9ead4f012..ee7ef572e 100644 --- a/packages/user/src/Charcoal/User/AuthenticatorInterface.php +++ b/packages/user/src/Charcoal/User/AuthenticatorInterface.php @@ -1,5 +1,7 @@ defaultResource; } @@ -117,9 +113,8 @@ protected function getDefaultResource() /** * @param string|null $resource The ACL resource identifier. * @throws InvalidArgumentException If the resource identifier is not a string. - * @return void */ - private function setDefaultResource($resource) + private function setDefaultResource($resource): void { if (!is_string($resource) && $resource !== null) { throw new InvalidArgumentException( diff --git a/packages/user/src/Charcoal/User/AuthorizerInterface.php b/packages/user/src/Charcoal/User/AuthorizerInterface.php index f4b5a304a..80c9508d4 100644 --- a/packages/user/src/Charcoal/User/AuthorizerInterface.php +++ b/packages/user/src/Charcoal/User/AuthorizerInterface.php @@ -1,5 +1,7 @@ $container['logger'], - 'user_type' => User::class, - 'user_factory' => $container['model/factory'], - 'token_type' => AuthToken::class, - 'token_factory' => $container['model/factory'], - ]); - }; + $container['authenticator'] = (fn(Container $container): \Charcoal\User\Authenticator => new Authenticator([ + 'logger' => $container['logger'], + 'user_type' => User::class, + 'user_factory' => $container['model/factory'], + 'token_type' => AuthToken::class, + 'token_factory' => $container['model/factory'], + ])); } if (!isset($container['authorizer'])) { @@ -45,22 +42,18 @@ public function register(Container $container) * @param Container $container The Pimple DI container. * @return Authorizer */ - $container['authorizer'] = function (Container $container) { - return new Authorizer([ - 'logger' => $container['logger'], - 'acl' => $container['authorizer/acl'], - 'resource' => 'charcoal', - ]); - }; + $container['authorizer'] = (fn(Container $container): \Charcoal\User\Authorizer => new Authorizer([ + 'logger' => $container['logger'], + 'acl' => $container['authorizer/acl'], + 'resource' => 'charcoal', + ])); } if (!isset($container['authorizer/acl'])) { /** * @return Acl */ - $container['authorizer/acl'] = function () { - return new Acl(); - }; + $container['authorizer/acl'] = (fn(): \Laminas\Permissions\Acl\Acl => new Acl()); } } } diff --git a/packages/user/src/Charcoal/User/UserInterface.php b/packages/user/src/Charcoal/User/UserInterface.php index 45e787a18..ef2851d32 100644 --- a/packages/user/src/Charcoal/User/UserInterface.php +++ b/packages/user/src/Charcoal/User/UserInterface.php @@ -1,5 +1,7 @@ setAccessible(true); - return $reflected; + return new ReflectionMethod($class, $name); } /** @@ -38,7 +35,7 @@ public function getMethod($class, $name) public function callMethod($object, $name, array $args = []) { $method = $this->getMethod($object, $name); - if (empty($args)) { + if ($args === []) { return $method->invoke($object); } else { return $method->invokeArgs($object, $args); @@ -65,13 +62,10 @@ public function callMethodWith($object, $name, ...$args) * * @param mixed $class The class name or object that contains the property. * @param string $name The property name to reflect. - * @return ReflectionProperty */ - public function getProperty($class, $name) + public function getProperty($class, $name): \ReflectionProperty { - $reflected = new ReflectionProperty($class, $name); - $reflected->setAccessible(true); - return $reflected; + return new ReflectionProperty($class, $name); } /** @@ -92,9 +86,8 @@ public function getPropertyValue($object, $name) * @param mixed $object The object to access. * @param string $name The property name to affect. * @param mixed $value The new value. - * @return void */ - public function setPropertyValue($object, $name, $value) + public function setPropertyValue($object, $name, $value): void { $this->getProperty($object, $name)->setValue($object, $value); } diff --git a/packages/user/tests/Charcoal/User/AbstractUserTest.php b/packages/user/tests/Charcoal/User/AbstractUserTest.php index 3a91b1d12..81c56a08e 100644 --- a/packages/user/tests/Charcoal/User/AbstractUserTest.php +++ b/packages/user/tests/Charcoal/User/AbstractUserTest.php @@ -21,22 +21,16 @@ class AbstractUserTest extends AbstractTestCase { /** * Tested Class. - * - * @var UserInterface */ - private $obj; + private \Charcoal\User\UserInterface $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ protected function setUp(): void { @@ -66,19 +60,13 @@ protected function setUp(): void ->will($this->returnValue('charcoal.user')); } - /** - * @return void - */ - public function testKey() + public function testKey(): void { $obj = $this->obj; $this->assertEquals('id', $obj->key()); } - /** - * @return void - */ - public function testDefaultValues() + public function testDefaultValues(): void { $obj = $this->obj; $this->assertTrue($obj['active']); @@ -88,10 +76,8 @@ public function testDefaultValues() * Assert that the `setData` method: * - is chainable * - set the various properties - * - * @return void */ - public function testSetData() + public function testSetData(): void { $obj = $this->obj; $ret = $obj->setData([ @@ -106,10 +92,7 @@ public function testSetData() $this->assertFalse($obj['active']); } - /** - * @return void - */ - public function testSetEmail() + public function testSetEmail(): void { $ret = $this->obj->setEmail('test@example.com'); $this->assertSame($ret, $this->obj); @@ -125,10 +108,7 @@ public function testSetEmail() $this->obj->setEmail(false); } - /** - * @return void - */ - public function testSetRoles() + public function testSetRoles(): void { $ret = $this->obj->setRoles(null); $this->assertSame($ret, $this->obj); @@ -144,10 +124,7 @@ public function testSetRoles() $this->obj->setRoles(42); } - /** - * @return void - */ - public function testSetLastLoginDate() + public function testSetLastLoginDate(): void { $ret = $this->obj->setLastLoginDate('today'); $this->assertSame($ret, $this->obj); @@ -172,10 +149,7 @@ public function testSetLastLoginDate() $this->obj->setLastLoginDate(false); } - /** - * @return void - */ - public function testSetLastLoginIp() + public function testSetLastLoginIp(): void { $ret = $this->obj->setLastLoginIp('8.8.8.8'); $this->assertSame($ret, $this->obj); @@ -197,10 +171,7 @@ public function testSetLastLoginIp() $this->obj->setLastLoginIp(false); } - /** - * @return void - */ - public function testSetLastPasswordDate() + public function testSetLastPasswordDate(): void { $ret = $this->obj->setLastPasswordDate('today'); $this->assertSame($ret, $this->obj); @@ -225,10 +196,7 @@ public function testSetLastPasswordDate() $this->obj->setLastPasswordDate(false); } - /** - * @return void - */ - public function testSetLastPasswordIp() + public function testSetLastPasswordIp(): void { $ret = $this->obj->setLastPasswordIp('8.8.8.8'); $this->assertSame($ret, $this->obj); @@ -253,12 +221,10 @@ public function testSetLastPasswordIp() /** * Set up the service container. - * - * @return Container */ - private function container() + private function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerBaseServices($container); diff --git a/packages/user/tests/Charcoal/User/Acl/ManagerTest.php b/packages/user/tests/Charcoal/User/Acl/ManagerTest.php index 6c9bc2f6d..3c3a925d2 100644 --- a/packages/user/tests/Charcoal/User/Acl/ManagerTest.php +++ b/packages/user/tests/Charcoal/User/Acl/ManagerTest.php @@ -21,22 +21,16 @@ class ManagerTest extends AbstractTestCase { /** * Tested Class. - * - * @var Manager */ - private $obj; + private \Charcoal\User\Acl\Manager $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ protected function setUp(): void { @@ -47,10 +41,7 @@ protected function setUp(): void ]); } - /** - * @return void - */ - public function testLoadPermissions() + public function testLoadPermissions(): void { $acl = new Acl(); $rsc = new Resource('phpunit'); @@ -83,10 +74,7 @@ public function testLoadPermissions() $this->assertFalse($acl->isAllowed('test2', 'phpunit', 'baz')); } - /** - * @return void - */ - public function testLoadPermissionsWithStringPermissions() + public function testLoadPermissionsWithStringPermissions(): void { $acl = new Acl(); $rsc = new Resource('phpunit'); @@ -115,12 +103,10 @@ public function testLoadPermissionsWithStringPermissions() /** * Set up the service container. - * - * @return Container */ - private function container() + private function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerBaseServices($container); diff --git a/packages/user/tests/Charcoal/User/Acl/PermissionCategoryTest.php b/packages/user/tests/Charcoal/User/Acl/PermissionCategoryTest.php index a11906d28..e7d86eff5 100644 --- a/packages/user/tests/Charcoal/User/Acl/PermissionCategoryTest.php +++ b/packages/user/tests/Charcoal/User/Acl/PermissionCategoryTest.php @@ -17,22 +17,16 @@ class PermissionCategoryTest extends AbstractTestCase { /** * Tested Class. - * - * @var PermissionCategory */ - private $obj; + private \Charcoal\User\Acl\PermissionCategory $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ protected function setUp(): void { @@ -44,10 +38,7 @@ protected function setUp(): void ]); } - /** - * @return void - */ - public function testSetName() + public function testSetName(): void { $ret = $this->obj->setName('foobar'); $this->assertSame($ret, $this->obj); @@ -56,12 +47,10 @@ public function testSetName() /** * Set up the service container. - * - * @return Container */ - private function container() + private function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerBaseServices($container); diff --git a/packages/user/tests/Charcoal/User/Acl/PermissionTest.php b/packages/user/tests/Charcoal/User/Acl/PermissionTest.php index f0d74ee35..f53679e78 100644 --- a/packages/user/tests/Charcoal/User/Acl/PermissionTest.php +++ b/packages/user/tests/Charcoal/User/Acl/PermissionTest.php @@ -17,22 +17,16 @@ class PermissionTest extends AbstractTestCase { /** * Tested Class. - * - * @var Permission */ - private $obj; + private \Charcoal\User\Acl\Permission|array $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ protected function setUp(): void { @@ -44,10 +38,7 @@ protected function setUp(): void ]); } - /** - * @return void - */ - public function testToString() + public function testToString(): void { $this->assertEquals('', (string)$this->obj); $this->obj->setIdent('foobar'); @@ -59,18 +50,13 @@ public function testToString() /** * Assert that the object's key is the "ident" property. - * - * @return void */ - public function testKey() + public function testKey(): void { $this->assertEquals('ident', $this->obj->key()); } - /** - * @return void - */ - public function testSetIdent() + public function testSetIdent(): void { $ret = $this->obj->setIdent('foobar'); $this->assertSame($ret, $this->obj); @@ -80,20 +66,14 @@ public function testSetIdent() $this->obj->setIdent(false); } - /** - * @return void - */ - public function testSetName() + public function testSetName(): void { $ret = $this->obj->setName('foobar'); $this->assertSame($ret, $this->obj); $this->assertEquals('foobar', (string)$this->obj['name']); } - /** - * @return void - */ - public function testCastToString() + public function testCastToString(): void { $this->obj->setIdent('foobar'); $this->assertEquals('foobar', (string)$this->obj); @@ -103,12 +83,10 @@ public function testCastToString() /** * Set up the service container. - * - * @return Container */ - private function container() + private function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerBaseServices($container); diff --git a/packages/user/tests/Charcoal/User/Acl/RoleTest.php b/packages/user/tests/Charcoal/User/Acl/RoleTest.php index cf49d59f8..6c1dfe6d3 100644 --- a/packages/user/tests/Charcoal/User/Acl/RoleTest.php +++ b/packages/user/tests/Charcoal/User/Acl/RoleTest.php @@ -17,22 +17,16 @@ class RoleTest extends AbstractTestCase { /** * Tested Class. - * - * @var Role */ - private $obj; + private \Charcoal\User\Acl\Role $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ protected function setUp(): void { @@ -41,10 +35,7 @@ protected function setUp(): void $this->obj = $container['model/factory']->create(Role::class); } - /** - * @return void - */ - public function testToString() + public function testToString(): void { $this->assertEquals('', (string)$this->obj); $this->obj->ident = 'foobar'; @@ -56,28 +47,20 @@ public function testToString() /** * Assert that the object's key is the "ident" property. - * - * @return void */ - public function testKey() + public function testKey(): void { $this->assertEquals('ident', $this->obj->key()); } - /** - * @return void - */ - public function testSetParent() + public function testSetParent(): void { $ret = $this->obj->setParent('foo'); $this->assertSame($ret, $this->obj); $this->assertEquals('foo', $this->obj['parent']); } - /** - * @return void - */ - public function testSetAllowed() + public function testSetAllowed(): void { $this->assertNull($this->obj['allowed']); $ret = $this->obj->setAllowed('foo'); @@ -88,10 +71,7 @@ public function testSetAllowed() $this->assertSame(['bar', 'baz'], $this->obj['allowed']); } - /** - * @return void - */ - public function testSuperuser() + public function testSuperuser(): void { $this->assertFalse($this->obj['superuser']); $ret = $this->obj->setSuperuser(1); @@ -101,12 +81,10 @@ public function testSuperuser() /** * Set up the service container. - * - * @return Container */ - private function container() + private function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerBaseServices($container); diff --git a/packages/user/tests/Charcoal/User/AuthTokenMetadataTest.php b/packages/user/tests/Charcoal/User/AuthTokenMetadataTest.php index 65b8364f5..c22230df6 100644 --- a/packages/user/tests/Charcoal/User/AuthTokenMetadataTest.php +++ b/packages/user/tests/Charcoal/User/AuthTokenMetadataTest.php @@ -17,25 +17,18 @@ class AuthTokenMetadataTest extends AbstractTestCase { /** * Tested Class. - * - * @var AuthTokenMetadata */ - private $obj; + private \Charcoal\User\AuthTokenMetadata $obj; /** * Set up the test. - * - * @return void */ protected function setUp(): void { $this->obj = new AuthTokenMetadata(); } - /** - * @return void - */ - public function testDefaults() + public function testDefaults(): void { $this->assertTrue($this->obj['enabled']); $this->assertEquals('charcoal_user_login', $this->obj['tokenName']); @@ -43,20 +36,14 @@ public function testDefaults() $this->assertFalse($this->obj['httpsOnly']); } - /** - * @return void - */ - public function testSetEnabled() + public function testSetEnabled(): void { $ret = $this->obj->setEnabled(false); $this->assertSame($ret, $this->obj); $this->assertFalse($this->obj['enabled']); } - /** - * @return void - */ - public function testSetTokenName() + public function testSetTokenName(): void { $ret = $this->obj->setTokenName('foobar'); $this->assertSame($ret, $this->obj); @@ -66,10 +53,7 @@ public function testSetTokenName() $this->obj->setTokenName(false); } - /** - * @return void - */ - public function testSetTokenDuration() + public function testSetTokenDuration(): void { $ret = $this->obj->setTokenDuration('2 month'); $this->assertSame($ret, $this->obj); diff --git a/packages/user/tests/Charcoal/User/AuthTokenTest.php b/packages/user/tests/Charcoal/User/AuthTokenTest.php index 16ddc4ecb..7780e979a 100644 --- a/packages/user/tests/Charcoal/User/AuthTokenTest.php +++ b/packages/user/tests/Charcoal/User/AuthTokenTest.php @@ -19,22 +19,16 @@ class AuthTokenTest extends AbstractTestCase { /** * Tested Class. - * - * @var AuthToken */ - private $obj; + private \Charcoal\User\AuthToken $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ protected function setUp(): void { @@ -47,38 +41,26 @@ protected function setUp(): void ]); } - /** - * @return void - */ - public function testSetKeyIsIdent() + public function testSetKeyIsIdent(): void { $this->assertEquals('ident', $this->obj->key()); } - /** - * @return void - */ - public function testSetIdent() + public function testSetIdent(): void { $ret = $this->obj->setIdent('foo'); $this->assertSame($ret, $this->obj); $this->assertEquals('foo', $this->obj['ident']); } - /** - * @return void - */ - public function testSetToken() + public function testSetToken(): void { $ret = $this->obj->setToken('foo'); $this->assertSame($ret, $this->obj); $this->assertEquals('foo', $this->obj['token']); } - /** - * @return void - */ - public function testSetUserId() + public function testSetUserId(): void { $ret = $this->obj->setUserId('foo'); $this->assertSame($ret, $this->obj); @@ -88,10 +70,7 @@ public function testSetUserId() $this->obj->setUserId([]); } - /** - * @return void - */ - public function testSetExpiry() + public function testSetExpiry(): void { $date = new DateTime('tomorrow'); $ret = $this->obj->setExpiry($date); @@ -102,10 +81,7 @@ public function testSetExpiry() $this->obj->setExpiry('fsdjkfsadg'); } - /** - * @return void - */ - public function testSetCreated() + public function testSetCreated(): void { $date = new DateTime('tomorrow'); $ret = $this->obj->setCreated($date); @@ -116,10 +92,7 @@ public function testSetCreated() $this->obj->setCreated('fsdjkfsadg'); } - /** - * @return void - */ - public function testSetLastModified() + public function testSetLastModified(): void { $date = new DateTime('tomorrow'); $ret = $this->obj->setLastModified($date); @@ -132,12 +105,10 @@ public function testSetLastModified() /** * Set up the service container. - * - * @return Container */ - private function container() + private function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerBaseServices($container); diff --git a/packages/user/tests/Charcoal/User/AuthenticatorTest.php b/packages/user/tests/Charcoal/User/AuthenticatorTest.php index b1022640f..ba812d2f0 100644 --- a/packages/user/tests/Charcoal/User/AuthenticatorTest.php +++ b/packages/user/tests/Charcoal/User/AuthenticatorTest.php @@ -19,22 +19,16 @@ class AuthenticatorTest extends AbstractTestCase { /** * Tested Class. - * - * @var Authenticator */ - private $obj; + private \Charcoal\User\Authenticator $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ protected function setUp(): void { @@ -47,22 +41,18 @@ protected function setUp(): void /** * Create a new Authenticator instance. - * - * @return Authenticator */ - public function createAuthenticator() + public function createAuthenticator(): \Charcoal\User\Authenticator { $container = $this->container(); - $authenticator = new Authenticator([ + return new Authenticator([ 'logger' => $container['logger'], 'user_type' => User::class, 'user_factory' => $container['model/factory'], 'token_type' => AuthToken::class, 'token_factory' => $container['model/factory'], ]); - - return $authenticator; } /** @@ -73,59 +63,41 @@ public function createAuthenticator() */ public function createUser(Authenticator $authenticator) { - $factoryMethod = new ReflectionMethod(Authenticator, 'userFactory'); + $factoryMethod = new ReflectionMethod(\AUTHENTICATOR, 'userFactory'); return $factoryMethod->invoke($authenticator)->create(User::class); } - /** - * @return void - */ - public function testConstructor() + public function testConstructor(): void { $this->assertInstanceOf(Authenticator::class, $this->obj); } - /** - * @return void - */ - public function testAuthenticate() + public function testAuthenticate(): void { $ret = $this->obj->authenticate(); $this->assertNull($ret); } - /** - * @return void - */ - public function testAuthenticateByPasswordInvalidEmail() + public function testAuthenticateByPasswordInvalidEmail(): void { $this->expectException(\InvalidArgumentException::class); $this->obj->authenticateByPassword([], ''); } - /** - * @return void - */ - public function testAuthenticateByPasswordInvalidPassword() + public function testAuthenticateByPasswordInvalidPassword(): void { $this->expectException(\InvalidArgumentException::class); $this->obj->authenticateByPassword('', []); } - /** - * @return void - */ - public function testAuthenticateByPasswordEmpty() + public function testAuthenticateByPasswordEmpty(): void { $this->expectException(\InvalidArgumentException::class); $this->obj->authenticateByPassword('', ''); } - /** - * @return void - */ - public function testAuthenticateByPassword() + public function testAuthenticateByPassword(): void { $this->assertNull($this->obj->authenticateByPassword('test', 'password')); } @@ -137,14 +109,13 @@ public function testAuthenticateByPassword() public function testUpdateSession() { $obj = $this->obj; - + $sessionKey = $obj::sessionKey(); $this->obj['id'] = 'foo'; $this->obj->saveToSession(); $this->assertEquals($_SESSION[$sessionKey], $this->obj['id']); } */ - /** * @return void */ @@ -153,22 +124,19 @@ public function testResetPassword() { $ret = $this->obj->resetPassword('foo'); $this->assertSame($ret, $this->obj); - + $this->obj['id'] = 'bar'; - + $this->expectException(InvalidArgumentException::class); $this->obj->resetPassword(false); } */ - /** * Set up the service container. - * - * @return Container */ - private function container() + private function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerBaseServices($container); diff --git a/packages/user/tests/Charcoal/User/AuthorizerTest.php b/packages/user/tests/Charcoal/User/AuthorizerTest.php index 8264f90e9..23ef31422 100644 --- a/packages/user/tests/Charcoal/User/AuthorizerTest.php +++ b/packages/user/tests/Charcoal/User/AuthorizerTest.php @@ -29,33 +29,25 @@ class AuthorizerTest extends AbstractTestCase /** * Tested Class. - * - * @var Authorizer */ - private $auth; + private \Charcoal\User\Authorizer $auth; /** * Store the ACL manager. - * - * @var Acl */ - private $acl; + private \Laminas\Permissions\Acl\Acl $acl; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ protected function setUp(): void { - $container = $this->container(); + $this->container(); $this->acl = new Acl(); $this->acl->addResource('area'); @@ -97,9 +89,8 @@ protected function setUpComplexRolesAndPrivileges() * Create an Authorizer instance. * * @param array $data Class dependencies. - * @return Authorizer */ - protected function createAuthorizer(array $data = []) + protected function createAuthorizer(array $data = []): \Charcoal\User\Authorizer { $container = $this->container(); @@ -109,9 +100,7 @@ protected function createAuthorizer(array $data = []) 'resource' => 'area', ]; - $authorizer = new Authorizer($data); - - return $authorizer; + return new Authorizer($data); } /** @@ -130,9 +119,7 @@ protected function mockAuthorizer(array $data = []) 'resource' => 'area', ]; - $stub = $this->getMockForAbstractClass(AbstractAuthorizer::class, [ $data ]); - - return $stub; + return $this->getMockForAbstractClass(AbstractAuthorizer::class, [ $data ]); } /** @@ -144,24 +131,18 @@ protected function createUser() { $container = $this->container(); - $user = $container['model/factory']->create(GenericUser::class); - - return $user; + return $container['model/factory']->create(GenericUser::class); } // Authorizer // ========================================================================= - - /** - * @return void - */ - public function testSetDefaultResourceWithNull() + public function testSetDefaultResourceWithNull(): void { $container = $this->container(); - $authorizer = $this->createAuthorizer([ + $this->createAuthorizer([ 'resource' => null ]); $auth = new Authorizer([ @@ -173,33 +154,24 @@ public function testSetDefaultResourceWithNull() $this->assertNull($this->callMethod($auth, 'getDefaultResource')); } - /** - * @return void - */ - public function testSetDefaultResourceWithBadValue() + public function testSetDefaultResourceWithBadValue(): void { $container = $this->container(); $this->expectException(\InvalidArgumentException::class); - $auth = new Authorizer([ + new Authorizer([ 'logger' => $container['logger'], 'acl' => $this->acl, 'resource' => 35, ]); } - /** - * @return void - */ - public function testRolesAllowedWithoutPermissions() + public function testRolesAllowedWithoutPermissions(): void { $this->assertTrue($this->auth->rolesAllowed([ 'guest' ], [])); } - /** - * @return void - */ - public function testRolesAllowed() + public function testRolesAllowed(): void { $this->assertFalse($this->auth->rolesAllowed([ 'guest' ], [ 'privilege1' ])); @@ -213,10 +185,7 @@ public function testRolesAllowed() $this->assertTrue($this->auth->rolesAllowed([ null ], [ 'privilege1' ])); } - /** - * @return void - */ - public function testUserAllowedWithoutPermissions() + public function testUserAllowedWithoutPermissions(): void { $user = $this->createUser(); $user['roles'] = 'guest'; @@ -224,10 +193,7 @@ public function testUserAllowedWithoutPermissions() $this->assertTrue($this->auth->userAllowed($user, [])); } - /** - * @return void - */ - public function testUserAllowed() + public function testUserAllowed(): void { $user = $this->createUser(); $user['roles'] = 'guest'; @@ -239,10 +205,7 @@ public function testUserAllowed() $this->assertFalse($this->auth->userAllowed($user, [ 'privilege1', 'privilege2' ])); } - /** - * @return void - */ - public function testIsAllowedWithDefaultResource() + public function testIsAllowedWithDefaultResource(): void { $this->acl->allow('guest', 'area', 'privilege1'); $this->assertFalse($this->auth->isAllowed('guest', null, 'privilege1')); @@ -254,11 +217,7 @@ public function testIsAllowedWithDefaultResource() // AbstractAuthorizer // ========================================================================= - - /** - * @return void - */ - public function testIsRoleGrantedCatchesAclExceptions() + public function testIsRoleGrantedCatchesAclExceptions(): void { $this->acl->allow('guest', null, [ 'privilege1', 'privilege2', 'privilege3' ]); $this->acl->deny('guest', null, [ 'privilege4', 'privilege5' ]); @@ -270,10 +229,7 @@ public function testIsRoleGrantedCatchesAclExceptions() $this->assertNull($this->auth->isRoleGrantedAny('guest', 'nonexistent', [ 'privilege4', 'privilege5' ])); } - /** - * @return void - */ - public function testIsRoleGrantedAll() + public function testIsRoleGrantedAll(): void { $this->acl->allow('guest', null, [ 'privilege1', 'privilege2', 'privilege3' ]); $this->acl->deny('guest', null, 'privilege4'); @@ -282,10 +238,7 @@ public function testIsRoleGrantedAll() $this->assertFalse($this->auth->isRoleGrantedAll('guest', null, [ 'privilege1', 'privilege4' ])); } - /** - * @return void - */ - public function testAllRolesGrantedAll() + public function testAllRolesGrantedAll(): void { $this->setUpComplexRolesAndPrivileges(); @@ -293,10 +246,7 @@ public function testAllRolesGrantedAll() $this->assertFalse($this->auth->allRolesGrantedAll([ 'aide', 'staff' ], null, [ 'edit', 'submit' ])); } - /** - * @return void - */ - public function testAnyRolesGrantedAll() + public function testAnyRolesGrantedAll(): void { $this->setUpComplexRolesAndPrivileges(); @@ -304,10 +254,7 @@ public function testAnyRolesGrantedAll() $this->assertFalse($this->auth->anyRolesGrantedAll([ 'aide', 'staff' ], null, [ 'edit', 'publish' ])); } - /** - * @return void - */ - public function testIsRoleGrantedAny() + public function testIsRoleGrantedAny(): void { $this->acl->allow('guest', null, [ 'privilege1', 'privilege2', 'privilege3' ]); $this->acl->deny('guest', null, [ 'privilege4', 'privilege5' ]); @@ -316,10 +263,7 @@ public function testIsRoleGrantedAny() $this->assertFalse($this->auth->isRoleGrantedAny('guest', null, [ 'privilege4', 'privilege5' ])); } - /** - * @return void - */ - public function testAllRolesGrantedAny() + public function testAllRolesGrantedAny(): void { $this->setUpComplexRolesAndPrivileges(); @@ -327,10 +271,7 @@ public function testAllRolesGrantedAny() $this->assertFalse($this->auth->allRolesGrantedAny([ 'aide', 'staff' ], null, [ 'publish', 'other' ])); } - /** - * @return void - */ - public function testAnyRolesGrantedAny() + public function testAnyRolesGrantedAny(): void { $this->setUpComplexRolesAndPrivileges(); @@ -338,10 +279,7 @@ public function testAnyRolesGrantedAny() $this->assertFalse($this->auth->anyRolesGrantedAny([ 'aide', 'staff' ], null, [ 'publish', 'other' ])); } - /** - * @return void - */ - public function testIsUserGrantedWithoutPermissions() + public function testIsUserGrantedWithoutPermissions(): void { $user = $this->createUser(); $user['roles'] = 'guest'; @@ -349,10 +287,7 @@ public function testIsUserGrantedWithoutPermissions() $this->assertFalse($this->auth->isUserGranted($user, null, null)); } - /** - * @return void - */ - public function testIsUserGranted() + public function testIsUserGranted(): void { $this->setUpComplexRolesAndPrivileges(); @@ -367,32 +302,24 @@ public function testIsUserGranted() // ACL // ========================================================================= - /** * Ensures that by default, Laminas ACL denies access to everything by all. - * - * @return void */ - public function testDefaultDeny() + public function testDefaultDeny(): void { $this->assertFalse($this->auth->isAllowed()); } /** * Ensures that by default, Laminas ACL can allow access to everything by all. - * - * @return void */ - public function testDefaultAllow() + public function testDefaultAllow(): void { $this->acl->allow(); $this->assertTrue($this->auth->isAllowed()); } - /** - * @return void - */ - public function testProxyMethods() + public function testProxyMethods(): void { $this->setUpComplexRolesAndPrivileges(); @@ -425,15 +352,12 @@ public function testProxyMethods() // Dependencies // ========================================================================= - /** * Set up the service container. - * - * @return Container */ - private function container() + private function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerBaseServices($container); diff --git a/packages/user/tests/Charcoal/User/ContainerProvider.php b/packages/user/tests/Charcoal/User/ContainerProvider.php index e2bf4f9c5..a60bb85e1 100644 --- a/packages/user/tests/Charcoal/User/ContainerProvider.php +++ b/packages/user/tests/Charcoal/User/ContainerProvider.php @@ -42,9 +42,8 @@ class ContainerProvider * Register the unit tests required services. * * @param Container $container A DI container. - * @return void */ - public function registerBaseServices(Container $container) + public function registerBaseServices(Container $container): void { $this->registerDatabase($container); $this->registerLogger($container); @@ -58,11 +57,10 @@ public function registerBaseServices(Container $container) * Note: Uses SQLite to create a database in memory. * * @param Container $container A DI container. - * @return void */ - public function registerDatabase(Container $container) + public function registerDatabase(Container $container): void { - $container['database'] = function () { + $container['database'] = function (): \PDO { $pdo = new PDO('sqlite::memory:'); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); return $pdo; @@ -73,167 +71,141 @@ public function registerDatabase(Container $container) * Setup the application's logging interface. * * @param Container $container A DI container. - * @return void */ - public function registerLogger(Container $container) + public function registerLogger(Container $container): void { - $container['logger'] = function () { - return new NullLogger(); - }; + $container['logger'] = (fn(): \Psr\Log\NullLogger => new NullLogger()); } /** * Setup the application's caching interface. * * @param Container $container A DI container. - * @return void */ - public function registerCache(Container $container) + public function registerCache(Container $container): void { - $container['cache'] = function () { - return new Pool(); - }; + $container['cache'] = (fn(): \Stash\Pool => new Pool()); } /** * Setup the framework's metadata loader interface. * * @param Container $container A DI container. - * @return void */ - public function registerMetadataLoader(Container $container) + public function registerMetadataLoader(Container $container): void { - $container['metadata/loader'] = function (Container $container) { - return new MetadataLoader([ - 'cache' => $container['cache'], - 'logger' => $container['logger'], - 'base_path' => realpath(__DIR__ . '/../../../'), - 'paths' => [ - 'metadata', - // Standalone repo - 'vendor/charcoal/user/metadata', - // Monorepo - '/../user/metadata', - ] - ]); - }; + $container['metadata/loader'] = (fn(Container $container): \Charcoal\Model\Service\MetadataLoader => new MetadataLoader([ + 'cache' => $container['cache'], + 'logger' => $container['logger'], + 'base_path' => realpath(__DIR__ . '/../../../'), + 'paths' => [ + 'metadata', + // Standalone repo + 'vendor/charcoal/user/metadata', + // Monorepo + '/../user/metadata', + ] + ])); } /** * Setup the framework's data source factory. * * @param Container $container A DI container. - * @return void */ - public function registerSourceFactory(Container $container) + public function registerSourceFactory(Container $container): void { $this->registerLogger($container); $this->registerCache($container); $this->registerDatabase($container); - $container['source/factory'] = function ($container) { - return new Factory([ - 'map' => [ - 'database' => DatabaseSource::class - ], - 'arguments' => [[ - 'logger' => $container['logger'], - 'cache' => $container['cache'], - 'pdo' => $container['database'] - ]] - ]); - }; + $container['source/factory'] = (fn($container): \Charcoal\Factory\GenericFactory => new Factory([ + 'map' => [ + 'database' => DatabaseSource::class + ], + 'arguments' => [[ + 'logger' => $container['logger'], + 'cache' => $container['cache'], + 'pdo' => $container['database'] + ]] + ])); } /** * Setup the framework's model factory. * * @param Container $container A DI container. - * @return void */ - public function registerModelFactory(Container $container) + public function registerModelFactory(Container $container): void { $this->registerSourceFactory($container); $this->registerMetadataLoader($container); $this->registerPropertyFactory($container); - $container['model/factory'] = function ($container) { - return new Factory([ - 'arguments' => [[ - 'container' => $container, - 'logger' => $container['logger'], - 'metadata_loader' => $container['metadata/loader'], - 'source_factory' => $container['source/factory'], - 'property_factory' => $container['property/factory'] - ]] - ]); - }; + $container['model/factory'] = (fn($container): \Charcoal\Factory\GenericFactory => new Factory([ + 'arguments' => [[ + 'container' => $container, + 'logger' => $container['logger'], + 'metadata_loader' => $container['metadata/loader'], + 'source_factory' => $container['source/factory'], + 'property_factory' => $container['property/factory'] + ]] + ])); } /** * Setup the framework's property factory. * * @param Container $container A DI container. - * @return void */ - public function registerPropertyFactory(Container $container) + public function registerPropertyFactory(Container $container): void { $this->registerLogger($container); $this->registerDatabase($container); $this->registerTranslator($container); - $container['property/factory'] = function (Container $container) { - return new Factory([ - 'resolver_options' => [ - 'prefix' => '\\Charcoal\\Property\\', - 'suffix' => 'Property' - ], - 'arguments' => [[ - 'container' => $container, - 'database' => $container['database'], - 'logger' => $container['logger'], - 'translator' => $container['translator'] - ]] - ]); - }; + $container['property/factory'] = (fn(Container $container): \Charcoal\Factory\GenericFactory => new Factory([ + 'resolver_options' => [ + 'prefix' => '\\Charcoal\\Property\\', + 'suffix' => 'Property' + ], + 'arguments' => [[ + 'container' => $container, + 'database' => $container['database'], + 'logger' => $container['logger'], + 'translator' => $container['translator'] + ]] + ])); } /** * Setup the framework's collection loader interface. * * @param Container $container A DI container. - * @return void */ - public function registerModelCollectionLoader(Container $container) + public function registerModelCollectionLoader(Container $container): void { - $container['model/collection/loader'] = function (Container $container) { - return new CollectionLoader([ - 'logger' => $container['logger'], - 'cache' => $container['cache'] - ]); - }; + $container['model/collection/loader'] = (fn(Container $container): \Charcoal\Loader\CollectionLoader => new CollectionLoader([ + 'logger' => $container['logger'], + 'cache' => $container['cache'] + ])); } /** * Setup the framework's Translator. * * @param Container $container A DI container. - * @return void */ - public function registerTranslator(Container $container) + public function registerTranslator(Container $container): void { - $container['locales/manager'] = function () { - return new LocalesManager([ - 'locales' => [ - 'en' => [ 'locale' => 'en-US' ] - ] - ]); - }; - - $container['translator'] = function (Container $container) { - return new Translator([ - 'manager' => $container['locales/manager'] - ]); - }; + $container['locales/manager'] = (fn(): \Charcoal\Translator\LocalesManager => new LocalesManager([ + 'locales' => [ + 'en' => [ 'locale' => 'en-US' ] + ] + ])); + + $container['translator'] = (fn(Container $container): \Charcoal\Translator\Translator => new Translator([ + 'manager' => $container['locales/manager'] + ])); } } diff --git a/packages/user/tests/Charcoal/User/GenericUserTest.php b/packages/user/tests/Charcoal/User/GenericUserTest.php index 16944ce3b..1cb1747a5 100644 --- a/packages/user/tests/Charcoal/User/GenericUserTest.php +++ b/packages/user/tests/Charcoal/User/GenericUserTest.php @@ -20,22 +20,16 @@ class GenericUserTest extends AbstractTestCase { /** * Tested Class. - * - * @var UserInterface */ - private $obj; + private \Charcoal\User\GenericUser $obj; /** * Store the service container. - * - * @var Container */ - private $container; + private ?\Pimple\Container $container = null; /** * Set up the test. - * - * @return void */ protected function setUp(): void { @@ -54,10 +48,7 @@ protected function setUp(): void ]); } - /** - * @return void - */ - public function testSessionKey() + public function testSessionKey(): void { $obj = $this->obj; @@ -67,12 +58,10 @@ public function testSessionKey() /** * Set up the service container. - * - * @return Container */ - private function container() + private function container(): \Pimple\Container { - if ($this->container === null) { + if (!$this->container instanceof \Pimple\Container) { $container = new Container(); $containerProvider = new ContainerProvider(); $containerProvider->registerBaseServices($container); diff --git a/packages/view/composer.json b/packages/view/composer.json index 3187d8755..782e151b1 100644 --- a/packages/view/composer.json +++ b/packages/view/composer.json @@ -1,8 +1,13 @@ { - "type": "library", "name": "charcoal/view", "description": "Charcoal View (templates rendering and tools)", - "keywords": ["charcoal", "view", "templates", "mustache", "twig"], + "keywords": [ + "charcoal", + "view", + "templates", + "mustache", + "twig" + ], "homepage": "https://charcoal.locomotive.ca", "license": "MIT", "authors": [ @@ -11,20 +16,16 @@ "homepage": "https://locomotive.ca" } ], - "extra": { - "branch-alias": { - "dev-main": "5.x-dev" - } - }, + "type": "library", "require": { - "php": "^7.4 || ^8.0", + "php": "^8.3", "ext-json": "*", "psr/http-message": "^1.0", "charcoal/config": "^5.1", "erusev/parsedown": "^1.7" }, "require-dev": { - "phpunit/phpunit": "^9.5", + "phpunit/phpunit": "^12.5", "squizlabs/php_codesniffer": "^3.5", "php-coveralls/php-coveralls": "^2.2", "mustache/mustache": "^2.11", @@ -32,13 +33,9 @@ "pimple/pimple": "^3.0", "slim/slim": "^3.7", "charcoal/translator": "^5.1", - "phpstan/phpstan": "^1.6", + "phpstan/phpstan": "^2.0", "charcoal/app": "^5.1" }, - "suggest": { - "mustache/mustache": "Mustache is suggested as the default templating engine.", - "twig/twig": "Twig is a second templating engine option, offering more features but not as integrated within Charcoal." - }, "autoload": { "psr-4": { "Charcoal\\View\\": "src/Charcoal/View" @@ -49,8 +46,10 @@ "Charcoal\\Tests\\": "tests/Charcoal" } }, - "replace": { - "locomotivemtl/charcoal-view": "*" + "extra": { + "branch-alias": { + "dev-main": "5.x-dev" + } }, "scripts": { "test": [ @@ -68,6 +67,13 @@ "phpunit": "php vendor/bin/phpunit --coverage-text --coverage-clover build/logs/clover.xml", "phpstan": "php vendor/bin/phpstan analyze -n -l3 src/" }, + "suggest": { + "mustache/mustache": "Mustache is suggested as the default templating engine.", + "twig/twig": "Twig is a second templating engine option, offering more features but not as integrated within Charcoal." + }, + "replace": { + "locomotivemtl/charcoal-view": "*" + }, "minimum-stability": "dev", "prefer-stable": true } diff --git a/packages/view/src/Charcoal/View/AbstractEngine.php b/packages/view/src/Charcoal/View/AbstractEngine.php index fd6af2e7d..6e2a13a92 100644 --- a/packages/view/src/Charcoal/View/AbstractEngine.php +++ b/packages/view/src/Charcoal/View/AbstractEngine.php @@ -16,10 +16,7 @@ */ abstract class AbstractEngine implements EngineInterface { - /** - * @var LoaderInterface - */ - private $loader; + private \Charcoal\View\LoaderInterface $loader; /** * The cache option. @@ -45,9 +42,6 @@ public function __construct(array $data) } } - /** - * @return string - */ abstract public function type(): string; /** @@ -84,7 +78,6 @@ public function loadTemplate(string $templateIdent): string /** * @param string $varName The name of the variable to set this template unto. * @param string|null $templateIdent The "dynamic template" to set. null to clear. - * @return void */ public function setDynamicTemplate(string $varName, ?string $templateIdent): void { @@ -97,7 +90,6 @@ public function setDynamicTemplate(string $varName, ?string $templateIdent): voi * @param mixed $cache A engine cache implementation, * an absolute path to the compiled views, * a boolean to enable/disable cache. - * @return void */ protected function setCache($cache): void { @@ -115,9 +107,6 @@ public function cache() } - /** - * @return LoaderInterface - */ protected function loader(): LoaderInterface { return $this->loader; @@ -127,7 +116,6 @@ protected function loader(): LoaderInterface /** * @param LoaderInterface $loader A loader instance. - * @return void */ private function setLoader(LoaderInterface $loader): void { diff --git a/packages/view/src/Charcoal/View/AbstractLoader.php b/packages/view/src/Charcoal/View/AbstractLoader.php index 4e8152872..8a36d18d5 100644 --- a/packages/view/src/Charcoal/View/AbstractLoader.php +++ b/packages/view/src/Charcoal/View/AbstractLoader.php @@ -41,7 +41,7 @@ public function __construct(?array $data = null) public function load($ident) { // Handle dynamic template - if (substr($ident, 0, 1) === '$') { + if (str_starts_with($ident, '$')) { $ident = $this->dynamicTemplate(substr($ident, 1)); } @@ -63,7 +63,6 @@ public function load($ident) /** * @param string $varName The name of the variable to get template ident from. - * @return string */ public function dynamicTemplate(string $varName): string { @@ -78,7 +77,6 @@ public function dynamicTemplate(string $varName): string * @param string $varName The name of the variable to set this template unto. * @param string|null $templateIdent The "dynamic template" to set or NULL to clear. * or if the template is not a string (and not null). - * @return void */ public function setDynamicTemplate(string $varName, ?string $templateIdent): void { @@ -92,24 +90,17 @@ public function setDynamicTemplate(string $varName, ?string $templateIdent): voi /** * @param string $varName The name of the variable to remove. - * @return void */ public function removeDynamicTemplate(string $varName): void { unset($this->dynamicTemplates[$varName]); } - /** - * @return void - */ public function clearDynamicTemplates(): void { $this->dynamicTemplates = []; } - /** - * @return string - */ protected function basePath(): string { return $this->basePath; @@ -117,9 +108,8 @@ protected function basePath(): string /** * @param string $basePath The base path to set. - * @return self */ - private function setBasePath(string $basePath) + private function setBasePath(string $basePath): static { $basePath = realpath($basePath); $this->basePath = rtrim($basePath, '/\\') . DIRECTORY_SEPARATOR; @@ -136,9 +126,8 @@ protected function paths(): array /** * @param string[] $paths The list of path to add. - * @return self */ - private function setPaths(array $paths) + private function setPaths(array $paths): static { $this->paths = []; @@ -151,9 +140,8 @@ private function setPaths(array $paths) /** * @param string $path The path to add to the load. - * @return self */ - private function addPath(string $path) + private function addPath(string $path): static { $this->paths[] = $this->resolvePath($path); @@ -162,13 +150,12 @@ private function addPath(string $path) /** * @param string $path The path to resolve. - * @return string */ private function resolvePath(string $path): string { $basePath = $this->basePath(); $path = rtrim($path, '/\\') . DIRECTORY_SEPARATOR; - if ($basePath && strpos($path, $basePath) === false) { + if ($basePath && !str_contains($path, $basePath)) { $path = $basePath . DIRECTORY_SEPARATOR . $path; } @@ -187,7 +174,7 @@ private function resolvePath(string $path): string */ protected function isTemplateString(string $ident): bool { - return strpos($ident, PHP_EOL) !== false; + return str_contains($ident, PHP_EOL); } /** @@ -223,7 +210,6 @@ protected function findTemplateFile(string $ident): ?string /** * @param string $ident The template identifier to convert to a filename. - * @return string */ abstract protected function filenameFromIdent(string $ident): string; } diff --git a/packages/view/src/Charcoal/View/AbstractView.php b/packages/view/src/Charcoal/View/AbstractView.php index 19d9bf7b2..0f46b84b7 100644 --- a/packages/view/src/Charcoal/View/AbstractView.php +++ b/packages/view/src/Charcoal/View/AbstractView.php @@ -17,10 +17,7 @@ */ abstract class AbstractView implements ViewInterface { - /** - * @var EngineInterface $engine - */ - private $engine; + private \Charcoal\View\EngineInterface $engine; /** * Build the object with an array of dependencies. @@ -37,11 +34,10 @@ public function __construct(array $data) * Load a template (from identifier). * * @param string $templateIdent The template identifier to load.. - * @return string */ public function loadTemplate(string $templateIdent): string { - if (!$templateIdent) { + if ($templateIdent === '' || $templateIdent === '0') { return ''; } return $this->engine()->loadTemplate($templateIdent); @@ -52,7 +48,6 @@ public function loadTemplate(string $templateIdent): string * * @param string $templateIdent The template identifier, to load and render. * @param mixed $context The view controller (rendering context). - * @return string */ public function render(string $templateIdent, $context = null): string { @@ -64,7 +59,6 @@ public function render(string $templateIdent, $context = null): string * * @param string $templateString The full template string to render. * @param mixed $context The view controller (rendering context). - * @return string */ public function renderTemplate(string $templateString, $context = null): string { @@ -74,7 +68,6 @@ public function renderTemplate(string $templateString, $context = null): string /** * @param string $varName The name of the variable to set this template unto. * @param string|null $templateIdent The "dynamic template" to set. null to clear. - * @return void */ public function setDynamicTemplate(string $varName, ?string $templateIdent): void { @@ -83,8 +76,6 @@ public function setDynamicTemplate(string $varName, ?string $templateIdent): voi /** * Get the view's rendering engine instance. - * - * @return EngineInterface */ protected function engine(): EngineInterface { @@ -95,7 +86,6 @@ protected function engine(): EngineInterface * Set the engine (`EngineInterface`) dependency. * * @param EngineInterface $engine The rendering engine. - * @return void */ private function setEngine(EngineInterface $engine): void { diff --git a/packages/view/src/Charcoal/View/EngineInterface.php b/packages/view/src/Charcoal/View/EngineInterface.php index 6d2f205b5..680ffbd50 100644 --- a/packages/view/src/Charcoal/View/EngineInterface.php +++ b/packages/view/src/Charcoal/View/EngineInterface.php @@ -13,7 +13,6 @@ interface EngineInterface * Load a template (from identifier). * * @param string $templateIdent The template identifier to load. - * @return string */ public function loadTemplate(string $templateIdent): string; @@ -38,7 +37,6 @@ public function renderTemplate(string $templateString, $context): string; /** * @param string $varName The name of the variable to set this template unto. * @param string|null $templateIdent The "dynamic template" to set. null to clear. - * @return void */ public function setDynamicTemplate(string $varName, ?string $templateIdent): void; } diff --git a/packages/view/src/Charcoal/View/LoaderInterface.php b/packages/view/src/Charcoal/View/LoaderInterface.php index 8fe660861..5d2d7898c 100644 --- a/packages/view/src/Charcoal/View/LoaderInterface.php +++ b/packages/view/src/Charcoal/View/LoaderInterface.php @@ -18,13 +18,11 @@ public function load($ident); /** * @param string $varName The name of the variable to set this template unto. * @param string|null $templateIdent The "dynamic template" to set. null to clear. - * @return void */ public function setDynamicTemplate(string $varName, ?string $templateIdent): void; /** * @param string $varName The name of the variable to get template ident from. - * @return string */ public function dynamicTemplate(string $varName): string; } diff --git a/packages/view/src/Charcoal/View/Mustache/AssetsHelpers.php b/packages/view/src/Charcoal/View/Mustache/AssetsHelpers.php index 4862ba681..f9cc1d4bc 100644 --- a/packages/view/src/Charcoal/View/Mustache/AssetsHelpers.php +++ b/packages/view/src/Charcoal/View/Mustache/AssetsHelpers.php @@ -14,10 +14,8 @@ class AssetsHelpers implements HelpersInterface { /** * A string concatenation of inline ` + + + + diff --git a/packages/image/build/report/AbstractImage.php.html b/packages/image/build/report/AbstractImage.php.html new file mode 100644 index 000000000..5aebe35ee --- /dev/null +++ b/packages/image/build/report/AbstractImage.php.html @@ -0,0 +1,1060 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/AbstractImage.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 56.30% covered (warning) +
+
+
56.30%
67 / 119
+
+ 42.86% covered (warning) +
+
+
42.86%
9 / 21
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
AbstractImage
+
+ 56.30% covered (warning) +
+
+
56.30%
67 / 119
+
+ 42.86% covered (warning) +
+
+
42.86%
9 / 21
181.26
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 __call
+
+ 50.00% covered (warning) +
+
+
50.00%
2 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
1.12
 effectFactory
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
 driverType
n/a
0 / 0
n/a
0 / 0
0
 setData
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
7
 setSource
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
 source
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 setTarget
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
 target
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 setEffects
+
+ 75.00% covered (warning) +
+
+
75.00%
3 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2.06
 effects
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
 addEffect
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 3
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
 process
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
12
 processEffect
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 create
n/a
0 / 0
n/a
0 / 0
0
 open
n/a
0 / 0
n/a
0 / 0
0
 save
n/a
0 / 0
n/a
0 / 0
0
 width
n/a
0 / 0
n/a
0 / 0
0
 height
n/a
0 / 0
n/a
0 / 0
0
 ratio
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 7
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
12
 orientation
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
12
 isHorizontal
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
 isVertical
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
 isSquare
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
 createEffect
+
+ 57.89% covered (warning) +
+
+
57.89%
11 / 19
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
6.87
 availableChannels
+
+ 100.00% covered (success) +
+
+
100.00%
13 / 13
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 availableGravities
+
+ 100.00% covered (success) +
+
+
100.00%
11 / 11
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 availableFilters
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 15
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image;
4
5use Exception;
6use InvalidArgumentException;
7use Charcoal\Image\ImageInterface;
8use Charcoal\Image\EffectInterface;
9use Charcoal\Image\EffectFactory;
10
11/**
12 * Base Image class
13 */
14abstract class AbstractImage implements ImageInterface
15{
16    /**
17     * @var string $source
18     */
19    protected $source;
20
21    /**
22     * @var string $target
23     */
24    protected $target;
25
26    /**
27     * @var array $Effects
28     */
29    protected $effects = [];
30
31    private ?\Charcoal\Image\EffectFactory $effectFactory = null;
32
33
34    /**
35     * Magic: Attempt to load the effect of the called method name.
36     *
37     * Example, `$img->blur([ 'sigma' => 15 ]);` would create a "Blur" effect.
38     *
39     * @param  string $fxType The effect type.
40     * @param  array  $data   The effect options.
41     * @return ImageInterface Chainable
42     */
43    public function __call(string $fxType, array $data)
44    {
45        $data['type'] = $fxType;
46
47        $fx = $this->createEffect($data);
48        $fx->process();
49
50        return $this;
51    }
52
53    /**
54     * Safe effect factory getter.
55     * If the factory doesn't exist, create it.
56     *
57     * @return EffectFactory
58     */
59    protected function effectFactory()
60    {
61        if (!$this->effectFactory instanceof \Charcoal\Image\EffectFactory) {
62            $this->effectFactory = new EffectFactory();
63        }
64        return $this->effectFactory;
65    }
66
67    /**
68     * @return string
69     */
70    abstract public function driverType();
71
72
73    /**
74     * @param array $data The image data (source, target and effects).
75     * @return ImageInterface Chainable
76     */
77    public function setData(array $data)
78    {
79        if (isset($data['source']) && $data['source'] !== null) {
80            $this->setSource($data['source']);
81        }
82        if (isset($data['target']) && $data['target'] !== null) {
83            $this->setTarget($data['target']);
84        }
85        if (isset($data['effects']) && $data['effects'] !== null) {
86            $this->setEffects($data['effects']);
87        }
88        return $this;
89    }
90
91    /**
92     * @param string $source The image source.
93     * @throws InvalidArgumentException If the source argument is not a string.
94     * @return ImageInterface Chainable
95     */
96    public function setSource($source)
97    {
98        if (!is_string($source)) {
99            throw new InvalidArgumentException(
100                'Source must be a string'
101            );
102        }
103        $this->source = $source;
104        return $this;
105    }
106
107    /**
108     * @return string
109     */
110    public function source()
111    {
112        return $this->source;
113    }
114
115    /**
116     * @param string $target The image target.
117     * @throws InvalidArgumentException If the target argument is not a string.
118     * @return ImageInterface Chainable
119     */
120    public function setTarget($target)
121    {
122        if (!is_string($target)) {
123            throw new InvalidArgumentException(
124                'Target must be a string'
125            );
126        }
127        $this->target = $target;
128        return $this;
129    }
130
131    /**
132     * @return string
133     */
134    public function target()
135    {
136        return $this->target;
137    }
138
139    /**
140     * @param array $effects The effects to apply.
141     * @return ImageInterface Chainable
142     */
143    public function setEffects(array $effects)
144    {
145        $this->effects = [];
146        foreach ($effects as $effect) {
147            $this->addEffect($effect);
148        }
149        return $this;
150    }
151
152    /**
153     * @return EffectInterface[] The array of `EffectInterface` effects.
154     */
155    public function effects()
156    {
157        return $this->effects;
158    }
159
160    /**
161     * @param array|EffectInterface $effect The effect to add.
162     * @return ImageInterface Chainable
163     */
164    public function addEffect($effect)
165    {
166        $fx = $this->createEffect($effect);
167        $this->effects[] = $fx;
168        return $this;
169    }
170
171    /**
172     * @param array $effects Optional. The effects to process. If null, use in-memory's.
173     * @return ImageInterface Chainable
174     */
175    public function process(?array $effects = null)
176    {
177        if ($effects !== null) {
178            $this->setEffects($effects);
179        }
180
181        $effects = $this->effects();
182        foreach ($effects as $fx) {
183            $fx->process();
184        }
185        return $this;
186    }
187
188    /**
189     * @param array|EffectInterface $effect The effect to process.
190     * @return ImageInterface Chainable
191     */
192    public function processEffect($effect)
193    {
194        $fx = $this->createEffect($effect);
195        $fx->process();
196        return $this;
197    }
198
199    /**
200     * Create a blank canvas of a given size, with a given background color.
201     *
202     * @param integer $width  Image width, in pixels.
203     * @param integer $height Image height, in pixels.
204     * @param string  $color  Default to transparent.
205     * @return ImageInterface Chainable
206     */
207    abstract public function create($width, $height, $color = 'rgb(100%, 100%, 100%, 0)');
208
209    /**
210     * Open an image file
211     *
212     * @param string $source The source path / filename.
213     * @return ImageInterface Chainable
214     */
215    abstract public function open($source = null);
216
217    /**
218     * Save an image to a target.
219     * If no target is set, the original source will be owerwritten
220     *
221     * @param string $target The target path / filename.
222     * @return ImageInterface Chainable
223     */
224    abstract public function save($target = null);
225
226    /**
227     * Get the image's width, in pixels
228     *
229     * @return integer
230     */
231    abstract public function width();
232
233    /**
234     * Get the image's height, in pixels
235     *
236     * @return integer
237     */
238    abstract public function height();
239
240    /**
241     * Get the image's ratio (width / height)
242     *
243     * @return float
244     * @throws Exception If the width or height has not been set.
245     */
246    public function ratio()
247    {
248        $width = $this->width();
249        $height = $this->height();
250        if (!$width || !$height) {
251            throw new Exception(
252                'Ratio can not be calculated. Invalid image dimensions'
253            );
254        }
255        return ($width / $height);
256    }
257
258    /**
259     * Orientation can be "horizontal", "vertical" or "square"
260     *
261     * @return string
262     */
263    public function orientation()
264    {
265        $ratio = $this->ratio();
266        if ($ratio > 1) {
267            return 'horizontal';
268        } elseif ($ratio < 1) {
269            return 'vertical';
270        } else {
271            return 'square';
272        }
273    }
274
275    /**
276     * @return boolean
277     */
278    public function isHorizontal()
279    {
280        return ($this->orientation() == 'horizontal');
281    }
282
283    /**
284     * @return boolean
285     */
286    public function isVertical()
287    {
288        return ($this->orientation() == 'vertical');
289    }
290
291    /**
292     * @return boolean
293     */
294    public function isSquare()
295    {
296        return ($this->orientation() == 'square');
297    }
298
299    /**
300     * Ensure an EffectInterface object
301     *
302     * @param array|EffectInterface $effect The effect to create.
303     * @throws InvalidArgumentException If the argument is not an array or Effect.
304     * @return EffectInterface
305     */
306    protected function createEffect($effect)
307    {
308        if ($effect instanceof EffectInterface) {
309            $effect->setImage($this);
310            return $effect;
311        } elseif (is_array($effect)) {
312            if (!isset($effect['type'])) {
313                throw new InvalidArgumentException(
314                    'Effect parameter must define effect type'
315                );
316            }
317            $fxType = $effect['type'];
318            if (!str_contains((string)$fxType, '/')) {
319                // Core effects do not need to be namespaced
320                $driver = $this->driverType();
321                $fxType = 'charcoal/image/' . $driver . '/effect/' . $driver . '-' . $fxType . '-effect';
322            }
323            $imageEffect = $this->effectFactory()->create($fxType);
324            $imageEffect->setImage($this);
325            $imageEffect->setData($effect);
326            return $imageEffect;
327        } else {
328            throw new InvalidArgumentException(
329                'Effect must be an array or effect object'
330            );
331        }
332    }
333
334    /**
335     * @return array
336     */
337    public function availableChannels()
338    {
339        return [
340            // RGB
341            'red',
342            'green',
343            'blue',
344            // CMYK
345            'cyan',
346            'magenta',
347            'yellow',
348            'black',
349            // Others
350            'all',
351            'alpha',
352            'opacity',
353            'gray'
354        ];
355    }
356
357    /**
358     * @return array
359     */
360    public function availableGravities()
361    {
362        return [
363            'center',
364            'n',
365            's',
366            'e',
367            'w',
368            'ne',
369            'nw',
370            'se',
371            'sw'
372        ];
373    }
374
375    /**
376     * @return array
377     */
378    public function availableFilters()
379    {
380        // Unsupported: bartlett, bohman, kaiser, parzen and welsh.
381        return [
382            'blackman',
383            'box',
384            'catrom',
385            'cubic',
386            'hamming',
387            'hanning',
388            'hermite',
389            'gaussian',
390            'lanczos',
391            'mitchell',
392            'point',
393            'quadratic',
394            'triangle'
395        ];
396    }
397}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Effect/AbstractAutoorientationEffect.php.html b/packages/image/build/report/Effect/AbstractAutoorientationEffect.php.html new file mode 100644 index 000000000..71f5e85ca --- /dev/null +++ b/packages/image/build/report/Effect/AbstractAutoorientationEffect.php.html @@ -0,0 +1,115 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/AbstractAutoorientationEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
AbstractAutoorientationEffect
n/a
0 / 0
n/a
0 / 0
0
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + +
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image\Effect;
6
7use Charcoal\Image\AbstractEffect;
8
9/**
10 * Reads image EXIF data to automatically rotate it to the proper orientation
11 */
12abstract class AbstractAutoorientationEffect extends AbstractEffect
13{
14    // This effect does not have any options.
15}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Effect/AbstractBlurEffect.php.html b/packages/image/build/report/Effect/AbstractBlurEffect.php.html new file mode 100644 index 000000000..abc7ad6e3 --- /dev/null +++ b/packages/image/build/report/Effect/AbstractBlurEffect.php.html @@ -0,0 +1,656 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/AbstractBlurEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 96.30% covered (success) +
+
+
96.30%
52 / 54
+
+ 90.91% covered (success) +
+
+
90.91%
10 / 11
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
AbstractBlurEffect
+
+ 96.30% covered (success) +
+
+
96.30%
52 / 54
+
+ 90.91% covered (success) +
+
+
90.91%
10 / 11
25
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 setRadius
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
3
 radius
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 setSigma
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
3
 sigma
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 setMode
+
+ 100.00% covered (success) +
+
+
100.00%
14 / 14
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
 mode
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 setChannel
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
 channel
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 setAngle
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
 angle
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 process
+
+ 81.82% covered (success) +
+
+
81.82%
9 / 11
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
8.38
 processAdaptive
n/a
0 / 0
n/a
0 / 0
0
 processGaussian
n/a
0 / 0
n/a
0 / 0
0
 processMotion
n/a
0 / 0
n/a
0 / 0
0
 processRadial
n/a
0 / 0
n/a
0 / 0
0
 processSoft
n/a
0 / 0
n/a
0 / 0
0
 processStandard
n/a
0 / 0
n/a
0 / 0
0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Effect;
4
5use InvalidArgumentException;
6use Charcoal\Image\AbstractEffect;
7
8/**
9 * Blur the image
10 */
11abstract class AbstractBlurEffect extends AbstractEffect
12{
13    /**
14     * @var float $radius
15     */
16    private $radius = 0;
17    /**
18     * @var float $sigma
19     */
20    private $sigma = 1;
21    private string $mode = 'standard';
22
23    /**
24     * @var string $channel
25     */
26    private $channel = 'all';
27
28    /**
29     * The angle is only used for "motion" and "radial" modes
30     * @var float $angle
31     */
32    private $angle = 0;
33
34    /**
35     * @param float $radius The blur radius value.
36     * @throws InvalidArgumentException If the argument is not a valid number.
37     * @return AbstractBlurEffect Chainable
38     */
39    public function setRadius($radius)
40    {
41        if (!is_numeric($radius) || ($radius < 0)) {
42            throw new InvalidArgumentException(
43                'Radius must be a float (greater than 0)'
44            );
45        }
46         $this->radius = (float)$radius;
47         return $this;
48    }
49
50    /**
51     * @return float
52     */
53    public function radius()
54    {
55        return $this->radius;
56    }
57
58    /**
59     * @param float $sigma The blur sigma value.
60     * @throws InvalidArgumentException If the argument is not a valid number.
61     * @return AbstractBlurEffect Chainable
62     */
63    public function setSigma($sigma)
64    {
65        if (!is_numeric($sigma) || ($sigma < 0)) {
66            throw new InvalidArgumentException(
67                'Sigma value must be a float (greater than 0)'
68            );
69        }
70        $this->sigma = (float)$sigma;
71        return $this;
72    }
73
74    /**
75     * @return float
76     */
77    public function sigma()
78    {
79        return $this->sigma;
80    }
81
82    /**
83     * @param string $mode The blur mode.
84     * @throws InvalidArgumentException If the argument is not a valid blur mode.
85     * @return AbstractBlurEffect Chainable
86     */
87    public function setMode($mode)
88    {
89        $allowedModes = [
90            'standard',
91            'adaptive',
92            'gaussian',
93            'motion',
94            'radial',
95            'soft'
96        ];
97        if (!in_array($mode, $allowedModes)) {
98            throw new InvalidArgumentException(
99                sprintf('Mode %s is not an allowed blur mode', $mode)
100            );
101        }
102        $this->mode = $mode;
103        return $this;
104    }
105
106    /**
107     * @return string
108     */
109    public function mode()
110    {
111        return $this->mode;
112    }
113
114    /**
115     * @param string $channel The blur channel.
116     * @throws InvalidArgumentException If the argument is not a valid image channel.
117     * @return AbstractBlurEffect Chainable
118     */
119    public function setChannel($channel)
120    {
121        if (!in_array($channel, $this->image()->availableChannels())) {
122            throw new InvalidArgumentException(
123                'Channel is not valid'
124            );
125        }
126        $this->channel = $channel;
127        return $this;
128    }
129
130    /**
131     * @return string
132     */
133    public function channel()
134    {
135        return $this->channel;
136    }
137
138    /**
139     * @param float $angle The blur angle.
140     * @throws InvalidArgumentException If the argument is not a valid number.
141     * @return AbstractBlurEffect Chainable
142     */
143    public function setAngle($angle)
144    {
145        if (!is_numeric($angle)) {
146            throw new InvalidArgumentException(
147                'Angle must be a numeric value, in degrees'
148            );
149        }
150        $this->angle = (float)$angle;
151        return $this;
152    }
153
154    /**
155     * @return float
156     */
157    public function angle()
158    {
159        return $this->angle;
160    }
161
162    /**
163     * @param array $data The effect data, if available.
164     * @return AbstractBlurEffect Chainable
165     */
166    public function process(?array $data = null)
167    {
168        if ($data !== null) {
169            $this->setData($data);
170        }
171
172        $mode = $this->mode();
173        return match ($mode) {
174            'adaptive' => $this->processAdaptive(),
175            'gaussian' => $this->processGaussian(),
176            'motion' => $this->processMotion(),
177            'radial' => $this->processRadial(),
178            'soft' => $this->processSoft(),
179            default => $this->processStandard(),
180        };
181    }
182
183    /**
184     * @return AbstractBlurEffect Chainable
185     */
186    abstract public function processAdaptive();
187
188    /**
189     * @return AbstractBlurEffect Chainable
190     */
191    abstract public function processGaussian();
192
193    /**
194     * @return AbstractBlurEffect Chainable
195     */
196    abstract public function processMotion();
197
198    /**
199     * @return AbstractBlurEffect Chainable
200     */
201    abstract public function processRadial();
202
203    /**
204     * @return AbstractBlurEffect Chainable
205     */
206    abstract public function processSoft();
207
208    /**
209     * @return AbstractBlurEffect Chainable
210     */
211    abstract public function processStandard();
212}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Effect/AbstractCompressionEffect.php.html b/packages/image/build/report/Effect/AbstractCompressionEffect.php.html new file mode 100644 index 000000000..d1b2b36e5 --- /dev/null +++ b/packages/image/build/report/Effect/AbstractCompressionEffect.php.html @@ -0,0 +1,208 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/AbstractCompressionEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 3
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 2
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
AbstractCompressionEffect
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 3
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 2
6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 setQuality
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
 quality
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image\Effect;
6
7use Charcoal\Image\AbstractEffect;
8
9/**
10 * Image compression effect to adapt for web.
11 * Defaults to 100% quality
12 */
13abstract class AbstractCompressionEffect extends AbstractEffect
14{
15    private int $quality = 100;
16
17    /**
18     * @param int $quality Image quality from 1 to 100
19     * @return AbstractEffect Chainable
20     */
21    public function setQuality(int $quality)
22    {
23        $this->quality = $quality;
24        return $this;
25    }
26
27    /**
28     * @return int
29     */
30    public function quality()
31    {
32        return $this->quality;
33    }
34}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Effect/AbstractCropEffect.php.html b/packages/image/build/report/Effect/AbstractCropEffect.php.html new file mode 100644 index 000000000..42f11bf4b --- /dev/null +++ b/packages/image/build/report/Effect/AbstractCropEffect.php.html @@ -0,0 +1,706 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/AbstractCropEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 56
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 15
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
AbstractCropEffect
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 56
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 15
870
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 setWidth
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
12
 width
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
 setHeight
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
12
 height
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
 setX
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
12
 x
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
 setY
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
12
 y
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
 setGeometry
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
20
 geometry
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
 setGravity
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
6
 gravity
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
 setRepage
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
 repage
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
 process
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 11
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
12
 doCrop
n/a
0 / 0
n/a
0 / 0
0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Effect;
4
5use Exception;
6use InvalidArgumentException;
7use Charcoal\Image\AbstractEffect;
8
9/**
10 * Resize an image to given dimensions
11 */
12abstract class AbstractCropEffect extends AbstractEffect
13{
14    private int $x = 0;
15
16    private int $y = 0;
17
18    private int $width = 0;
19
20    private int $height = 0;
21
22    private string|float|int|null $geometry = null;
23
24    /**
25     * @var string $gravity
26     */
27    private $gravity = 'center';
28
29    private bool $repage = false;
30
31    /**
32     * @param  integer $width The crop width.
33     * @throws InvalidArgumentException If the width argument is not valid.
34     * @return AbstractCropEffect
35     */
36    public function setWidth($width)
37    {
38        if (!is_int($width) || ($width < 0)) {
39            throw new InvalidArgumentException(
40                'Width must be a a positive integer'
41            );
42        }
43        $this->width = $width;
44        return $this;
45    }
46
47    /**
48     * @return integer
49     */
50    public function width()
51    {
52        return $this->width;
53    }
54
55    /**
56     * @param  integer $height The crop height.
57     * @throws InvalidArgumentException If the height argument is not valid.
58     * @return AbstractCropEffect
59     */
60    public function setHeight($height)
61    {
62        if (!is_int($height) || ($height < 0)) {
63            throw new InvalidArgumentException(
64                'Height must be a positive integer'
65            );
66        }
67        $this->height = $height;
68        return $this;
69    }
70
71    /**
72     * @return integer
73     */
74    public function height()
75    {
76        return $this->height;
77    }
78
79    /**
80     * The X coordinate of the cropped region's top left corner
81     *
82     * @param  integer $x The x-position (in pixel) of the crop.
83     * @throws InvalidArgumentException If the x argument is not valid.
84     * @return AbstractCropEffect
85     */
86    public function setX($x)
87    {
88        if (!is_int($x) || ($x < 0)) {
89            throw new InvalidArgumentException(
90                'Height must be a positive integer'
91            );
92        }
93        $this->x = $x;
94        return $this;
95    }
96
97    /**
98     * @return integer
99     */
100    public function x()
101    {
102        return $this->x;
103    }
104
105    /**
106     * The Y coordinate of the cropped region's top left corner
107     *
108     * @param  integer $y The y-position (in pixel) of the crop.
109     * @throws InvalidArgumentException If the y argumnet is not valid.
110     * @return AbstractCropEffect
111     */
112    public function setY($y)
113    {
114        if (!is_int($y) || ($y < 0)) {
115            throw new InvalidArgumentException(
116                'Height must be a positive integer'
117            );
118        }
119        $this->y = $y;
120        return $this;
121    }
122
123    /**
124     * @return integer
125     */
126    public function y()
127    {
128        return $this->y;
129    }
130
131    /**
132     * Set a complex geometry value.
133     *
134     * @param  mixed $geometry The image geometry.
135     * @throws InvalidArgumentException If the geometry argument is not valid.
136     * @return AbstractCropEffect
137     */
138    public function setGeometry($geometry)
139    {
140        if ($geometry !== null && !is_string($geometry) && !is_numeric($geometry)) {
141            throw new InvalidArgumentException(
142                'Geometry must be a valid crop'
143            );
144        }
145        $this->geometry = $geometry;
146        return $this;
147    }
148
149    /**
150     * Retrieve the complex geometry value.
151     *
152     * @return mixed
153     */
154    public function geometry()
155    {
156        return $this->geometry;
157    }
158
159    /**
160     * @param  string $gravity The crop gravity.
161     * @throws InvalidArgumentException If the argument is not a valid gravity name.
162     * @return AbstractCropEffect
163     */
164    public function setGravity($gravity)
165    {
166        if (!in_array($gravity, $this->image()->availableGravities())) {
167            throw new InvalidArgumentException(
168                'Gravity is not valid'
169            );
170        }
171        $this->gravity = $gravity;
172        return $this;
173    }
174
175    /**
176     * @return string
177     */
178    public function gravity()
179    {
180        return $this->gravity;
181    }
182
183    /**
184     * @param  boolean $repage The repage image flag.
185     * @return AbstractCropEffect
186     */
187    public function setRepage($repage)
188    {
189        $this->repage = (bool)$repage;
190        return $this;
191    }
192
193    /**
194     * @return boolean
195     */
196    public function repage()
197    {
198        return $this->repage;
199    }
200
201    /**
202     * @param  array $data The effect data.
203     * @return AbstractCropEffect
204     */
205    public function process(?array $data = null)
206    {
207        if ($data !== null) {
208            $this->setData($data);
209        }
210
211        if ($this->geometry()) {
212            $this->doCrop(0, 0, 0, 0);
213            return $this;
214        }
215
216        $y = $this->y();
217        $x = $this->x();
218        $width  = $this->width();
219        $height = $this->height();
220
221        $this->doCrop($width, $height, $x, $y);
222
223        return $this;
224    }
225
226    /**
227     * @param  integer $width  The crop width.
228     * @param  integer $height The crop height.
229     * @param  integer $x      The x-position (in pixel) of the crop.
230     * @param  integer $y      The y-position (in pixel) of the crop.
231     * @return void
232     */
233    abstract protected function doCrop($width, $height, $x, $y);
234}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Effect/AbstractDitherEffect.php.html b/packages/image/build/report/Effect/AbstractDitherEffect.php.html new file mode 100644 index 000000000..df99f67ff --- /dev/null +++ b/packages/image/build/report/Effect/AbstractDitherEffect.php.html @@ -0,0 +1,306 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/AbstractDitherEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 100.00% covered (success) +
+
+
100.00%
36 / 36
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
AbstractDitherEffect
+
+ 100.00% covered (success) +
+
+
100.00%
36 / 36
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 setColors
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
 colors
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 setMode
+
+ 100.00% covered (success) +
+
+
100.00%
28 / 28
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
 mode
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image\Effect;
6
7use InvalidArgumentException;
8use Charcoal\Image\AbstractEffect;
9
10/**
11 * AbstractDitherEffect an image to a reduced number of colors
12 */
13abstract class AbstractDitherEffect extends AbstractEffect
14{
15    private int $colors = 16;
16    private string $mode = '';
17
18    /**
19     * @param integer $colors The numver of dither colors.
20     * @throws InvalidArgumentException If the argument is not numeric.
21     * @return AbstractDitherEffect Chainable
22     */
23    public function setColors($colors)
24    {
25        if (!is_numeric($colors)) {
26            throw new InvalidArgumentException(
27                'Colors must be an integer'
28            );
29        }
30        $this->colors = (int)$colors;
31        return $this;
32    }
33
34    /**
35     * @return integer
36     */
37    public function colors()
38    {
39        return $this->colors;
40    }
41
42    /**
43     * @param string $mode The dither mode.
44     * @throws InvalidArgumentException If the argument is not a valid dither mode.
45     * @return AbstractDitherEffect Chainable
46     */
47    public function setMode($mode)
48    {
49        $allowedModes = [
50            '',
51// Quantize
52            'threshold',
53            'checks',
54            'o2x2',
55            'o3x3',
56            'o4x4',
57            'o8x8',
58            'h4x4a',
59            'h6x6a',
60            'h8x8a',
61            'h4x4o',
62            'h6x6o',
63            'h8x8o',
64            'h16x16o',
65            'c5x5b',
66            'c5x5w',
67            'c6x6b',
68            'c6x6w',
69            'c7x7b',
70            'c7x7w'
71        ];
72        if (!in_array($mode, $allowedModes)) {
73            throw new InvalidArgumentException(
74                'Invalid dither mode'
75            );
76        }
77        $this->mode = $mode;
78        return $this;
79    }
80
81    /**
82     * @return string
83     */
84    public function mode()
85    {
86        return $this->mode;
87    }
88}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Effect/AbstractFormatEffect.php.html b/packages/image/build/report/Effect/AbstractFormatEffect.php.html new file mode 100644 index 000000000..773a0d1da --- /dev/null +++ b/packages/image/build/report/Effect/AbstractFormatEffect.php.html @@ -0,0 +1,216 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/AbstractFormatEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 11
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 2
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
AbstractFormatEffect
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 11
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 2
12
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 setFormat
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 10
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
6
 format
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Effect;
4
5use InvalidArgumentException;
6use Charcoal\Image\AbstractEffect;
7
8/**
9 * Format (or colorize) the image with a certain color.
10 */
11abstract class AbstractFormatEffect extends AbstractEffect
12{
13    protected $format;
14
15    public const ACCEPTED_FORMAT = [ 'webp', 'jpg', 'jpeg' ];
16
17    /**
18     * Must be one of the accepted format
19     *
20     * @var string $format
21     * @throws InvalidArgumentException If the format is not supported
22     */
23    public function setFormat(string $format)
24    {
25        if (!in_array($format, static::ACCEPTED_FORMAT)) {
26            throw new InvalidArgumentException(
27                sprintf(
28                    'Invalid image format provided. Must be one of %s. %s provided.',
29                    implode(',', static::ACCEPTED_FORMAT),
30                    $format
31                )
32            );
33        }
34        $this->format = $format;
35        return $this;
36    }
37
38    public function format()
39    {
40        return $this->format;
41    }
42}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Effect/AbstractGrayscaleEffect.php.html b/packages/image/build/report/Effect/AbstractGrayscaleEffect.php.html new file mode 100644 index 000000000..d6470c7d5 --- /dev/null +++ b/packages/image/build/report/Effect/AbstractGrayscaleEffect.php.html @@ -0,0 +1,115 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/AbstractGrayscaleEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
AbstractGrayscaleEffect
n/a
0 / 0
n/a
0 / 0
0
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + +
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image\Effect;
6
7use Charcoal\Image\AbstractEffect;
8
9/**
10 * Convert an image to grayscale colorspace
11 */
12abstract class AbstractGrayscaleEffect extends AbstractEffect
13{
14    // This effect does not have any options.
15}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Effect/AbstractMaskEffect.php.html b/packages/image/build/report/Effect/AbstractMaskEffect.php.html new file mode 100644 index 000000000..94eaa3cd8 --- /dev/null +++ b/packages/image/build/report/Effect/AbstractMaskEffect.php.html @@ -0,0 +1,222 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/AbstractMaskEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
AbstractMaskEffect
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
4
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 setMask
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
3
 mask
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image\Effect;
6
7use InvalidArgumentException;
8use Charcoal\Image\AbstractEffect;
9use Charcoal\Image\ImageInterface;
10use Charcoal\Image\Effect\LayerEffectInterface;
11use Charcoal\Image\Effect\LayerEffectTrait;
12
13/**
14 * Composite an opacity mask on top of the image
15 */
16abstract class AbstractMaskEffect extends AbstractEffect implements LayerEffectInterface
17{
18    use LayerEffectTrait;
19
20    /**
21     * The mask image source
22     */
23    private ?string $mask = null;
24
25    /**
26     * @param string $mask The mask image source.
27     * @throws InvalidArgumentException If the mask source is not a string.
28     * @return AbstractMaskEffect Chainable
29     */
30    public function setMask($mask)
31    {
32        if (!is_string($mask) || ($mask instanceof ImageInterface)) {
33            throw new InvalidArgumentException(
34                'Mask must be a string'
35            );
36        }
37        $this->mask = $mask;
38        return $this;
39    }
40
41    /**
42     * @return string
43     */
44    public function mask()
45    {
46        return $this->mask;
47    }
48}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Effect/AbstractMirrorEffect.php.html b/packages/image/build/report/Effect/AbstractMirrorEffect.php.html new file mode 100644 index 000000000..21ee58308 --- /dev/null +++ b/packages/image/build/report/Effect/AbstractMirrorEffect.php.html @@ -0,0 +1,218 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/AbstractMirrorEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 100.00% covered (success) +
+
+
100.00%
8 / 8
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
AbstractMirrorEffect
+
+ 100.00% covered (success) +
+
+
100.00%
8 / 8
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
4
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 setAxis
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
3
 axis
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image\Effect;
6
7use InvalidArgumentException;
8use Charcoal\Image\AbstractEffect;
9
10/**
11 * Flip an image horizontally or vertically.
12 */
13abstract class AbstractMirrorEffect extends AbstractEffect
14{
15    /**
16     * Axis can be "x" (flip) or "y" (flop)
17     */
18    private string $axis = 'y';
19
20    /**
21     * @param string $axis The mirror axis.
22     * @throws InvalidArgumentException If the argument is not x or y.
23     * @return AbstractMirrorEffect Chainable
24     */
25    public function setAxis($axis)
26    {
27        $allowedVals = ['x', 'y'];
28        if (!is_string($axis) || !in_array($axis, $allowedVals)) {
29            throw new InvalidArgumentException(
30                'Axis must be "x" or "y"'
31            );
32        }
33        $this->axis = $axis;
34        return $this;
35    }
36
37    /**
38     * @return string
39     */
40    public function axis()
41    {
42        return $this->axis;
43    }
44}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Effect/AbstractModulateEffect.php.html b/packages/image/build/report/Effect/AbstractModulateEffect.php.html new file mode 100644 index 000000000..51bcab45a --- /dev/null +++ b/packages/image/build/report/Effect/AbstractModulateEffect.php.html @@ -0,0 +1,366 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/AbstractModulateEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 100.00% covered (success) +
+
+
100.00%
21 / 21
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
AbstractModulateEffect
+
+ 100.00% covered (success) +
+
+
100.00%
21 / 21
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
15
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 setHue
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
4
 hue
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 setSaturation
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
4
 saturation
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 setLuminance
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
4
 luminance
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image\Effect;
6
7use InvalidArgumentException;
8use Charcoal\Image\AbstractEffect;
9
10/**
11 * Modifies an image's colors in the special HSL (hue-saturation-luminance) colorspace.
12 */
13abstract class AbstractModulateEffect extends AbstractEffect
14{
15    /**
16     * The color tint (-100 to 100)
17     * @var float $hue
18     */
19    private $hue = 0;
20
21    /**
22     * The color intensity (-100 to 100)
23     * @var float $saturation
24     */
25    private $saturation = 0;
26
27    /**
28     * The brightness (-100 to 100)
29     * @var float $luminance
30     */
31    private $luminance = 0;
32
33    /**
34     * @param float $hue The modulate hue.
35     * @throws InvalidArgumentException If the argument is not numeric or within valid range.
36     * @return AbstractModulateEffect Chainable
37     */
38    public function setHue($hue)
39    {
40        if (!is_numeric($hue) || ($hue < -100) || ($hue > 100)) {
41            throw new InvalidArgumentException(
42                'Hue (color tint) must be a float between 0 and 200'
43            );
44        }
45        $this->hue = (float)$hue;
46        return $this;
47    }
48
49    /**
50     * @return float
51     */
52    public function hue()
53    {
54        return $this->hue;
55    }
56
57    /**
58     * @param float $saturation The modulate saturation.
59     * @throws InvalidArgumentException If the argument is not numeric or within valid range.
60     * @return AbstractModulateEffect Chainable
61     */
62    public function setSaturation($saturation)
63    {
64        if (!is_numeric($saturation) || ($saturation < -100) || ($saturation > 100)) {
65            throw new InvalidArgumentException(
66                'Saturation (color intensity) must be a float between 0 and 200'
67            );
68        }
69        $this->saturation = (float)$saturation;
70        return $this;
71    }
72
73    /**
74     * @return float
75     */
76    public function saturation()
77    {
78        return $this->saturation;
79    }
80
81    /**
82     * @param float $luminance The modulate luminance.
83     * @throws InvalidArgumentException If the argument is not numeric or within valid range.
84     * @return AbstractModulateEffect Chainable
85     */
86    public function setLuminance($luminance)
87    {
88        if (!is_numeric($luminance) || ($luminance < -100) || ($luminance > 100)) {
89            throw new InvalidArgumentException(
90                'Luminance (brightness) must be a float between 0 and 200'
91            );
92        }
93        $this->luminance = (float)$luminance;
94        return $this;
95    }
96
97    /**
98     * @return float
99     */
100    public function luminance()
101    {
102        return $this->luminance;
103    }
104}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Effect/AbstractResizeEffect.php.html b/packages/image/build/report/Effect/AbstractResizeEffect.php.html new file mode 100644 index 000000000..e045517e8 --- /dev/null +++ b/packages/image/build/report/Effect/AbstractResizeEffect.php.html @@ -0,0 +1,1142 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/AbstractResizeEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 58.62% covered (warning) +
+
+
58.62%
102 / 174
+
+ 75.00% covered (warning) +
+
+
75.00%
18 / 24
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
AbstractResizeEffect
+
+ 58.62% covered (warning) +
+
+
58.62%
102 / 174
+
+ 75.00% covered (warning) +
+
+
75.00%
18 / 24
650.22
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 setMode
+
+ 100.00% covered (success) +
+
+
100.00%
17 / 17
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
3
 mode
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 setSize
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
5
 size
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 setWidth
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
3
 width
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 setHeight
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
3
 height
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 setMinWidth
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
12
 minWidth
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 setMinHeight
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
12
 minHeight
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 setMaxWidth
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
12
 maxWidth
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 setMaxHeight
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
12
 maxHeight
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 setGravity
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
 gravity
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 setBackgroundColor
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
 backgroundColor
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 setAdaptive
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 adaptive
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 autoMode
+
+ 90.91% covered (success) +
+
+
90.91%
10 / 11
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
9.06
 process
+
+ 40.51% covered (warning) +
+
+
40.51%
32 / 79
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
342.07
 doResize
n/a
0 / 0
n/a
0 / 0
0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Effect;
4
5use Exception;
6use InvalidArgumentException;
7use Charcoal\Image\AbstractEffect;
8
9/**
10 * Resize an image to given dimensions
11 */
12abstract class AbstractResizeEffect extends AbstractEffect
13{
14    private string $mode = 'auto';
15
16    private string|float|int|null $size = null;
17
18    private int $width = 0;
19
20    private int $height = 0;
21
22    private int $minWidth = 0;
23
24    private int $minHeight = 0;
25
26    private int $maxWidth = 0;
27
28    private int $maxHeight = 0;
29
30    /**
31     * @var string $gravity
32     */
33    private $gravity = 'center';
34
35    private string $backgroundColor = 'rgba(100%, 100%, 100%, 0)';
36
37    private bool $adaptive = false;
38
39    /**
40     * @param string $mode The resize mode.
41     * @throws InvalidArgumentException If the mode argument is not a valid resize mode.
42     * @return self
43     */
44    public function setMode($mode)
45    {
46        $allowedModes = [
47            'auto',
48            'exact',
49            'width',
50            'height',
51            'best_fit',
52            'constraints',
53            'crop',
54            'fill',
55            'none'
56        ];
57        if (!is_string($mode) || (!in_array($mode, $allowedModes))) {
58            throw new InvalidArgumentException(
59                'Mode is not valid'
60            );
61        }
62        $this->mode = $mode;
63        return $this;
64    }
65
66    /**
67     * @return string
68     */
69    public function mode()
70    {
71        return $this->mode;
72    }
73
74    /**
75     * Set a complex resize value.
76     *
77     * @param  mixed $size The size.
78     * @throws InvalidArgumentException If the size argument is not valid.
79     * @return self
80     */
81    public function setSize($size)
82    {
83        if ($size !== null && !is_string($size) && (!is_numeric($size) || ($size < 0))) {
84            throw new InvalidArgumentException(
85                'Size must be a valid scale'
86            );
87        }
88
89        $this->size = $size;
90
91        return $this;
92    }
93
94    /**
95     * Retrieve the complex resize value.
96     *
97     * @return mixed
98     */
99    public function size()
100    {
101        return $this->size;
102    }
103
104    /**
105     * @param integer $width The target resize width.
106     * @throws InvalidArgumentException If the width argument is not numeric or lower than 0.
107     * @return self
108     */
109    public function setWidth($width)
110    {
111        if (!is_numeric($width) || ($width < 0)) {
112            throw new InvalidArgumentException(
113                'Width must be a positive integer'
114            );
115        }
116        $this->width = (int)$width;
117        return $this;
118    }
119
120    /**
121     * @return float
122     */
123    public function width()
124    {
125        return $this->width;
126    }
127
128    /**
129     * @param integer $height The target resize height.
130     * @throws InvalidArgumentException If the height argument is not numeric or lower than 0.
131     * @return self
132     */
133    public function setHeight($height)
134    {
135        if (!is_int($height) || ($height < 0)) {
136            throw new InvalidArgumentException(
137                'Height must be a positive integer'
138            );
139        }
140        $this->height = $height;
141        return $this;
142    }
143
144    /**
145     * @return float
146     */
147    public function height()
148    {
149        return $this->height;
150    }
151
152    /**
153     * @param integer $minWidth The resize minimal width.
154     * @throws InvalidArgumentException If the argument is not numeric or lower than 0.
155     * @return self
156     */
157    public function setMinWidth($minWidth)
158    {
159        if (!is_numeric($minWidth) || ($minWidth < 0)) {
160            throw new InvalidArgumentException(
161                'Min Width must be a positive integer'
162            );
163        }
164        $this->minWidth = (int)$minWidth;
165        return $this;
166    }
167
168    /**
169     * @return float
170     */
171    public function minWidth()
172    {
173        return $this->minWidth;
174    }
175
176    /**
177     * @param integer $minHeight The resize minimal height.
178     * @throws InvalidArgumentException If the argument is not numeric or lower than 0.
179     * @return self
180     */
181    public function setMinHeight($minHeight)
182    {
183        if (!is_numeric($minHeight) || ($minHeight < 0)) {
184            throw new InvalidArgumentException(
185                'Min Height must be a positive integer'
186            );
187        }
188        $this->minHeight = (int)$minHeight;
189        return $this;
190    }
191
192    /**
193     * @return float
194     */
195    public function minHeight()
196    {
197        return $this->minHeight;
198    }
199
200    /**
201     * @param integer $maxWidth The resize max width.
202     * @throws InvalidArgumentException If the argument is not numeric or lower than 0.
203     * @return self
204     */
205    public function setMaxWidth($maxWidth)
206    {
207        if (!is_numeric($maxWidth) || ($maxWidth < 0)) {
208            throw new InvalidArgumentException(
209                'Max Width must be a positive integer'
210            );
211        }
212        $this->maxWidth = (int)$maxWidth;
213        return $this;
214    }
215
216    /**
217     * @return float
218     */
219    public function maxWidth()
220    {
221        return $this->maxWidth;
222    }
223
224    /**
225     * @param integer $maxHeight The resize max height.
226     * @throws InvalidArgumentException If the argument is not numeric or lower than 0.
227     * @return self
228     */
229    public function setMaxHeight($maxHeight)
230    {
231        if (!is_numeric($maxHeight) || ($maxHeight < 0)) {
232            throw new InvalidArgumentException(
233                'Height must be a positive integer'
234            );
235        }
236        $this->maxHeight = (int)$maxHeight;
237        return $this;
238    }
239
240    /**
241     * @return float
242     */
243    public function maxHeight()
244    {
245        return $this->maxHeight;
246    }
247
248    /**
249     * @param string $gravity The resize gravity.
250     * @throws InvalidArgumentException If the argument is not a valid gravity name.
251     * @return self
252     */
253    public function setGravity($gravity)
254    {
255        if (!in_array($gravity, $this->image()->availableGravities())) {
256            throw new InvalidArgumentException(
257                'Gravity is not valid'
258            );
259        }
260        $this->gravity = $gravity;
261        return $this;
262    }
263
264    /**
265     * @return string
266     */
267    public function gravity()
268    {
269        return $this->gravity;
270    }
271
272    /**
273     * @param string $color The resize background color.
274     * @throws InvalidArgumentException If the color argument is not a string.
275     * @return self
276     */
277    public function setBackgroundColor($color)
278    {
279        if (!is_string($color)) {
280            throw new InvalidArgumentException(
281                'Color must be a string'
282            );
283        }
284        $this->backgroundColor = $color;
285        return $this;
286    }
287
288    /**
289     * @return string
290     */
291    public function backgroundColor()
292    {
293        return $this->backgroundColor;
294    }
295
296    /**
297     * @param boolean $adaptive The adaptative resize flag.
298     * @return self
299     */
300    public function setAdaptive($adaptive)
301    {
302        $this->adaptive = (bool)$adaptive;
303        return $this;
304    }
305
306    /**
307     * @return boolean
308     */
309    public function adaptive()
310    {
311        return $this->adaptive;
312    }
313
314    /**
315     * @return string
316     */
317    public function autoMode()
318    {
319        $width = $this->width();
320        $height = $this->height();
321
322        if ($width > 0 && $height > 0) {
323            return 'exact';
324        } elseif ($width > 0) {
325            return 'width';
326        } elseif ($height > 0) {
327            return 'height';
328        } elseif ($this->minWidth() || $this->minHeight() || $this->maxWidth() || $this->maxHeight()) {
329            return 'constraints';
330        } else {
331            return 'none';
332        }
333    }
334
335    /**
336     * @param array $data The effect data, if available.
337     * @throws Exception If the effect data is invalid for its resize mode.
338     * @return self
339     */
340    public function process(?array $data = null)
341    {
342        if ($data !== null) {
343            $this->setData($data);
344        }
345
346        $mode = $this->mode();
347        if ($mode == 'auto') {
348            $mode = $this->autoMode();
349        }
350
351        $size = $this->size();
352        if ($size) {
353            $this->doResize(0, 0, false);
354            return $this;
355        }
356
357        if ($mode == 'none') {
358            // Noting to do.
359            return $this;
360        }
361
362        $imageWidth  = $this->image()->width();
363        $imageHeight = $this->image()->height();
364
365        if ($imageWidth == 0 || $imageHeight == 0) {
366            throw new Exception(
367                'Can not process image; invalid image (0 width or height)'
368            );
369        }
370
371        switch ($mode) {
372            case 'exact':
373                if (($this->width() <= 0) || ($this->height() <= 0)) {
374                    throw new Exception(
375                        'Missing parameters to perform exact resize'
376                    );
377                }
378                if ($imageWidth != $this->width() || $imageHeight != $this->height()) {
379                    $this->doResize($this->width(), $this->height(), false);
380                }
381                break;
382
383            case 'width':
384                if ($this->width() <= 0) {
385                    throw new Exception(
386                        'Missing parameters to perform exact width resize'
387                    );
388                }
389                if ($imageWidth != $this->width()) {
390                    $this->doResize($this->width(), 0, false);
391                }
392                break;
393
394            case 'height':
395                if ($this->height() <= 0) {
396                    throw new Exception(
397                        'Missing parameters to perform exact height resize'
398                    );
399                }
400                if ($imageHeight != $this->height()) {
401                    $this->doResize(0, $this->height(), false);
402                }
403                break;
404
405            case 'best_fit':
406                if (($this->width() <= 0) || ($this->height() <= 0)) {
407                    throw new Exception(
408                        'Missing parameters to perform "best fit" resize'
409                    );
410                }
411                if ($imageWidth != $this->width() || $imageHeight != $this->height()) {
412                    $this->doResize($this->width(), $this->height(), true);
413                }
414                break;
415
416            case 'constraints':
417                $minW = $this->minWidth();
418                $minH = $this->minHeight();
419                $maxW = $this->maxWidth();
420                $maxH = $this->maxHeight();
421
422                if (array_sum([$minW, $minH, $maxW, $maxH]) == 0) {
423                    throw new Exception(
424                        'Missing parameter(s) to perform "constraints" resize'
425                    );
426                }
427
428                if (($minW && ($minW > $imageWidth)) || ($minH && ($minH > $imageHeight))) {
429                    // Must scale up, keeping ratio
430                    $this->doResize($minW, $minH, true);
431                    break;
432                }
433
434                if (($maxW && ($maxW < $imageWidth)) || ($maxH && ($maxH < $imageHeight))) {
435                    // Must scale down. keeping ratio
436                    $this->doResize($maxW, $maxH, true);
437                    break;
438                }
439                break;
440
441            case 'crop':
442                throw new Exception(
443                    'Crop resize mode is not (yet) supported'
444                );
445
446            case 'fill':
447                $newWidth = $this->width();
448                $newHeight = $this->height();
449
450                $oldRatio = $imageHeight ? ($imageWidth / $imageHeight) : 0;
451                $newRatio = $newHeight ? ($newWidth / $newHeight) : 0;
452
453                if ($newRatio > $oldRatio) {
454                    $newHeight = ($imageHeight * $this->width() / $imageWidth);
455                } else {
456                    $newWidth = ($imageWidth * $this->height() / $imageHeight);
457                }
458
459                $this->doResize($newWidth, $newHeight);
460        }
461
462        return $this;
463    }
464
465    /**
466     * @param integer $width   The target width.
467     * @param integer $height  The target height.
468     * @param boolean $bestFit The "best_fit" flag.
469     * @return void
470     */
471    abstract protected function doResize($width, $height, $bestFit = false);
472}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Effect/AbstractRevertEffect.php.html b/packages/image/build/report/Effect/AbstractRevertEffect.php.html new file mode 100644 index 000000000..338468aa8 --- /dev/null +++ b/packages/image/build/report/Effect/AbstractRevertEffect.php.html @@ -0,0 +1,215 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/AbstractRevertEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
AbstractRevertEffect
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
3
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 setChannel
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
 channel
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Effect;
4
5use InvalidArgumentException;
6use Charcoal\Image\AbstractEffect;
7
8/**
9 * Revert (negate) the image's colors.
10 */
11abstract class AbstractRevertEffect extends AbstractEffect
12{
13    /**
14     * @var string $channel
15     */
16    private $channel = 'all';
17
18    /**
19     * @param string $channel The channel to revert.
20     * @throws InvalidArgumentException If the channel argument is not a valid channel.
21     * @return self
22     */
23    public function setChannel($channel)
24    {
25        if (!in_array($channel, $this->image()->availableChannels())) {
26            throw new InvalidArgumentException(
27                'Channel is not valid'
28            );
29        }
30        $this->channel = $channel;
31        return $this;
32    }
33
34    /**
35     * @return string
36     */
37    public function channel()
38    {
39        return $this->channel;
40    }
41}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Effect/AbstractRotateEffect.php.html b/packages/image/build/report/Effect/AbstractRotateEffect.php.html new file mode 100644 index 000000000..5c6159bb4 --- /dev/null +++ b/packages/image/build/report/Effect/AbstractRotateEffect.php.html @@ -0,0 +1,292 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/AbstractRotateEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 100.00% covered (success) +
+
+
100.00%
14 / 14
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
AbstractRotateEffect
+
+ 100.00% covered (success) +
+
+
100.00%
14 / 14
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 setAngle
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
 angle
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 setBackgroundColor
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
 backgroundColor
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image\Effect;
6
7use InvalidArgumentException;
8use Charcoal\Image\AbstractEffect;
9
10/**
11 * Rotate the image by a certain angle
12 */
13abstract class AbstractRotateEffect extends AbstractEffect
14{
15    /**
16     * The angle of rotation, in degrees, clockwise
17     * @var float $Angle
18     */
19    private $angle = 0;
20
21    /**
22     * The background color, for non-90-multiple rotation
23     * Defaults to transparent
24     */
25    private string $backgroundColor = 'rgb(100%, 100%, 100%, 0)';
26
27    /**
28     * @param float $angle The rotation angle.
29     * @throws InvalidArgumentException If the angle argument is not numeric.
30     * @return AbstractRotateEffect Chainable
31     */
32    public function setAngle($angle)
33    {
34        if (!is_numeric($angle)) {
35            throw new InvalidArgumentException(
36                'Angle must be a float'
37            );
38        }
39        $this->angle = (float)$angle;
40        return $this;
41    }
42
43    /**
44     * @return float
45     */
46    public function angle()
47    {
48        return $this->angle;
49    }
50
51    /**
52     * @param string $color The background color, for non-90 rotations.
53     * @throws InvalidArgumentException If the color argument is not a string.
54     * @return AbstractRotateEffect Chainable
55     */
56    public function setBackgroundColor($color)
57    {
58        if (!is_string($color)) {
59            throw new InvalidArgumentException(
60                'Color must be a string'
61            );
62        }
63        $this->backgroundColor = $color;
64        return $this;
65    }
66
67    /**
68     * @return string
69     */
70    public function backgroundColor()
71    {
72        return $this->backgroundColor;
73    }
74}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Effect/AbstractSepiaEffect.php.html b/packages/image/build/report/Effect/AbstractSepiaEffect.php.html new file mode 100644 index 000000000..d53feece9 --- /dev/null +++ b/packages/image/build/report/Effect/AbstractSepiaEffect.php.html @@ -0,0 +1,219 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/AbstractSepiaEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 100.00% covered (success) +
+
+
100.00%
8 / 8
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
AbstractSepiaEffect
+
+ 100.00% covered (success) +
+
+
100.00%
8 / 8
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
5
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 setThreshold
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
4
 threshold
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image\Effect;
6
7use InvalidArgumentException;
8use Charcoal\Image\AbstractEffect;
9
10/**
11 * Convert an image to grayscale colorspace
12 */
13abstract class AbstractSepiaEffect extends AbstractEffect
14{
15    /**
16     * @var float $threshold
17     */
18    private $threshold = 75;
19
20
21    /**
22     * @param float $threshold The sepia threshold value.
23     * @throws InvalidArgumentException If the threshold argument is not numeric, lower than 0 or greater than max.
24     * @return AbstractSepiaEffect Chainable
25     */
26    public function setThreshold($threshold)
27    {
28        $max = 255;
29        if (!is_numeric($threshold) || ($threshold < 0) || ($threshold > $max)) {
30            throw new InvalidArgumentException(
31                sprintf('Threshold must be a number between 0 and %s', $max)
32            );
33        }
34        $this->threshold = (float)$threshold;
35        return $this;
36    }
37
38    /**
39     * @return float
40     */
41    public function threshold()
42    {
43        return $this->threshold;
44    }
45}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Effect/AbstractSharpenEffect.php.html b/packages/image/build/report/Effect/AbstractSharpenEffect.php.html new file mode 100644 index 000000000..9ce6b7ce4 --- /dev/null +++ b/packages/image/build/report/Effect/AbstractSharpenEffect.php.html @@ -0,0 +1,672 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/AbstractSharpenEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 96.08% covered (success) +
+
+
96.08%
49 / 51
+
+ 92.31% covered (success) +
+
+
92.31%
12 / 13
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
AbstractSharpenEffect
+
+ 96.08% covered (success) +
+
+
96.08%
49 / 51
+
+ 92.31% covered (success) +
+
+
92.31%
12 / 13
27
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 setRadius
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
3
 radius
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 setSigma
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
3
 sigma
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 setAmount
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
3
 amount
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 setThreshold
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
3
 threshold
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 setMode
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
 mode
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 setChannel
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
 channel
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 process
+
+ 75.00% covered (warning) +
+
+
75.00%
6 / 8
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
5.39
 processAdaptive
n/a
0 / 0
n/a
0 / 0
0
 processUnsharp
n/a
0 / 0
n/a
0 / 0
0
 processStandard
n/a
0 / 0
n/a
0 / 0
0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Effect;
4
5use InvalidArgumentException;
6use Charcoal\Image\AbstractEffect;
7
8/**
9 * Sharpen an image, with a simple sharpen algorithm or unsharp mask options
10 */
11abstract class AbstractSharpenEffect extends AbstractEffect
12{
13    /**
14     * @var float $radius
15     */
16    private $radius = 0;
17
18    /**
19     * @var float $sigma
20     */
21    private $sigma = 1;
22
23    /**
24     * Amount (or _gain_) to unsharp. Only used in `unsharp` mode
25     * @var float $amount
26     */
27    private $amount = 1;
28
29    /**
30     * Threshold. Ony used in `unsharp` mode
31     */
32    private float $threshold = 0.05;
33
34    private string $mode = 'standard';
35
36    /**
37     * @var string $channel
38     */
39    private $channel = 'all';
40
41
42    /**
43     * @param float $radius The sharpen radius value.
44     * @throws InvalidArgumentException If the radius argument is not numeric or lower than 0.
45     * @return self
46     */
47    public function setRadius($radius)
48    {
49        if (!is_numeric($radius) || ($radius < 0)) {
50            throw new InvalidArgumentException(
51                'Radius must be a float (greater than 0)'
52            );
53        }
54         $this->radius = (float)$radius;
55         return $this;
56    }
57
58    /**
59     * @return float
60     */
61    public function radius()
62    {
63        return $this->radius;
64    }
65
66    /**
67     * @param float $sigma The sharpen sigma value.
68     * @throws InvalidArgumentException If the ssigma value is not numeric or lower than 0.
69     * @return self
70     */
71    public function setSigma($sigma)
72    {
73        if (!is_numeric($sigma) || ($sigma < 0)) {
74            throw new InvalidArgumentException(
75                'Sigma value must be a float (greater than 0)'
76            );
77        }
78        $this->sigma = (float)$sigma;
79        return $this;
80    }
81
82    /**
83     * @return float
84     */
85    public function sigma()
86    {
87        return $this->sigma;
88    }
89
90    /**
91     * @param float $amount The sharpen amount.
92     * @throws InvalidArgumentException If the amount argument is not numeric or lower than 0.
93     * @return self
94     */
95    public function setAmount($amount)
96    {
97        if (!is_numeric($amount) || ($amount < 0)) {
98            throw new InvalidArgumentException(
99                'Threshold must be a float (greater than 0)'
100            );
101        }
102         $this->amount = (float)$amount;
103         return $this;
104    }
105
106    /**
107     * @return float
108     */
109    public function amount()
110    {
111        return $this->amount;
112    }
113
114    /**
115     * @param float $threshold The sharpen threshold value.
116     * @throws InvalidArgumentException If the threshold argumnet is not numeric or lower than 0.
117     * @return self
118     */
119    public function setThreshold($threshold)
120    {
121        if (!is_numeric($threshold) || ($threshold < 0)) {
122            throw new InvalidArgumentException(
123                'Threshold must be a float (greater than 0)'
124            );
125        }
126         $this->threshold = (float)$threshold;
127         return $this;
128    }
129
130    /**
131     * @return float
132     */
133    public function threshold()
134    {
135        return $this->threshold;
136    }
137
138    /**
139     * @param string $mode The sharpen mode.
140     * @throws InvalidArgumentException If the mode argument is not a valid sharpen mode.
141     * @return self
142     */
143    public function setMode($mode)
144    {
145        $allowedModes = ['standard', 'adaptive', 'unsharp'];
146        if (!in_array($mode, $allowedModes)) {
147            throw new InvalidArgumentException(
148                sprintf('Mode %s is not an allowed blur mode', $mode)
149            );
150        }
151        $this->mode = $mode;
152        return $this;
153    }
154
155    /**
156     * @return string
157     */
158    public function mode()
159    {
160        return $this->mode;
161    }
162
163    /**
164     * @param string $channel The sharpen channel.
165     * @throws InvalidArgumentException If the channel argument is not a valid channel.
166     * @return self
167     */
168    public function setChannel($channel)
169    {
170        if (!in_array($channel, $this->image()->availableChannels())) {
171            throw new InvalidArgumentException(
172                'Channel is not valid'
173            );
174        }
175        $this->channel = $channel;
176        return $this;
177    }
178
179    /**
180     * @return string
181     */
182    public function channel()
183    {
184        return $this->channel;
185    }
186
187
188    /**
189     * @param array $data The effect data, if available.
190     * @return self
191     */
192    public function process(?array $data = null)
193    {
194        if ($data !== null) {
195            $this->setData($data);
196        }
197
198        $mode = $this->mode();
199        return match ($mode) {
200            'adaptive' => $this->processAdaptive(),
201            'unsharp' => $this->processUnsharp(),
202            default => $this->processStandard(),
203        };
204    }
205
206    /**
207     * @return self
208     */
209    abstract public function processAdaptive();
210
211    /**
212     * @return self
213     */
214    abstract public function processUnsharp();
215
216    /**
217     * @return self
218     */
219    abstract public function processStandard();
220}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Effect/AbstractThresholdEffect.php.html b/packages/image/build/report/Effect/AbstractThresholdEffect.php.html new file mode 100644 index 000000000..9b63a9ed7 --- /dev/null +++ b/packages/image/build/report/Effect/AbstractThresholdEffect.php.html @@ -0,0 +1,214 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/AbstractThresholdEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
AbstractThresholdEffect
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
5
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 setThreshold
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
4
 threshold
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image\Effect;
6
7use InvalidArgumentException;
8use Charcoal\Image\AbstractEffect;
9
10/**
11 * Convert the image to monochrome (black and white)
12 */
13abstract class AbstractThresholdEffect extends AbstractEffect
14{
15    private float $threshold = 0.5;
16
17    /**
18     * @param float $threshold The threshold value.
19     * @throws InvalidArgumentException If the threshold is not numeric or lower than zero / greater than 1.
20     * @return AbstractThresholdEffect Chainable
21     */
22    public function setThreshold($threshold)
23    {
24        if (!is_numeric($threshold) || ($threshold < 0) || ($threshold > 1)) {
25            throw new InvalidArgumentException(
26                'Threshold must be a float between 0 and 1.'
27            );
28        }
29         $this->threshold = (float)$threshold;
30         return $this;
31    }
32
33    /**
34     * @return float
35     */
36    public function threshold()
37    {
38        return $this->threshold;
39    }
40}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Effect/AbstractTintEffect.php.html b/packages/image/build/report/Effect/AbstractTintEffect.php.html new file mode 100644 index 000000000..3bf1c60ce --- /dev/null +++ b/packages/image/build/report/Effect/AbstractTintEffect.php.html @@ -0,0 +1,349 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/AbstractTintEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 100.00% covered (success) +
+
+
100.00%
17 / 17
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
AbstractTintEffect
+
+ 100.00% covered (success) +
+
+
100.00%
17 / 17
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
10
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 setColor
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
 color
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 setOpacity
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
4
 opacity
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 setMidtone
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 midtone
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image\Effect;
6
7use InvalidArgumentException;
8use Charcoal\Image\AbstractEffect;
9
10/**
11 * Tint (or colorize) the image with a certain color.
12 */
13abstract class AbstractTintEffect extends AbstractEffect
14{
15    private string $color = 'rgb(0,0,0)';
16    /**
17     * @var float $opacity;
18     */
19    private float $opacity = 0.5;
20    private bool $midtone = true;
21
22    /**
23     * @param string $color The tint color value.
24     * @throws InvalidArgumentException If the color is not a string.
25     * @return AbstractTintEffect Chainable
26     */
27    public function setColor($color)
28    {
29        if (!is_string($color)) {
30            throw new InvalidArgumentException(
31                'Color must be a string'
32            );
33        }
34        $this->color = $color;
35        return $this;
36    }
37
38    /**
39     * @return string
40     */
41    public function color()
42    {
43        return $this->color;
44    }
45
46    /**
47     * @param float $opacity The tint opacity value.
48     * @throws InvalidArgumentException If the tint value is not numeric or lower than 0 / greater than 1.
49     * @return AbstractTintEffect Chainable
50     */
51    public function setOpacity($opacity)
52    {
53        if (!is_numeric($opacity) || ($opacity < 0) || ( $opacity > 1)) {
54            throw new InvalidArgumentException(
55                'Opacity must be a float between 0.0 and 1.0'
56            );
57        }
58        $this->opacity = (float)$opacity;
59        return $this;
60    }
61
62    /**
63     * @return float
64     */
65    public function opacity()
66    {
67        return $this->opacity;
68    }
69
70    /**
71     * @param boolean $midtone The tint midtone flag.
72     * @return AbstractTintEffect Chainable
73     */
74    public function setMidtone($midtone)
75    {
76        $this->midtone = (bool)$midtone;
77        return $this;
78    }
79
80    /**
81     * @return boolean
82     */
83    public function midtone()
84    {
85        return $this->midtone;
86    }
87}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Effect/AbstractWatermarkEffect.php.html b/packages/image/build/report/Effect/AbstractWatermarkEffect.php.html new file mode 100644 index 000000000..a1c6fb9af --- /dev/null +++ b/packages/image/build/report/Effect/AbstractWatermarkEffect.php.html @@ -0,0 +1,224 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/AbstractWatermarkEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
AbstractWatermarkEffect
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
4
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 setWatermark
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
3
 watermark
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image\Effect;
6
7use InvalidArgumentException;
8use Charcoal\Image\AbstractEffect;
9use Charcoal\Image\ImageInterface;
10use Charcoal\Image\Effect\LayerEffectInterface;
11use Charcoal\Image\Effect\LayerEffectTrait;
12
13/**
14 * Composite a watermark on top of the image.
15 */
16abstract class AbstractWatermarkEffect extends AbstractEffect implements LayerEffectInterface
17{
18    use LayerEffectTrait;
19
20    /**
21     * The watermark image source (path or Image).
22     * @var string|ImageInterface $watermark
23     */
24    private string|\Charcoal\Image\ImageInterface|null $watermark = null;
25
26    /**
27     * @param string|ImageInterface $watermark The watermark (path or Image).
28     * @throws InvalidArgumentException If the watermark value is not a string or an Image.
29     * @return self
30     */
31    public function setWatermark($watermark)
32    {
33        if (is_string($watermark) || ($watermark instanceof ImageInterface)) {
34            $this->watermark = $watermark;
35            return $this;
36        } else {
37            throw new InvalidArgumentException(
38                'Watermark must be a string'
39            );
40        }
41    }
42
43    /**
44     * @return string
45     */
46    public function watermark()
47    {
48        return $this->watermark;
49    }
50}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Effect/LayerEffectInterface.php.html b/packages/image/build/report/Effect/LayerEffectInterface.php.html new file mode 100644 index 000000000..69e5ab1c6 --- /dev/null +++ b/packages/image/build/report/Effect/LayerEffectInterface.php.html @@ -0,0 +1,150 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/LayerEffectInterface.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image\Effect;
6
7/**
8 * Layer effect defines image effects that stack an image on top of another.
9 *
10 * The "layer" can therefore have the following properties:
11 * - opacity
12 * - gravity
13 * - x
14 * - y
15 */
16interface LayerEffectInterface
17{
18    /**
19     * @param float $opacity The mask opacity.
20     * @throws InvalidArgumentException If the mask opacity is not a numeric value or not between 0.0 and 1.0.
21     * @return AbstractMaskEffect Chainable
22     */
23    public function setOpacity($opacity);
24
25    /**
26     * @return float
27     */
28    public function opacity();
29
30    /**
31     * @param string $gravity The mask gravity.
32     * @throws InvalidArgumentException If the argument is not a valid gravity name.
33     * @return AbstractMaskEffect Chainable
34     */
35    public function setGravity($gravity);
36
37    /**
38     * @return string
39     */
40    public function gravity();
41
42    /**
43     * @param integer $x The mask X position.
44     * @throws InvalidArgumentException If the position is not a numeric value.
45     * @return AbstractMaskEffect Chainable
46     */
47    public function setX($x);
48
49    /**
50     * @return float
51     */
52    public function x();
53
54    /**
55     * @param integer $y The Y position.
56     * @return AbstractMaskEffect Chainable
57     */
58    public function setY($y);
59
60    /**
61     * @return float
62     */
63    public function y();
64}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Effect/LayerEffectTrait.php.html b/packages/image/build/report/Effect/LayerEffectTrait.php.html new file mode 100644 index 000000000..9d9b9f6f0 --- /dev/null +++ b/packages/image/build/report/Effect/LayerEffectTrait.php.html @@ -0,0 +1,456 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/LayerEffectTrait.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 100.00% covered (success) +
+
+
100.00%
28 / 28
+
+ 100.00% covered (success) +
+
+
100.00%
8 / 8
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
LayerEffectTrait
+
+ 100.00% covered (success) +
+
+
100.00%
28 / 28
+
+ 100.00% covered (success) +
+
+
100.00%
8 / 8
14
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 setOpacity
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
4
 opacity
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 setGravity
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
 gravity
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 setX
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
 x
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 setY
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
 y
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 image
n/a
0 / 0
n/a
0 / 0
0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Effect;
4
5use InvalidArgumentException;
6
7/**
8 * Full implementation, as a Trait, of the Layer Effect Interface.
9 */
10trait LayerEffectTrait
11{
12    /**
13     * @var float
14     */
15    private $opacity = 1.0;
16
17    /**
18     * @var string
19     */
20    private $gravity = 'nw';
21
22    /**
23     * Horizontal adjustment, in pixels.
24     * Negative values will move mask to the left, positive values to the right.
25     * Depends on the gravity setting
26     * @var integer
27     */
28    private $x = 0;
29
30    /**
31     * Vertical adjustment, in pixels.
32     * Negative values will move mask to the top, positive values to the bottom.
33     * Depends on the gravity setting
34     * @var integer
35     */
36    private $y = 0;
37
38    /**
39     * @param float $opacity The mask opacity.
40     * @throws InvalidArgumentException If the mask opacity is not a numeric value or not between 0.0 and 1.0.
41     * @return self
42     */
43    public function setOpacity($opacity)
44    {
45        if (!is_numeric($opacity) || ($opacity < 0) || ( $opacity > 1)) {
46            throw new InvalidArgumentException(
47                'Opacity must be a float between 0.0 and 1.0'
48            );
49        }
50        $this->opacity = (float)$opacity;
51        return $this;
52    }
53
54    /**
55     * @return float
56     */
57    public function opacity()
58    {
59        return $this->opacity;
60    }
61
62    /**
63     * @param string $gravity The mask gravity.
64     * @throws InvalidArgumentException If the argument is not a valid gravity name.
65     * @return self
66     */
67    public function setGravity($gravity)
68    {
69        if (!in_array($gravity, $this->image()->availableGravities())) {
70            throw new InvalidArgumentException(
71                'Gravity is not valid'
72            );
73        }
74        $this->gravity = $gravity;
75        return $this;
76    }
77
78    /**
79     * @return string
80     */
81    public function gravity()
82    {
83        return $this->gravity;
84    }
85
86    /**
87     * @param integer $x The mask X position.
88     * @throws InvalidArgumentException If the position is not a numeric value.
89     * @return self
90     */
91    public function setX($x)
92    {
93        if (!is_numeric($x)) {
94            throw new InvalidArgumentException(
95                'X must be a an integer (in pixel)'
96            );
97        }
98        $this->x = (int)$x;
99        return $this;
100    }
101
102    /**
103     * @return float
104     */
105    public function x()
106    {
107        return $this->x;
108    }
109
110    /**
111     * @param integer $y The Y position.
112     * @throws InvalidArgumentException If the position is not a numeric value.
113     * @return self
114     */
115    public function setY($y)
116    {
117        if (!is_numeric($y)) {
118            throw new InvalidArgumentException(
119                'Y must be a an integer (in pixel)'
120            );
121        }
122        $this->y = (int)$y;
123        return $this;
124    }
125
126    /**
127     * @return float
128     */
129    public function y()
130    {
131        return $this->y;
132    }
133
134    /**
135     * @return \Charcoal\Image\ImageInterface
136     */
137    abstract public function image();
138}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Effect/dashboard.html b/packages/image/build/report/Effect/dashboard.html new file mode 100644 index 000000000..d3fafc8a9 --- /dev/null +++ b/packages/image/build/report/Effect/dashboard.html @@ -0,0 +1,348 @@ + + + + + Dashboard for /var/www/html/packages/image/src/Charcoal/Image/Effect + + + + + + + +
+
+
+ +
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+ +
+

Project Risks

+
+ + + + + + + + + + + + + + + +
ClassCoverageComplexityCRAP
Charcoal\Image\Effect\AbstractCropEffect0.0%29870
Charcoal\Image\Effect\AbstractResizeEffect58.6%89650
Charcoal\Image\Effect\AbstractFormatEffect0.0%312
+
+
+
+
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MethodCoverage
setQuality0%
quality0%
setWidth0%
width0%
setHeight0%
height0%
setX0%
x0%
setY0%
y0%
setGeometry0%
geometry0%
setGravity0%
gravity0%
setRepage0%
repage0%
process0%
setFormat0%
format0%
setMinWidth0%
setMinHeight0%
setMaxWidth0%
setMaxHeight0%
process40%
process75%
+
+
+
+

Project Risks

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
MethodCoverageComplexityCRAP
process40.5%38342
setGeometry0.0%420
setWidth0.0%312
setHeight0.0%312
setX0.0%312
setY0.0%312
process0.0%312
setMinWidth0.0%312
setMinHeight0.0%312
setMaxWidth0.0%312
setMaxHeight0.0%312
setGravity0.0%26
setFormat0.0%26
process75.0%55
+
+
+
+ +
+ + + + + + diff --git a/packages/image/build/report/Effect/index.html b/packages/image/build/report/Effect/index.html new file mode 100644 index 000000000..a9bd913c6 --- /dev/null +++ b/packages/image/build/report/Effect/index.html @@ -0,0 +1,606 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect + + + + + + + +
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 71.32% covered (warning) +
+
+
71.32%
363 / 509
+
+ 74.77% covered (warning) +
+
+
74.77%
80 / 107
+
+ 64.71% covered (warning) +
+
+
64.71%
11 / 17
AbstractAutoorientationEffect.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
AbstractBlurEffect.php
+
+ 96.30% covered (success) +
+
+
96.30%
52 / 54
+
+ 90.91% covered (success) +
+
+
90.91%
10 / 11
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
AbstractCompressionEffect.php
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 3
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
AbstractCropEffect.php
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 56
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 15
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
AbstractDitherEffect.php
+
+ 100.00% covered (success) +
+
+
100.00%
36 / 36
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
AbstractFormatEffect.php
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 11
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 2
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
AbstractGrayscaleEffect.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
AbstractMaskEffect.php
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
AbstractMirrorEffect.php
+
+ 100.00% covered (success) +
+
+
100.00%
8 / 8
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
AbstractModulateEffect.php
+
+ 100.00% covered (success) +
+
+
100.00%
21 / 21
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
AbstractResizeEffect.php
+
+ 58.62% covered (warning) +
+
+
58.62%
102 / 174
+
+ 75.00% covered (warning) +
+
+
75.00%
18 / 24
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
AbstractRevertEffect.php
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
AbstractRotateEffect.php
+
+ 100.00% covered (success) +
+
+
100.00%
14 / 14
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
AbstractSepiaEffect.php
+
+ 100.00% covered (success) +
+
+
100.00%
8 / 8
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
AbstractSharpenEffect.php
+
+ 96.08% covered (success) +
+
+
96.08%
49 / 51
+
+ 92.31% covered (success) +
+
+
92.31%
12 / 13
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
AbstractThresholdEffect.php
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
AbstractTintEffect.php
+
+ 100.00% covered (success) +
+
+
100.00%
17 / 17
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
AbstractWatermarkEffect.php
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
LayerEffectInterface.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
LayerEffectTrait.php
+
+ 100.00% covered (success) +
+
+
100.00%
28 / 28
+
+ 100.00% covered (success) +
+
+
100.00%
8 / 8
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ +
+ + diff --git a/packages/image/build/report/EffectFactory.php.html b/packages/image/build/report/EffectFactory.php.html new file mode 100644 index 000000000..34668035e --- /dev/null +++ b/packages/image/build/report/EffectFactory.php.html @@ -0,0 +1,114 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/EffectFactory.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
EffectFactory
n/a
0 / 0
n/a
0 / 0
0
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + +
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image;
6
7// From 'charcoal-factory'
8use Charcoal\Factory\AbstractFactory;
9
10/**
11 *
12 */
13class EffectFactory extends AbstractFactory
14{
15}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/EffectInterface.php.html b/packages/image/build/report/EffectInterface.php.html new file mode 100644 index 000000000..af8aa54bf --- /dev/null +++ b/packages/image/build/report/EffectInterface.php.html @@ -0,0 +1,116 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/EffectInterface.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image;
6
7use Charcoal\Image\ImageInterface;
8
9/**
10 * Image Effect Interface.
11 */
12interface EffectInterface
13{
14    /**
15     * @param ImageInterface $image The parent image.
16     * @return EffectInterface
17     */
18    public function setImage(ImageInterface $image);
19
20    /**
21     * @param array $data The effect data.
22     * @return EffectInterface
23     */
24    public function setData(array $data);
25
26    /**
27     * @param array $data The effect data, if available.
28     * @return EffectInterface
29     */
30    public function process(?array $data = null);
31}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/ImageFactory.php.html b/packages/image/build/report/ImageFactory.php.html new file mode 100644 index 000000000..6484f53fc --- /dev/null +++ b/packages/image/build/report/ImageFactory.php.html @@ -0,0 +1,203 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/ImageFactory.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
CRAP
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
ImageFactory
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
3
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
 __construct
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
 defaultMap
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image;
4
5// From 'charcoal-factory'
6use Charcoal\Factory\AbstractFactory;
7
8/**
9 * Create image class from image processor type.
10 */
11class ImageFactory extends AbstractFactory
12{
13    /**
14     * @param array $data Constructor dependencies.
15     */
16    public function __construct(?array $data = null)
17    {
18        $data['map'] = isset($data['map']) ? array_merge($this->defaultMap(), $data['map']) : $this->defaultMap();
19
20        parent::__construct($data);
21    }
22
23    protected function defaultMap(): array
24    {
25        return [
26            'imagick'     => \Charcoal\Image\Imagick\ImagickImage::class,
27            'imagemagick' => \Charcoal\Image\Imagemagick\ImagemagickImage::class
28        ];
29    }
30}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/ImageInterface.php.html b/packages/image/build/report/ImageInterface.php.html new file mode 100644 index 000000000..2c5d14f8e --- /dev/null +++ b/packages/image/build/report/ImageInterface.php.html @@ -0,0 +1,220 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/ImageInterface.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image;
6
7interface ImageInterface
8{
9    /**
10     * @param array $data The image data.
11     * @return ImageInterface Chainable
12     */
13    public function setData(array $data);
14
15    /**
16     * @param string $source The source.
17     * @return ImageInterface Chainable
18     */
19    public function setSource($source);
20
21    /**
22     * @return string
23     */
24    public function source();
25
26    /**
27     * @param string $target The target.
28     * @return ImageInterface Chainable
29     */
30    public function setTarget($target);
31
32    /**
33     * @return string
34     */
35    public function target();
36
37    /**
38     * @param array $effects The list of effects.
39     * @return ImageInterface Chainable
40     */
41    public function setEffects(array $effects);
42
43    /**
44     * @return EffectInterface[] The array of `EffectInterface` effects
45     */
46    public function effects();
47
48    /**
49     * @param array|EffectInterface $effect The effect, or effect options, to add.
50     * @return ImageInterface Chainable
51     */
52    public function addEffect($effect);
53
54    /**
55     * @param array $effects Extra effects to process.
56     * @return ImageInterface Chainable
57     */
58    public function process(?array $effects = null);
59
60    /**
61     * @param array|EffectInterface $effect An effect to process.
62     * @return ImageInterface Chainable
63     */
64    public function processEffect($effect);
65
66    /**
67     * Create a blank canvas of a given size, with a given background color.
68     *
69     * @param integer $width  Image size, in pixels.
70     * @param integer $height Image height, in pixels.
71     * @param string  $color  Default to transparent.
72     * @return ImageInterface Chainable
73     */
74    public function create($width, $height, $color = 'rgb(100%, 100%, 100%, 0)');
75
76    /**
77     * Open an image file
78     *
79     * @param string $source The source path / filename.
80     * @return ImageInterface Chainable
81     */
82    public function open($source = null);
83
84    /**
85     * Save an image to a target.
86     * If no target is set, the original source will be owerwritten
87     *
88     * @param string $target The target path / filename.
89     * @return ImageInterface Chainable
90     */
91    public function save($target = null);
92
93    /**
94     * Get the image's width, in pixels
95     *
96     * @return integer
97     */
98    public function width();
99
100    /**
101     * Get the image's height, in pixels
102     *
103     * @return integer
104     */
105    public function height();
106
107    /**
108     * Get the image's ratio (width / height)
109     *
110     * @return float
111     */
112    public function ratio();
113
114    /**
115     * Orientation can be "horizontal", "vertical" or "square"
116     *
117     * @return string
118     */
119    public function orientation();
120
121    /**
122     * @return boolean
123     */
124    public function isHorizontal();
125
126    /**
127     * @return boolean
128     */
129    public function isVertical();
130
131    /**
132     * @return boolean
133     */
134    public function isSquare();
135}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Imagemagick/Effect/ImagemagickAutoorientationEffect.php.html b/packages/image/build/report/Imagemagick/Effect/ImagemagickAutoorientationEffect.php.html new file mode 100644 index 000000000..50df53239 --- /dev/null +++ b/packages/image/build/report/Imagemagick/Effect/ImagemagickAutoorientationEffect.php.html @@ -0,0 +1,178 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickAutoorientationEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagemagickAutoorientationEffect
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 process
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
6
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Imagemagick\Effect;
4
5use Charcoal\Image\Effect\AbstractAutoorientationEffect;
6
7/**
8 * Auto-orientate Effect for the Imagemagick driver.
9 */
10class ImagemagickAutoorientationEffect extends AbstractAutoorientationEffect
11{
12    /**
13     * @param array $data The effect data, if available.
14     * @return self
15     */
16    public function process(?array $data = null)
17    {
18        if ($data !== null) {
19            $this->setData($data);
20        }
21
22        $cmd = '-auto-orient';
23        return $this->image()->applyCmd($cmd);
24    }
25}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Imagemagick/Effect/ImagemagickBlurEffect.php.html b/packages/image/build/report/Imagemagick/Effect/ImagemagickBlurEffect.php.html new file mode 100644 index 000000000..564e20bae --- /dev/null +++ b/packages/image/build/report/Imagemagick/Effect/ImagemagickBlurEffect.php.html @@ -0,0 +1,321 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickBlurEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 86.96% covered (success) +
+
+
86.96%
20 / 23
+
+ 83.33% covered (success) +
+
+
83.33%
5 / 6
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagemagickBlurEffect
+
+ 86.96% covered (success) +
+
+
86.96%
20 / 23
+
+ 83.33% covered (success) +
+
+
83.33%
5 / 6
6.08
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 processAdaptive
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 processGaussian
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 processMotion
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 processRadial
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 processSoft
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 3
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
 processStandard
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Imagemagick\Effect;
4
5use Charcoal\Image\Effect\AbstractBlurEffect;
6
7/**
8 * Blur Effect for the Imagemagick driver.
9 */
10class ImagemagickBlurEffect extends AbstractBlurEffect
11{
12    public function processAdaptive(): static
13    {
14        $channel = $this->image()->convertChannel($this->channel());
15        $cmd = '-channel ' . $channel . ' -adaptive-blur ' . $this->radius() . 'x' . $this->sigma();
16        $this->image()->applyCmd($cmd);
17        return $this;
18    }
19
20    public function processGaussian(): static
21    {
22        $channel = $this->image()->convertChannel($this->channel());
23        $cmd = '-channel ' . $channel . ' -gaussian-blur ' . $this->radius() . 'x' . $this->sigma();
24        $this->image()->applyCmd($cmd);
25        return $this;
26    }
27
28    public function processMotion(): static
29    {
30        $channel = $this->image()->convertChannel($this->channel());
31        $cmd = '-channel ' . $channel . ' -motion-blur ' . $this->radius() . 'x' . $this->sigma() . '+' . $this->angle();
32        $this->image()->applyCmd($cmd);
33        return $this;
34    }
35
36    public function processRadial(): static
37    {
38        $channel = $this->image()->convertChannel($this->channel());
39        $cmd = '-channel ' . $channel . ' -rotational-blur ' . $this->angle();
40        $this->image()->applyCmd($cmd);
41        return $this;
42    }
43
44    public function processSoft(): static
45    {
46        $cmd = '-define convolve:scale=60,40% -morphology Convolve \'Gaussian:' . $this->radius() . 'x' . $this->sigma() . '\'';
47        $this->image()->applyCmd($cmd);
48        return $this;
49    }
50
51    public function processStandard(): static
52    {
53        $channel = $this->image()->convertChannel($this->channel());
54        $cmd = '-channel ' . $channel . ' -blur ' . $this->radius() . 'x' . $this->sigma();
55        $this->image()->applyCmd($cmd);
56        return $this;
57    }
58}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Imagemagick/Effect/ImagemagickCompressionEffect.php.html b/packages/image/build/report/Imagemagick/Effect/ImagemagickCompressionEffect.php.html new file mode 100644 index 000000000..343ca5401 --- /dev/null +++ b/packages/image/build/report/Imagemagick/Effect/ImagemagickCompressionEffect.php.html @@ -0,0 +1,178 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickCompressionEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 5
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagemagickCompressionEffect
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 5
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 process
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 5
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
6
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Imagemagick\Effect;
4
5use Charcoal\Image\Effect\AbstractCompressionEffect;
6
7/**
8 * Compression Effect for the Imagemagick driver.
9 */
10class ImagemagickCompressionEffect extends AbstractCompressionEffect
11{
12    /**
13     * @param array $data The effect data, if available.
14     */
15    public function process(?array $data = null): static
16    {
17        if ($data !== null) {
18            $this->setData($data);
19        }
20
21        $cmd = sprintf('-quality %s%', $this->quality());
22        $this->image()->applyCmd($cmd);
23        return $this;
24    }
25}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Imagemagick/Effect/ImagemagickCropEffect.php.html b/packages/image/build/report/Imagemagick/Effect/ImagemagickCropEffect.php.html new file mode 100644 index 000000000..ab4a1b6d5 --- /dev/null +++ b/packages/image/build/report/Imagemagick/Effect/ImagemagickCropEffect.php.html @@ -0,0 +1,196 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickCropEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 10
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagemagickCropEffect
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 10
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
30
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 doCrop
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 10
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
30
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Imagemagick\Effect;
4
5use Exception;
6use Charcoal\Image\Effect\AbstractCropEffect;
7
8/**
9 * Reisze Effect for the Imagemagick driver.
10 *
11 * See {@link http://www.imagemagick.org/script/command-line-processing.php#geometry Image Geometry}
12 * for complete details about the geometry argument.
13 */
14class ImagemagickCropEffect extends AbstractCropEffect
15{
16    /**
17     * @param  integer $width  The crop width.
18     * @param  integer $height The crop height.
19     * @param  integer $x      The x-position (in pixel) of the crop.
20     * @param  integer $y      The y-position (in pixel) of the crop.
21     * @return void
22     */
23    protected function doCrop($width, $height, $x, $y)
24    {
25        $option = '-crop';
26
27        $geometry = $this->geometry();
28        if ($geometry) {
29            $params = [ $option . ' "' . $geometry . '"' ];
30        } else {
31            $params = [ '-gravity "' . $this->gravity() . '"' ];
32
33            $size = $width . 'x' . $height . ($x >= 0 ? '+' : '') . $x . ($y >= 0 ? '+' : '') . $y;
34            $params[] = $option . ' ' . $size;
35        }
36
37        if ($this->repage()) {
38            $params[] = '+repage';
39        }
40
41        $this->image()->applyCmd(implode(' ', $params));
42    }
43}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Imagemagick/Effect/ImagemagickDitherEffect.php.html b/packages/image/build/report/Imagemagick/Effect/ImagemagickDitherEffect.php.html new file mode 100644 index 000000000..1ee3c5239 --- /dev/null +++ b/packages/image/build/report/Imagemagick/Effect/ImagemagickDitherEffect.php.html @@ -0,0 +1,182 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickDitherEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 5
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagemagickDitherEffect
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 5
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 process
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 5
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
6
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image\Imagemagick\Effect;
6
7use Exception;
8use Charcoal\Image\Effect\AbstractDitherEffect;
9
10/**
11 * Dither Effect for the Imagemagick driver.
12 */
13class ImagemagickDitherEffect extends AbstractDitherEffect
14{
15    /**
16     * @param array $data The effect data, if available.
17     * @throws Exception This effect is not supported for Imagemagick driver.
18     */
19    public function process(?array $data = null): void
20    {
21        if ($data !== null) {
22            $this->setData($data);
23        }
24
25        throw new Exception(
26            'Dither Effect is not (yet) supported with imagemagick driver.'
27        );
28    }
29}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Imagemagick/Effect/ImagemagickFormatEffect.php.html b/packages/image/build/report/Imagemagick/Effect/ImagemagickFormatEffect.php.html new file mode 100644 index 000000000..fcb734659 --- /dev/null +++ b/packages/image/build/report/Imagemagick/Effect/ImagemagickFormatEffect.php.html @@ -0,0 +1,181 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickFormatEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagemagickFormatEffect
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
12
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 process
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
12
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image\Imagemagick\Effect;
6
7use Charcoal\Image\Effect\AbstractFormatEffect;
8
9/**
10 * Format Effect for the Imagemagick driver.
11 */
12class ImagemagickFormatEffect extends AbstractFormatEffect
13{
14    /**
15     * @param array $data The effect data, if available.
16     */
17    public function process(?array $data = null): static
18    {
19        if ($data !== null) {
20            $this->setData($data);
21        }
22
23        if ($this->format()) {
24            // Currently not supported
25        }
26        return $this;
27    }
28}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Imagemagick/Effect/ImagemagickGrayscaleEffect.php.html b/packages/image/build/report/Imagemagick/Effect/ImagemagickGrayscaleEffect.php.html new file mode 100644 index 000000000..172a58558 --- /dev/null +++ b/packages/image/build/report/Imagemagick/Effect/ImagemagickGrayscaleEffect.php.html @@ -0,0 +1,178 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickGrayscaleEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 75.00% covered (warning) +
+
+
75.00%
3 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagemagickGrayscaleEffect
+
+ 75.00% covered (warning) +
+
+
75.00%
3 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2.06
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 process
+
+ 75.00% covered (warning) +
+
+
75.00%
3 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2.06
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Imagemagick\Effect;
4
5use Charcoal\Image\Effect\AbstractGrayscaleEffect;
6
7/**
8 * Grayscale Effect for the Imagemagick driver.
9 */
10class ImagemagickGrayscaleEffect extends AbstractGrayscaleEffect
11{
12    /**
13     * @param array $data The effect data, if available.
14     * @return self
15     */
16    public function process(?array $data = null)
17    {
18        if ($data !== null) {
19            $this->setData($data);
20        }
21
22        $cmd = '-colorspace Gray';
23        return $this->image()->applyCmd($cmd);
24    }
25}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Imagemagick/Effect/ImagemagickMaskEffect.php.html b/packages/image/build/report/Imagemagick/Effect/ImagemagickMaskEffect.php.html new file mode 100644 index 000000000..8c3a74c7a --- /dev/null +++ b/packages/image/build/report/Imagemagick/Effect/ImagemagickMaskEffect.php.html @@ -0,0 +1,182 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickMaskEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 5
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagemagickMaskEffect
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 5
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 process
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 5
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
6
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image\Imagemagick\Effect;
6
7use Exception;
8use Charcoal\Image\Effect\AbstractMaskEffect;
9
10/**
11 * Mask Effect for the Imagemagick driver.
12 */
13class ImagemagickMaskEffect extends AbstractMaskEffect
14{
15    /**
16     * @param array $data The effect data, if available.
17     * @throws Exception This effect is not yet supported by Imagemagick driver.
18     */
19    public function process(?array $data = null): void
20    {
21        if ($data !== null) {
22            $this->setData($data);
23        }
24
25        throw new Exception(
26            'Mask Effect is not (yet) supported with imagemagick driver.'
27        );
28    }
29}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Imagemagick/Effect/ImagemagickMirrorEffect.php.html b/packages/image/build/report/Imagemagick/Effect/ImagemagickMirrorEffect.php.html new file mode 100644 index 000000000..253ea57ab --- /dev/null +++ b/packages/image/build/report/Imagemagick/Effect/ImagemagickMirrorEffect.php.html @@ -0,0 +1,179 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickMirrorEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 83.33% covered (success) +
+
+
83.33%
5 / 6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagemagickMirrorEffect
+
+ 83.33% covered (success) +
+
+
83.33%
5 / 6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
3.04
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 process
+
+ 83.33% covered (success) +
+
+
83.33%
5 / 6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
3.04
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Imagemagick\Effect;
4
5use Charcoal\Image\Effect\AbstractMirrorEffect;
6
7/**
8 * Mirror Effect for the Imagemagick driver.
9 */
10class ImagemagickMirrorEffect extends AbstractMirrorEffect
11{
12    /**
13     * @param array $data The effect data, if available.
14     */
15    public function process(?array $data = null): static
16    {
17        if ($data !== null) {
18            $this->setData($data);
19        }
20
21        $axis = $this->axis();
22        $cmd = $axis == 'x' ? '-flip' : '-flop';
23        $this->image()->applyCmd($cmd);
24        return $this;
25    }
26}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Imagemagick/Effect/ImagemagickModulateEffect.php.html b/packages/image/build/report/Imagemagick/Effect/ImagemagickModulateEffect.php.html new file mode 100644 index 000000000..95df6c1be --- /dev/null +++ b/packages/image/build/report/Imagemagick/Effect/ImagemagickModulateEffect.php.html @@ -0,0 +1,182 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickModulateEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 87.50% covered (success) +
+
+
87.50%
7 / 8
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagemagickModulateEffect
+
+ 87.50% covered (success) +
+
+
87.50%
7 / 8
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2.01
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 process
+
+ 87.50% covered (success) +
+
+
87.50%
7 / 8
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2.01
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Imagemagick\Effect;
4
5use Charcoal\Image\Effect\AbstractModulateEffect;
6
7/**
8 * Modulate Effect for the Imagemagick driver.
9 */
10class ImagemagickModulateEffect extends AbstractModulateEffect
11{
12    /**
13     * @param array $data The effect data, if available.
14     */
15    public function process(?array $data = null): static
16    {
17        if ($data !== null) {
18            $this->setData($data);
19        }
20
21        $h = ($this->hue() + 100);
22        $s = ($this->saturation() + 100);
23        $l = ($this->luminance() + 100);
24
25        $cmd = '-modulate ' . $l . ',' . $s . ',' . $h;
26        $this->image()->applyCmd($cmd);
27        return $this;
28    }
29}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Imagemagick/Effect/ImagemagickResizeEffect.php.html b/packages/image/build/report/Imagemagick/Effect/ImagemagickResizeEffect.php.html new file mode 100644 index 000000000..35c32f05d --- /dev/null +++ b/packages/image/build/report/Imagemagick/Effect/ImagemagickResizeEffect.php.html @@ -0,0 +1,217 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickResizeEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 91.30% covered (success) +
+
+
91.30%
21 / 23
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagemagickResizeEffect
+
+ 91.30% covered (success) +
+
+
91.30%
21 / 23
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
10.07
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 doResize
+
+ 91.30% covered (success) +
+
+
91.30%
21 / 23
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
10.07
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Imagemagick\Effect;
4
5use Charcoal\Image\Effect\AbstractResizeEffect;
6
7/**
8 * Reisze Effect for the Imagemagick driver.
9 *
10 * See {@link http://www.imagemagick.org/script/command-line-processing.php#geometry Image Geometry}
11 * for complete details about the geometry argument.
12 */
13class ImagemagickResizeEffect extends AbstractResizeEffect
14{
15    /**
16     * @param  integer $width   The target width.
17     * @param  integer $height  The target height.
18     * @param  boolean $bestFit The "best_fit" flag.
19     * @return void
20     */
21    protected function doResize($width, $height, $bestFit = false)
22    {
23        $option = $this->adaptive() ? '-adaptive-resize' : '-resize';
24
25        $size = $this->size();
26        if ($size) {
27            $params = [ $option . ' "' . $size . '"' ];
28        } else {
29            if ($width === 0 && $height === 0) {
30                return;
31            }
32
33            $params = [
34                '-gravity "' . $this->gravity() . '"',
35                '-background "' . $this->backgroundColor() . '"'
36            ];
37
38            if ($width === 0) {
39                $width = '';
40            }
41
42            if ($height === 0) {
43                $height = '';
44            }
45
46            $size = $width . 'x' . $height;
47            if ($bestFit) {
48                if ($width && $height) {
49                    // Minimum values of width and height given, aspect ratio preserved.
50                    $operator = '^';
51                } else {
52                    $operator = '';
53                }
54
55                $params[] = $option . ' "' . $size . $operator . '"';
56                $params[] = '-extent ' . $size;
57            } else {
58                $params[] = $option . ' ' . $size;
59            }
60        }
61
62        $this->image()->applyCmd(implode(' ', $params));
63    }
64}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Imagemagick/Effect/ImagemagickRevertEffect.php.html b/packages/image/build/report/Imagemagick/Effect/ImagemagickRevertEffect.php.html new file mode 100644 index 000000000..7b0a3601c --- /dev/null +++ b/packages/image/build/report/Imagemagick/Effect/ImagemagickRevertEffect.php.html @@ -0,0 +1,179 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickRevertEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 83.33% covered (success) +
+
+
83.33%
5 / 6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagemagickRevertEffect
+
+ 83.33% covered (success) +
+
+
83.33%
5 / 6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2.02
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 process
+
+ 83.33% covered (success) +
+
+
83.33%
5 / 6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2.02
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Imagemagick\Effect;
4
5use Charcoal\Image\Effect\AbstractRevertEffect;
6
7/**
8 * Rotate Effect for the Imagemagick driver.
9 */
10class ImagemagickRevertEffect extends AbstractRevertEffect
11{
12    /**
13     * @param array $data The effect data, if available.
14     */
15    public function process(?array $data = null): static
16    {
17        if ($data !== null) {
18            $this->setData($data);
19        }
20
21        $channel = $this->image()->convertChannel($this->channel());
22        $cmd = '-channel ' . $channel . ' -negate';
23        $this->image()->applyCmd($cmd);
24        return $this;
25    }
26}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Imagemagick/Effect/ImagemagickRotateEffect.php.html b/packages/image/build/report/Imagemagick/Effect/ImagemagickRotateEffect.php.html new file mode 100644 index 000000000..fa5b3b039 --- /dev/null +++ b/packages/image/build/report/Imagemagick/Effect/ImagemagickRotateEffect.php.html @@ -0,0 +1,178 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickRotateEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 80.00% covered (success) +
+
+
80.00%
4 / 5
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagemagickRotateEffect
+
+ 80.00% covered (success) +
+
+
80.00%
4 / 5
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2.03
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 process
+
+ 80.00% covered (success) +
+
+
80.00%
4 / 5
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2.03
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Imagemagick\Effect;
4
5use Charcoal\Image\Effect\AbstractRotateEffect;
6
7/**
8 * Rotate Effect for the Imagemagick driver.
9 */
10class ImagemagickRotateEffect extends AbstractRotateEffect
11{
12    /**
13     * @param array $data The effect data, if available.
14     */
15    public function process(?array $data = null): static
16    {
17        if ($data !== null) {
18            $this->setData($data);
19        }
20
21        $cmd = '-background "' . $this->backgroundColor() . '" -rotate ' . $this->angle();
22        $this->image()->applyCmd($cmd);
23        return $this;
24    }
25}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Imagemagick/Effect/ImagemagickSepiaEffect.php.html b/packages/image/build/report/Imagemagick/Effect/ImagemagickSepiaEffect.php.html new file mode 100644 index 000000000..9b363ae85 --- /dev/null +++ b/packages/image/build/report/Imagemagick/Effect/ImagemagickSepiaEffect.php.html @@ -0,0 +1,179 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickSepiaEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 83.33% covered (success) +
+
+
83.33%
5 / 6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagemagickSepiaEffect
+
+ 83.33% covered (success) +
+
+
83.33%
5 / 6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2.02
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 process
+
+ 83.33% covered (success) +
+
+
83.33%
5 / 6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2.02
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Imagemagick\Effect;
4
5use Charcoal\Image\Effect\AbstractSepiaEffect;
6
7/**
8 * Sepia Effect for the Imagemagick driver.
9 */
10class ImagemagickSepiaEffect extends AbstractSepiaEffect
11{
12    /**
13     * @param array $data The effect data, if available.
14     */
15    public function process(?array $data = null): static
16    {
17        if ($data !== null) {
18            $this->setData($data);
19        }
20
21        $value = (($this->threshold() / 255) * 100) . '%';
22        $cmd = '-sepia-tone ' . $value;
23        $this->image()->applyCmd($cmd);
24        return $this;
25    }
26}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Imagemagick/Effect/ImagemagickSharpenEffect.php.html b/packages/image/build/report/Imagemagick/Effect/ImagemagickSharpenEffect.php.html new file mode 100644 index 000000000..27c5fe1d3 --- /dev/null +++ b/packages/image/build/report/Imagemagick/Effect/ImagemagickSharpenEffect.php.html @@ -0,0 +1,241 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickSharpenEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 30.00% covered (danger) +
+
+
30.00%
6 / 20
+
+ 33.33% covered (danger) +
+
+
33.33%
1 / 3
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagemagickSharpenEffect
+
+ 30.00% covered (danger) +
+
+
30.00%
6 / 20
+
+ 33.33% covered (danger) +
+
+
33.33%
1 / 3
6.09
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 processAdaptive
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
 processUnsharp
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 8
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
 processStandard
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Imagemagick\Effect;
4
5use Charcoal\Image\Effect\AbstractSharpenEffect;
6
7/**
8 * Sharpen Effect for the Imagemagick driver.
9 */
10class ImagemagickSharpenEffect extends AbstractSharpenEffect
11{
12    public function processAdaptive(): static
13    {
14        $radius = $this->radius();
15        $sigma = $this->sigma();
16        $channel = $this->image()->convertChannel($this->channel());
17        $cmd = '-channel ' . $channel . ' -adaptive-sharpen ' . $radius . 'x' . $sigma;
18        $this->image()->applyCmd($cmd);
19        return $this;
20    }
21
22    public function processUnsharp(): static
23    {
24        $radius = $this->radius();
25        $sigma = $this->sigma();
26        $amount = $this->amount();
27        $threshold = $this->threshold();
28        $channel = $this->image()->convertChannel($this->channel());
29
30        $cmd = '-channel ' . $channel . ' -unsharp ' . $radius . 'x' . $sigma . '+' . $amount . '+' . $threshold;
31        $this->image()->applyCmd($cmd);
32        return $this;
33    }
34
35    public function processStandard(): static
36    {
37        $radius = $this->radius();
38        $sigma = $this->sigma();
39        $channel = $this->image()->convertChannel($this->channel());
40        $cmd = '-channel ' . $channel . ' -sharpen ' . $radius . 'x' . $sigma;
41        $this->image()->applyCmd($cmd);
42        return $this;
43    }
44}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Imagemagick/Effect/ImagemagickThresholdEffect.php.html b/packages/image/build/report/Imagemagick/Effect/ImagemagickThresholdEffect.php.html new file mode 100644 index 000000000..3d4a0a2c0 --- /dev/null +++ b/packages/image/build/report/Imagemagick/Effect/ImagemagickThresholdEffect.php.html @@ -0,0 +1,179 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickThresholdEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 83.33% covered (success) +
+
+
83.33%
5 / 6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagemagickThresholdEffect
+
+ 83.33% covered (success) +
+
+
83.33%
5 / 6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2.02
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 process
+
+ 83.33% covered (success) +
+
+
83.33%
5 / 6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2.02
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Imagemagick\Effect;
4
5use Charcoal\Image\Effect\AbstractThresholdEffect;
6
7/**
8 * Threshold Effect for the Imagemagick driver.
9 */
10class ImagemagickThresholdEffect extends AbstractThresholdEffect
11{
12    /**
13     * @param array $data The effect data, if available.
14     */
15    public function process(?array $data = null): static
16    {
17        if ($data !== null) {
18            $this->setData($data);
19        }
20
21        $value = ($this->threshold() * 100) . '%';
22        $cmd = '-threshold ' . $value;
23        $this->image()->applyCmd($cmd);
24        return $this;
25    }
26}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Imagemagick/Effect/ImagemagickTintEffect.php.html b/packages/image/build/report/Imagemagick/Effect/ImagemagickTintEffect.php.html new file mode 100644 index 000000000..f2c90bfbb --- /dev/null +++ b/packages/image/build/report/Imagemagick/Effect/ImagemagickTintEffect.php.html @@ -0,0 +1,181 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickTintEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 87.50% covered (success) +
+
+
87.50%
7 / 8
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagemagickTintEffect
+
+ 87.50% covered (success) +
+
+
87.50%
7 / 8
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
3.02
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 process
+
+ 87.50% covered (success) +
+
+
87.50%
7 / 8
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
3.02
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Imagemagick\Effect;
4
5use Charcoal\Image\Effect\AbstractTintEffect;
6
7/**
8 * Tint Effect for the Imagemagick driver.
9 */
10class ImagemagickTintEffect extends AbstractTintEffect
11{
12    /**
13     * @param array $data The effect data, if available.
14     */
15    public function process(?array $data = null): static
16    {
17        if ($data !== null) {
18            $this->setData($data);
19        }
20
21        $tintCmd = $this->midtone() === true ? '-tint' : '-colorize';
22        $color = $this->color();
23        $value = ($this->opacity() * 100) . '%';
24        $cmd = '-fill "' . $color . '" ' . $tintCmd . ' ' . $value;
25        $this->image()->applyCmd($cmd);
26        return $this;
27    }
28}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Imagemagick/Effect/ImagemagickWatermarkEffect.php.html b/packages/image/build/report/Imagemagick/Effect/ImagemagickWatermarkEffect.php.html new file mode 100644 index 000000000..102d9f8d8 --- /dev/null +++ b/packages/image/build/report/Imagemagick/Effect/ImagemagickWatermarkEffect.php.html @@ -0,0 +1,207 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickWatermarkEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 25
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagemagickWatermarkEffect
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 25
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
20
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 process
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 25
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
20
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Imagemagick\Effect;
4
5use Charcoal\Image\Effect\AbstractWatermarkEffect;
6use Charcoal\Image\ImageInterface;
7
8/**
9 * Watermark Effect for the Imagemagick driver.
10 */
11class ImagemagickWatermarkEffect extends AbstractWatermarkEffect
12{
13    /**
14     * @param array $data The effect data, if available.
15     */
16    public function process(?array $data = null): static
17    {
18        if ($data !== null) {
19            $this->setData($data);
20        }
21
22        if ($this->watermark() instanceof ImageInterface) {
23            $out = '/tmp/_' . uniqid() . '.png';
24            $this->watermark()->save($out);
25            $width = $this->watermark()->width();
26            $height = $this->watermark()->height();
27            $watermark = $out;
28        } else {
29            $watermark = $this->watermark();
30            $c = $this->image()::class;
31            $w = new $c();
32            $w->open($watermark);
33            $width = $w->width();
34            $height = $w->height();
35        }
36
37        $gravity = $this->image()->imagemagickGravity($this->gravity());
38        $params  = [ '-gravity ' . $gravity ];
39
40        $cmd = null;
41        if ($this->image()->compositeCmd()) {
42            $cmd = 'composite';
43            $params[] = '-watermark 100% ' . $watermark . ' ' . $this->image()->tmp();
44        } else {
45            $params[] = '-geometry +' . $this->x() . '+' . $this->y();
46            $params[] = '-draw "image Multiply 0,0 ' . $width . ',' . $height;
47            $params[] = '\'' . $watermark . '\'"';
48        }
49
50        $this->image()->applyCmd(implode(' ', $params), $cmd);
51
52        return $this;
53    }
54}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Imagemagick/Effect/dashboard.html b/packages/image/build/report/Imagemagick/Effect/dashboard.html new file mode 100644 index 000000000..9dfdaab38 --- /dev/null +++ b/packages/image/build/report/Imagemagick/Effect/dashboard.html @@ -0,0 +1,339 @@ + + + + + Dashboard for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect + + + + + + + +
+
+ +
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+ +
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + + + + + + + + + + + + +
MethodCoverage
process0%
processSoft0%
process0%
doCrop0%
process0%
process0%
process0%
processAdaptive0%
processUnsharp0%
process0%
process75%
+
+
+
+

Project Risks

+
+ + + + + + + + + + + + + + + + + + + + +
MethodCoverageComplexityCRAP
doCrop0.0%530
process0.0%420
process0.0%312
process0.0%26
process0.0%26
process0.0%26
process0.0%26
process75.0%22
+
+
+
+ +
+ + + + + + diff --git a/packages/image/build/report/Imagemagick/Effect/index.html b/packages/image/build/report/Imagemagick/Effect/index.html new file mode 100644 index 000000000..fb4ab376b --- /dev/null +++ b/packages/image/build/report/Imagemagick/Effect/index.html @@ -0,0 +1,596 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect + + + + + + + +
+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 50.87% covered (warning) +
+
+
50.87%
88 / 173
+
+ 24.00% covered (danger) +
+
+
24.00%
6 / 25
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 18
ImagemagickAutoorientationEffect.php
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagemagickBlurEffect.php
+
+ 86.96% covered (success) +
+
+
86.96%
20 / 23
+
+ 83.33% covered (success) +
+
+
83.33%
5 / 6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagemagickCompressionEffect.php
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 5
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagemagickCropEffect.php
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 10
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagemagickDitherEffect.php
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 5
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagemagickFormatEffect.php
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagemagickGrayscaleEffect.php
+
+ 75.00% covered (warning) +
+
+
75.00%
3 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagemagickMaskEffect.php
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 5
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagemagickMirrorEffect.php
+
+ 83.33% covered (success) +
+
+
83.33%
5 / 6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagemagickModulateEffect.php
+
+ 87.50% covered (success) +
+
+
87.50%
7 / 8
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagemagickResizeEffect.php
+
+ 91.30% covered (success) +
+
+
91.30%
21 / 23
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagemagickRevertEffect.php
+
+ 83.33% covered (success) +
+
+
83.33%
5 / 6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagemagickRotateEffect.php
+
+ 80.00% covered (success) +
+
+
80.00%
4 / 5
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagemagickSepiaEffect.php
+
+ 83.33% covered (success) +
+
+
83.33%
5 / 6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagemagickSharpenEffect.php
+
+ 30.00% covered (danger) +
+
+
30.00%
6 / 20
+
+ 33.33% covered (danger) +
+
+
33.33%
1 / 3
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagemagickThresholdEffect.php
+
+ 83.33% covered (success) +
+
+
83.33%
5 / 6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagemagickTintEffect.php
+
+ 87.50% covered (success) +
+
+
87.50%
7 / 8
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagemagickWatermarkEffect.php
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 25
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ +
+ + diff --git a/packages/image/build/report/Imagemagick/ImagemagickImage.php.html b/packages/image/build/report/Imagemagick/ImagemagickImage.php.html new file mode 100644 index 000000000..2c1713bd9 --- /dev/null +++ b/packages/image/build/report/Imagemagick/ImagemagickImage.php.html @@ -0,0 +1,1036 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/ImagemagickImage.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 52.83% covered (warning) +
+
+
52.83%
84 / 159
+
+ 47.62% covered (warning) +
+
+
47.62%
10 / 21
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagemagickImage
+
+ 52.83% covered (warning) +
+
+
52.83%
84 / 159
+
+ 47.62% covered (warning) +
+
+
47.62%
10 / 21
451.53
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 __construct
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 __destruct
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 driverType
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 create
+
+ 66.67% covered (warning) +
+
+
66.67%
8 / 12
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
5.93
 open
+
+ 100.00% covered (success) +
+
+
100.00%
12 / 12
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
6
 save
+
+ 45.45% covered (warning) +
+
+
45.45%
5 / 11
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
9.06
 width
+
+ 75.00% covered (warning) +
+
+
75.00%
3 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2.06
 height
+
+ 75.00% covered (warning) +
+
+
75.00%
3 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2.06
 convertChannel
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 findCmd
+
+ 33.33% covered (danger) +
+
+
33.33%
8 / 24
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
26.96
 availableCommands
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 cmd
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 19
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
72
 mogrifyCmd
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
 convertCmd
+
+ 75.00% covered (warning) +
+
+
75.00%
3 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2.06
 compositeCmd
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
6
 identifyCmd
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
 tmp
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
 resetTmp
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
 exec
+
+ 80.00% covered (success) +
+
+
80.00%
16 / 20
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
3.07
 applyCmd
+
+ 57.14% covered (warning) +
+
+
57.14%
4 / 7
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
3.71
 imagemagickGravity
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 16
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
6
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Imagemagick;
4
5use Exception;
6use InvalidArgumentException;
7use OutOfBoundsException;
8use Charcoal\Image\AbstractImage;
9
10/**
11 * The Imagemagick image driver.
12 *
13 * Run from the binary imagemagick scripts.
14 * (`mogrify`, `convert` and `identify`)
15 */
16class ImagemagickImage extends AbstractImage
17{
18    /**
19     * The temporary file location
20     */
21    private ?string $tmpFile = null;
22
23    /**
24     * @var string $mogrifyCmd
25     */
26    private $mogrifyCmd;
27
28    /**
29     * @var string $convertCmd
30     */
31    private $convertCmd;
32
33    /**
34     * @var string $compositeCmd
35     */
36    private $compositeCmd;
37
38    /**
39     * @var string $identifyCmd
40     */
41    private $identifyCmd;
42
43    /**
44     * Set up the commands.
45     */
46    public function __construct()
47    {
48        // This will throw exception if the binaris are not found.
49        $this->mogrifyCmd = $this->mogrifyCmd();
50        $this->convertCmd = $this->convertCmd();
51    }
52
53    /**
54     * Clean up the tmp file, if necessary
55     */
56    public function __destruct()
57    {
58        $this->resetTmp();
59    }
60
61    public function driverType(): string
62    {
63        return 'imagemagick';
64    }
65
66    /**
67     * Create a blank canvas of a given size, with a given background color.
68     *
69     * @param integer $width  Image size, in pixels.
70     * @param integer $height Image height, in pixels.
71     * @param string  $color  Default to transparent.
72     * @throws InvalidArgumentException If the size arguments are not valid.
73     */
74    public function create($width, $height, $color = 'rgb(100%, 100%, 100%, 0)'): static
75    {
76        if (!is_numeric($width) || $width < 1) {
77            throw new InvalidArgumentException(
78                'Width must be an integer of at least 1 pixel'
79            );
80        }
81        if (!is_numeric($height) || $height < 1) {
82            throw new InvalidArgumentException(
83                'Height must be an integer of at least 1 pixel'
84            );
85        }
86
87        $this->resetTmp();
88        touch($this->tmp());
89        $this->exec($this->convertCmd() . ' -size ' . (int)$width . 'x' . (int)$height . ' canvas:"' . $color . '" ' . $this->tmp());
90        return $this;
91    }
92
93    /**
94     * Open an image file
95     *
96     * @param string $source The source path / filename.
97     * @throws Exception If the source file does not exist.
98     * @throws InvalidArgumentException If the source argument is not a string.
99     */
100    public function open($source = null): static
101    {
102        if ($source !== null && !is_string($source)) {
103            throw new InvalidArgumentException(
104                'Source must be a string (file path)'
105            );
106        }
107
108        $source = $source ?: $this->source();
109        $this->resetTmp();
110        if (!file_exists($source) && null === parse_url($source, PHP_URL_HOST)) {
111            throw new Exception(
112                sprintf('File "%s" does not exist', $source)
113            );
114        }
115
116        copy($source, $this->tmp());
117
118        return $this;
119    }
120
121    /**
122     * Save an image to a target.
123     * If no target is set, the original source will be owerwritten
124     *
125     * @param string $target The target path / filename.
126     * @throws Exception If the target file does not exist or is not writeable.
127     * @throws InvalidArgumentException If the target argument is not a string.
128     */
129    public function save($target = null): static
130    {
131        if ($target !== null && !is_string($target)) {
132            throw new InvalidArgumentException(
133                'Target must be a string (file path)'
134            );
135        }
136
137        $target = $target ?: $this->target();
138        if (!is_writable(dirname($target))) {
139            throw new Exception(
140                sprintf('Target "%s" is not writable', $target)
141            );
142        }
143
144        copy($this->tmp(), $target);
145
146        return $this;
147    }
148
149    /**
150     * Get the image's width, in pixels
151     */
152    public function width(): int
153    {
154        if (!file_exists($this->tmp())) {
155            return 0;
156        }
157        $cmd = $this->identifyCmd() . ' -format "%w" ' . $this->tmp();
158        return (int)trim($this->exec($cmd));
159    }
160
161    /**
162     * Get the image's height, in pixels
163     */
164    public function height(): int
165    {
166        if (!file_exists($this->tmp())) {
167            return 0;
168        }
169        $cmd = $this->identifyCmd() . ' -format "%h" ' . $this->tmp();
170        return (int)trim($this->exec($cmd));
171    }
172
173    /**
174     * @param string $channel The channel name to convert.
175     */
176    public function convertChannel($channel): string
177    {
178        return ucfirst($channel);
179    }
180
181    /**
182     * Find a command.
183     *
184     * Try (as best as possible) to find a command name:
185     *
186     * - With `type -p`
187     * - Or else, with `where`
188     * - Or else, with `which`
189     *
190     * @param  string $cmdName The command name to find.
191     * @throws InvalidArgumentException If the given command name is not a string.
192     * @throws OutOfBoundsException If the command is unsupported or can not be found.
193     * @return string
194     */
195    protected function findCmd($cmdName)
196    {
197        if (!is_string($cmdName)) {
198            throw new InvalidArgumentException(sprintf(
199                'Target image must be a string, received %s',
200                (get_debug_type($cmdName))
201            ));
202        }
203
204        if (!in_array($cmdName, $this->availableCommands())) {
205            if (!is_string($cmdName)) {
206                $cmdName = (get_debug_type($cmdName));
207            }
208            throw new OutOfBoundsException(sprintf(
209                'Unsupported command "%s" provided',
210                $cmdName
211            ));
212        }
213
214        $cmd = exec('type -p ' . $cmdName);
215        $cmd = str_replace($cmdName . ' is ', '', $cmd);
216
217        if ($cmd === '' || $cmd === '0') {
218            $cmd = exec('where ' . $cmdName);
219        }
220
221        if (!$cmd) {
222            $cmd = exec('which ' . $cmdName);
223        }
224
225        if (!$cmd) {
226            throw new OutOfBoundsException(sprintf(
227                'Can not find ImageMagick\'s "%s" command.',
228                $cmdName
229            ));
230        }
231
232        return $cmd;
233    }
234
235    /**
236     * Retrieve the list of available commands.
237     */
238    public function availableCommands(): array
239    {
240        return [ 'mogrify', 'convert', 'composite', 'identify' ];
241    }
242
243    /**
244     * Retrieve the list of available commands.
245     *
246     * @param  string $name The name of an available command (mogrify, convert, composite or identify).
247     * @throws InvalidArgumentException If the name is not a string.
248     * @throws OutOfBoundsException If the name is unsupported.
249     * @return string
250     */
251    public function cmd($name)
252    {
253        if (!is_string($name)) {
254            throw new InvalidArgumentException(sprintf(
255                'Command name must be a string, received %s',
256                (get_debug_type($name))
257            ));
258        }
259
260        switch ($name) {
261            case 'mogrify':
262                return $this->mogrifyCmd();
263
264            case 'convert':
265                return $this->convertCmd();
266
267            case 'composite':
268                return $this->compositeCmd();
269
270            case 'identify':
271                return $this->identifyCmd();
272
273            default:
274                if (!is_string($name)) {
275                    $name = (get_debug_type($name));
276                }
277                throw new OutOfBoundsException(sprintf(
278                    'Unsupported command "%s" provided',
279                    $name
280                ));
281        }
282    }
283
284    /**
285     * @return string The full path of the mogrify command.
286     */
287    public function mogrifyCmd()
288    {
289        if ($this->mogrifyCmd !== null) {
290            return $this->mogrifyCmd;
291        }
292        $this->mogrifyCmd = $this->findCmd('mogrify');
293        return $this->mogrifyCmd;
294    }
295
296    /**
297     * @return string The full path of the convert command.
298     */
299    public function convertCmd()
300    {
301        if ($this->convertCmd !== null) {
302            return $this->convertCmd;
303        }
304        $this->convertCmd = $this->findCmd('convert');
305        return $this->convertCmd;
306    }
307
308    /**
309     * @return string The full path of the composite command.
310     */
311    public function compositeCmd()
312    {
313        if ($this->compositeCmd !== null) {
314            return $this->compositeCmd;
315        }
316        $this->compositeCmd = $this->findCmd('composite');
317        return $this->compositeCmd;
318    }
319
320    /**
321     * @return string The full path of the identify comand.
322     */
323    public function identifyCmd()
324    {
325        if ($this->identifyCmd !== null) {
326            return $this->identifyCmd;
327        }
328        $this->identifyCmd = $this->findCmd('identify');
329        return $this->identifyCmd;
330    }
331
332    /**
333     * Generate a temporary file, to apply effects on.
334     */
335    public function tmp(): string
336    {
337        if ($this->tmpFile !== null) {
338            return $this->tmpFile;
339        }
340
341        $this->tmpFile = rtrim(sys_get_temp_dir(), '/\\') . DIRECTORY_SEPARATOR . 'charcoal_image_' . uniqid();
342        return $this->tmpFile;
343    }
344
345    /**
346     * @return ImagemagickImage Chainable
347     */
348    public function resetTmp(): static
349    {
350        if (file_exists($this->tmpFile)) {
351            unlink($this->tmpFile);
352        }
353        $this->tmpFile = null;
354        return $this;
355    }
356
357    /**
358     * Exec a command, either with `proc_open()` or `shell_exec()`
359     *
360     * The `proc_open()` method is preferred, as it allows to catch errors in
361     * the STDERR buffer (and throw Exception) but it might be disabled in some
362     * systems for security reasons.
363     *
364     * @param string $cmd The command to execute.
365     * @throws Exception If the command fails.
366     * @return string
367     */
368    public function exec(string $cmd): string|false|null
369    {
370        if (function_exists('proc_open')) {
371            $proc = proc_open(
372                $cmd,
373                [
374                    1 => ['pipe','w'],
375                    2 => ['pipe','w'],
376                ],
377                $pipes
378            );
379            $out = stream_get_contents($pipes[1]);
380            fclose($pipes[1]);
381            $err = stream_get_contents($pipes[2]);
382            fclose($pipes[2]);
383            proc_close($proc);
384
385            if ($err) {
386                throw new Exception(
387                    sprintf('Error executing command "%s": %s', $cmd, $err)
388                );
389            }
390
391            return $out;
392        } else {
393            return shell_exec($cmd);
394        }
395    }
396
397    /**
398     * @param  string      $params The $cmd's arguments and options.
399     * @param  string|null $cmd    The command to run.
400     * @throws Exception If the tmp file was not properly set.
401     * @return ImagemagickImage Chainable
402     */
403    public function applyCmd(string $params, $cmd = null): static
404    {
405        $cmd = $cmd === null ? $this->mogrifyCmd() : $this->cmd($cmd);
406
407        if (!file_exists($this->tmp())) {
408            throw new Exception(
409                'No file currently set as tmp file, commands can not be executed.'
410            );
411        }
412
413        $this->exec($cmd . ' ' . $params . ' ' . $this->tmp());
414
415        return $this;
416    }
417
418    /**
419     * Convert a gravity name (string) to an `Imagick::GRAVITY_*` constant (integer)
420     *
421     * @param string $gravity The standard gravity name.
422     * @throws InvalidArgumentException If the gravity argument is not a valid gravity.
423     */
424    public function imagemagickGravity($gravity): string
425    {
426        $gravityMap = [
427            'center'    => 'center',
428            'n'         => 'north',
429            's'         => 'south',
430            'e'         => 'east',
431            'w'         => 'west',
432            'ne'        => 'northeast',
433            'nw'        => 'northwest',
434            'se'        => 'southeast',
435            'sw'        => 'southwest'
436        ];
437        if (!isset($gravityMap[$gravity])) {
438            throw new InvalidArgumentException(
439                'Invalid gravity. Possible values are: center, n, s, e, w, ne, nw, se, sw.'
440            );
441        }
442        return $gravityMap[$gravity];
443    }
444}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Imagemagick/dashboard.html b/packages/image/build/report/Imagemagick/dashboard.html new file mode 100644 index 000000000..ede942db8 --- /dev/null +++ b/packages/image/build/report/Imagemagick/dashboard.html @@ -0,0 +1,360 @@ + + + + + Dashboard for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick + + + + + + + +
+
+ +
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+ +
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MethodCoverage
process0%
processSoft0%
process0%
doCrop0%
process0%
process0%
process0%
processAdaptive0%
processUnsharp0%
process0%
cmd0%
compositeCmd0%
imagemagickGravity0%
findCmd33%
save45%
applyCmd57%
create66%
process75%
width75%
height75%
convertCmd75%
+
+
+
+

Project Risks

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MethodCoverageComplexityCRAP
cmd0.0%872
doCrop0.0%530
findCmd33.3%826
process0.0%420
process0.0%312
save45.5%59
process0.0%26
process0.0%26
process0.0%26
process0.0%26
compositeCmd0.0%26
imagemagickGravity0.0%26
create66.7%55
applyCmd57.1%33
process75.0%22
width75.0%22
height75.0%22
convertCmd75.0%22
+
+
+
+ +
+ + + + + + diff --git a/packages/image/build/report/Imagemagick/index.html b/packages/image/build/report/Imagemagick/index.html new file mode 100644 index 000000000..08c7411ba --- /dev/null +++ b/packages/image/build/report/Imagemagick/index.html @@ -0,0 +1,147 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick + + + + + + + +
+
+
+ +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 51.81% covered (warning) +
+
+
51.81%
172 / 332
+
+ 34.78% covered (danger) +
+
+
34.78%
16 / 46
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 19
Effect
+
+ 50.87% covered (warning) +
+
+
50.87%
88 / 173
+
+ 24.00% covered (danger) +
+
+
24.00%
6 / 25
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 18
ImagemagickImage.php
+
+ 52.83% covered (warning) +
+
+
52.83%
84 / 159
+
+ 47.62% covered (warning) +
+
+
47.62%
10 / 21
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ +
+ + diff --git a/packages/image/build/report/Imagick/Effect/ImagickAutoorientationEffect.php.html b/packages/image/build/report/Imagick/Effect/ImagickAutoorientationEffect.php.html new file mode 100644 index 000000000..88f4f376a --- /dev/null +++ b/packages/image/build/report/Imagick/Effect/ImagickAutoorientationEffect.php.html @@ -0,0 +1,212 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickAutoorientationEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 32
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagickAutoorientationEffect
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 32
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
132
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 process
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 32
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
132
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Imagick\Effect;
4
5use Imagick;
6use Charcoal\Image\Effect\AbstractAutoorientationEffect;
7
8/**
9 * Auto-orientation Effect for the Imagick driver.
10 */
11class ImagickAutoorientationEffect extends AbstractAutoorientationEffect
12{
13    /**
14     * @param array $data The effect data, if available.
15     * @return ImagickAutoorientationEffect Chainable
16     */
17    public function process(?array $data = null): static
18    {
19        if ($data !== null) {
20            $this->setData($data);
21        }
22
23        switch ($this->image()->imagick()->getImageOrientation()) {
24            case Imagick::ORIENTATION_TOPLEFT:
25                break;
26            case Imagick::ORIENTATION_TOPRIGHT:
27                $this->image()->imagick()->flopImage();
28                break;
29            case Imagick::ORIENTATION_BOTTOMRIGHT:
30                $this->image()->imagick()->rotateImage('#000', 180);
31                break;
32            case Imagick::ORIENTATION_BOTTOMLEFT:
33                $this->image()->imagick()->flopImage();
34                $this->image()->imagick()->rotateImage('#000', 180);
35                break;
36            case Imagick::ORIENTATION_LEFTTOP:
37                $this->image()->imagick()->flopImage();
38                $this->image()->imagick()->rotateImage('#000', -90);
39                break;
40            case Imagick::ORIENTATION_RIGHTTOP:
41                $this->image()->imagick()->rotateImage('#000', 90);
42                break;
43            case Imagick::ORIENTATION_RIGHTBOTTOM:
44                $this->image()->imagick()->flopImage();
45                $this->image()->imagick()->rotateImage('#000', 90);
46                break;
47            case Imagick::ORIENTATION_LEFTBOTTOM:
48                $this->image()->imagick()->rotateImage('#000', -90);
49                break;
50            default:
51                // Invalid orientation
52                break;
53        }
54
55        $this->image()->imagick()->setImageOrientation(Imagick::ORIENTATION_TOPLEFT);
56
57        return $this;
58    }
59}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Imagick/Effect/ImagickBlurEffect.php.html b/packages/image/build/report/Imagick/Effect/ImagickBlurEffect.php.html new file mode 100644 index 000000000..5779e5777 --- /dev/null +++ b/packages/image/build/report/Imagick/Effect/ImagickBlurEffect.php.html @@ -0,0 +1,336 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickBlurEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 84.21% covered (success) +
+
+
84.21%
16 / 19
+
+ 83.33% covered (success) +
+
+
83.33%
5 / 6
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagickBlurEffect
+
+ 84.21% covered (success) +
+
+
84.21%
16 / 19
+
+ 83.33% covered (success) +
+
+
83.33%
5 / 6
6.14
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 processAdaptive
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 processGaussian
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 processMotion
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 processRadial
+
+ 100.00% covered (success) +
+
+
100.00%
4 / 4
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 processSoft
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 3
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
 processStandard
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Imagick\Effect;
4
5use Exception;
6use Charcoal\Image\Effect\AbstractBlurEffect;
7
8/**
9 * Blur Effect for the Imagick driver.
10 */
11class ImagickBlurEffect extends AbstractBlurEffect
12{
13    /**
14     * @return ImagickBlurEffect Chainable
15     */
16    public function processAdaptive(): static
17    {
18        $channel = $this->image()->imagickChannel($this->channel());
19        $this->image()->imagick()->adaptiveBlurImage($this->radius(), $this->sigma(), $channel);
20        return $this;
21    }
22
23    /**
24     * @return ImagickBlurEffect Chainable
25     */
26    public function processGaussian(): static
27    {
28        $channel = $this->image()->imagickChannel($this->channel());
29        $this->image()->imagick()->gaussianBlurImage($this->radius(), $this->sigma(), $channel);
30        return $this;
31    }
32
33    /**
34     * @return ImagickBlurEffect Chainable
35     */
36    public function processMotion(): static
37    {
38        $channel = $this->image()->imagickChannel($this->channel());
39        $this->image()->imagick()->motionBlurImage($this->radius(), $this->sigma(), $this->angle(), $channel);
40        return $this;
41    }
42
43    /**
44     * @return ImagickBlurEffect Chainable
45     */
46    public function processRadial(): static
47    {
48        $angle = $this->angle();
49        $channel = $this->image()->imagickChannel($this->channel());
50        $this->image()->imagick()->rotationalBlurImage(($angle), $channel);
51        return $this;
52    }
53
54    /**
55     * @throws Exception This method is not yet supported on Imagick.
56     */
57    public function processSoft(): never
58    {
59        throw new Exception(
60            'Soft blur is not (yet) supported with imagick driver.'
61        );
62    }
63
64    /**
65     * @return ImagickBlurEffect Chainable
66     */
67    public function processStandard(): static
68    {
69        $channel = $this->image()->imagickChannel($this->channel());
70        $this->image()->imagick()->blurImage($this->radius(), $this->sigma(), $channel);
71        return $this;
72    }
73}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Imagick/Effect/ImagickCompressionEffect.php.html b/packages/image/build/report/Imagick/Effect/ImagickCompressionEffect.php.html new file mode 100644 index 000000000..f9b4fe684 --- /dev/null +++ b/packages/image/build/report/Imagick/Effect/ImagickCompressionEffect.php.html @@ -0,0 +1,209 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickCompressionEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 17
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagickCompressionEffect
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 17
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
56
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 process
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 17
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
56
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Imagick\Effect;
4
5use Charcoal\Image\Effect\AbstractCompressionEffect;
6
7/**
8 * Compression Effect for the Imagick driver.
9 */
10class ImagickCompressionEffect extends AbstractCompressionEffect
11{
12    /**
13     * @param array $data The effect data, if available.
14     */
15    public function process(?array $data = null): static
16    {
17        if ($data !== null) {
18            $this->setData($data);
19        }
20
21        $this->image()->target();
22        $extension = strtolower((string)$this->image()->imagick()->getImageFormat());
23
24        $invalidExtensions = [ 'gif', 'bmp' ];
25
26
27        // Abort when facing invalid extensions
28        if (in_array($extension, $invalidExtensions)) {
29            return $this;
30        }
31
32        // PNG compression is... different.
33        // 0 is for the best quality, 9 for the worst quality
34        // So we calculate the opposite quality (100 - quality) and extra that percent from 9
35        // If quality = 100, changes to 0
36        // Quality is rounded (ceil) to avoid any compression problems
37        if ($extension === 'png') {
38            $reverse = (100 - $this->quality());
39            if ($reverse > 0) {
40                $reverse = min(ceil(($reverse / 100) * 9), 9); // Make sure it doesn't get further than 9
41            }
42
43            if ($reverse <= 9) {
44                $this->image()->imagick()->setOption('png:compression-level', $reverse);
45                $this->image()->imagick()->setOption('png:compression-filter', '5');
46            }
47        }
48
49        // Default image compression applies to jpg, jpeg, webp and tiff
50        if (in_array($extension, ['jpg', 'jpeg', 'webp', 'tiff'])) {
51            $this->image()->imagick()->setImageCompressionQuality($this->quality());
52        }
53
54        return $this;
55    }
56}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Imagick/Effect/ImagickCropEffect.php.html b/packages/image/build/report/Imagick/Effect/ImagickCropEffect.php.html new file mode 100644 index 000000000..5ceefde4d --- /dev/null +++ b/packages/image/build/report/Imagick/Effect/ImagickCropEffect.php.html @@ -0,0 +1,220 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickCropEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 36
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagickCropEffect
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 36
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
110
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 doCrop
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 36
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
110
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Imagick\Effect;
4
5use Imagick;
6use Charcoal\Image\Effect\AbstractCropEffect;
7
8/**
9 * Resize Effect for the Imagick driver
10 */
11class ImagickCropEffect extends AbstractCropEffect
12{
13    /**
14     * @param integer $width  The crop width.
15     * @param integer $height The crop height.
16     * @param integer $x      The x-position (in pixel) of the crop.
17     * @param integer $y      The y-position (in pixel) of the crop.
18     * @return void
19     */
20    protected function doCrop($width, $height, $x, $y)
21    {
22        $gravity = $this->image()->imagickGravity($this->gravity());
23
24        // This sets the gravity for the rest of the chain
25        $this->image()->imagick()->setGravity($gravity);
26
27        // Apply gravity to crop coordinates
28
29        $imageWidth  = $this->image()->width();
30        $imageHeight = $this->image()->height();
31
32        switch ($this->image()->imagick()->getGravity()) {
33            case Imagick::GRAVITY_NORTHWEST:
34                break;
35            case Imagick::GRAVITY_NORTH:
36                $x = ($imageWidth / 2 - $width / 2);
37                break;
38            case Imagick::GRAVITY_NORTHEAST:
39                $x = ($imageWidth - $width);
40                break;
41            case Imagick::GRAVITY_WEST:
42                $y = ($imageHeight / 2 - $height / 2);
43                break;
44            case Imagick::GRAVITY_CENTER:
45                $x = ($imageWidth / 2 - $width / 2);
46                $y = ($imageHeight / 2 - $height / 2);
47                break;
48            case Imagick::GRAVITY_EAST:
49                $x = ($imageWidth - $width);
50                $y = ($imageHeight / 2 - $height / 2);
51                break;
52            case Imagick::GRAVITY_SOUTHWEST:
53                $y = ($imageHeight - $height);
54                break;
55            case Imagick::GRAVITY_SOUTH:
56                $x = ($imageWidth / 2 - $width / 2);
57                $y = ($imageHeight - $height);
58                break;
59            case Imagick::GRAVITY_SOUTHEAST:
60                $x = ($imageWidth - $width);
61                $y = ($imageHeight - $height);
62                break;
63        }
64
65        $this->image()->imagick()->cropImage($width, $height, $x, $y);
66    }
67}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Imagick/Effect/ImagickDitherEffect.php.html b/packages/image/build/report/Imagick/Effect/ImagickDitherEffect.php.html new file mode 100644 index 000000000..7da426f05 --- /dev/null +++ b/packages/image/build/report/Imagick/Effect/ImagickDitherEffect.php.html @@ -0,0 +1,190 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickDitherEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 81.82% covered (success) +
+
+
81.82%
9 / 11
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagickDitherEffect
+
+ 81.82% covered (success) +
+
+
81.82%
9 / 11
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
3.05
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 process
+
+ 81.82% covered (success) +
+
+
81.82%
9 / 11
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
3.05
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Imagick\Effect;
4
5use Imagick;
6use Charcoal\Image\Effect\AbstractDitherEffect;
7
8/**
9 * Dither Effect for the Imagick driver.
10 */
11class ImagickDitherEffect extends AbstractDitherEffect
12{
13    /**
14     * @param array $data The effect data, if available.
15     * @return ImagickDitherEffect Chainable
16     */
17    public function process(?array $data = null): static
18    {
19        if ($data !== null) {
20            $this->setData($data);
21        }
22
23        $mode = $this->mode();
24        if ($mode === '') {
25            $colorspace = $this->image()->imagick()->getColorSpace();
26            $tree_depth = 0;
27            $dither = true;
28            $this->image()->imagick()->quantizeImage($this->colors(), $colorspace, $tree_depth, $dither, false);
29        } else {
30            $this->image()->imagick()->orderedPosterizeImage($mode);
31        }
32
33        $this->image()->imagick()->setImageColorSpace(Imagick::COLORSPACE_GRAY);
34
35        return $this;
36    }
37}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Imagick/Effect/ImagickFormatEffect.php.html b/packages/image/build/report/Imagick/Effect/ImagickFormatEffect.php.html new file mode 100644 index 000000000..0e4ecd3cc --- /dev/null +++ b/packages/image/build/report/Imagick/Effect/ImagickFormatEffect.php.html @@ -0,0 +1,178 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickFormatEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 3
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagickFormatEffect
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 3
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 process
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 3
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
6
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Imagick\Effect;
4
5use Exception;
6use Charcoal\Image\Effect\AbstractFormatEffect;
7
8/**
9 * Format Effect for the Imagick driver.
10 */
11class ImagickFormatEffect extends AbstractFormatEffect
12{
13    /**
14     * @param array $data The effect data, if available.
15     * @return ImageFormatEffect Chainable
16     */
17    public function process(?array $data = null): static
18    {
19        if ($this->format()) {
20            $this->image()->imagick()->setimageFormat($this->format());
21        }
22
23        return $this;
24    }
25}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Imagick/Effect/ImagickGrayscaleEffect.php.html b/packages/image/build/report/Imagick/Effect/ImagickGrayscaleEffect.php.html new file mode 100644 index 000000000..4a21cd4db --- /dev/null +++ b/packages/image/build/report/Imagick/Effect/ImagickGrayscaleEffect.php.html @@ -0,0 +1,180 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickGrayscaleEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 75.00% covered (warning) +
+
+
75.00%
3 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagickGrayscaleEffect
+
+ 75.00% covered (warning) +
+
+
75.00%
3 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2.06
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 process
+
+ 75.00% covered (warning) +
+
+
75.00%
3 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2.06
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Imagick\Effect;
4
5use Imagick as Imagick;
6use Charcoal\Image\Effect\AbstractGrayscaleEffect;
7
8/**
9 * Grayscale Effect for the Imagick driver.
10 */
11class ImagickGrayscaleEffect extends AbstractGrayscaleEffect
12{
13    /**
14     * @param array $data The effect data, if available.
15     * @return ImagickGrayscaleEffect Chainable
16     */
17    public function process(?array $data = null): static
18    {
19        if ($data !== null) {
20            $this->setData($data);
21        }
22
23        $this->image()->imagick()->transformImageColorSpace(Imagick::COLORSPACE_GRAY);
24
25        return $this;
26    }
27}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Imagick/Effect/ImagickMaskEffect.php.html b/packages/image/build/report/Imagick/Effect/ImagickMaskEffect.php.html new file mode 100644 index 000000000..53b4c8fa4 --- /dev/null +++ b/packages/image/build/report/Imagick/Effect/ImagickMaskEffect.php.html @@ -0,0 +1,182 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickMaskEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 5
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagickMaskEffect
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 5
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 process
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 5
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
6
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image\Imagick\Effect;
6
7use Exception;
8use Charcoal\Image\Effect\AbstractMaskEffect;
9
10/**
11 * Mask Effect for the Imagick driver.
12 */
13class ImagickMaskEffect extends AbstractMaskEffect
14{
15    /**
16     * @param array $data The effect data, if available.
17     * @throws Exception This effect is not yet supported for Imagick driver.
18     */
19    public function process(?array $data = null): void
20    {
21        if ($data !== null) {
22            $this->setData($data);
23        }
24
25        throw new Exception(
26            'Mask effect is not (yet) supported with imagick driver.'
27        );
28    }
29}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Imagick/Effect/ImagickMirrorEffect.php.html b/packages/image/build/report/Imagick/Effect/ImagickMirrorEffect.php.html new file mode 100644 index 000000000..70c1ead27 --- /dev/null +++ b/packages/image/build/report/Imagick/Effect/ImagickMirrorEffect.php.html @@ -0,0 +1,183 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickMirrorEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 85.71% covered (success) +
+
+
85.71%
6 / 7
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagickMirrorEffect
+
+ 85.71% covered (success) +
+
+
85.71%
6 / 7
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
3.03
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 process
+
+ 85.71% covered (success) +
+
+
85.71%
6 / 7
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
3.03
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Imagick\Effect;
4
5use Charcoal\Image\Effect\AbstractMirrorEffect;
6
7/**
8 * Mirror Effect for the Imagick driver.
9 */
10class ImagickMirrorEffect extends AbstractMirrorEffect
11{
12    /**
13     * @param array $data The effect data, if available.
14     */
15    public function process(?array $data = null): static
16    {
17        if ($data !== null) {
18            $this->setData($data);
19        }
20
21        $axis = $this->axis();
22        if ($axis == 'x') {
23            // Vertical mirror
24            $this->image()->imagick()->flipImage();
25        } else {
26            $this->image()->imagick()->flopImage();
27        }
28        return $this;
29    }
30}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Imagick/Effect/ImagickModulateEffect.php.html b/packages/image/build/report/Imagick/Effect/ImagickModulateEffect.php.html new file mode 100644 index 000000000..03660a5e9 --- /dev/null +++ b/packages/image/build/report/Imagick/Effect/ImagickModulateEffect.php.html @@ -0,0 +1,183 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickModulateEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 85.71% covered (success) +
+
+
85.71%
6 / 7
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagickModulateEffect
+
+ 85.71% covered (success) +
+
+
85.71%
6 / 7
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2.01
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 process
+
+ 85.71% covered (success) +
+
+
85.71%
6 / 7
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2.01
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Imagick\Effect;
4
5use Charcoal\Image\Effect\AbstractModulateEffect;
6
7/**
8 * Module Effect for the Imagick driver.
9 */
10class ImagickModulateEffect extends AbstractModulateEffect
11{
12    /**
13     * @param array $data The effect data, if available.
14     * @return AbstractModulateEffect Chainable
15     */
16    public function process(?array $data = null): static
17    {
18        if ($data !== null) {
19            $this->setData($data);
20        }
21
22        $h = ($this->hue() + 100);
23        $s = ($this->saturation() + 100);
24        $l = ($this->luminance() + 100);
25
26        $this->image()->imagick()->modulateImage($l, $s, $h);
27
28        return $this;
29    }
30}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Imagick/Effect/ImagickResizeEffect.php.html b/packages/image/build/report/Imagick/Effect/ImagickResizeEffect.php.html new file mode 100644 index 000000000..35dbcbbbb --- /dev/null +++ b/packages/image/build/report/Imagick/Effect/ImagickResizeEffect.php.html @@ -0,0 +1,180 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickResizeEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 66.67% covered (warning) +
+
+
66.67%
2 / 3
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagickResizeEffect
+
+ 66.67% covered (warning) +
+
+
66.67%
2 / 3
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2.15
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 doResize
+
+ 66.67% covered (warning) +
+
+
66.67%
2 / 3
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2.15
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Imagick\Effect;
4
5use Imagick;
6use Charcoal\Image\Effect\AbstractResizeEffect;
7
8/**
9 * Resize Effect for the Imagick driver
10 */
11class ImagickResizeEffect extends AbstractResizeEffect
12{
13    /**
14     * @param integer $width   The target width.
15     * @param integer $height  The target height.
16     * @param boolean $bestFit The "best_fit" flag.
17     * @return void
18     */
19    protected function doResize($width, $height, $bestFit = false)
20    {
21        if ($this->adaptive()) {
22            $this->image()->imagick()->adaptiveResizeImage($width, $height, $bestFit);
23        } else {
24            $this->image()->imagick()->resizeImage($width, $height, Imagick::FILTER_LANCZOS, 1, $bestFit);
25        }
26    }
27}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Imagick/Effect/ImagickRevertEffect.php.html b/packages/image/build/report/Imagick/Effect/ImagickRevertEffect.php.html new file mode 100644 index 000000000..50021d1e7 --- /dev/null +++ b/packages/image/build/report/Imagick/Effect/ImagickRevertEffect.php.html @@ -0,0 +1,180 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickRevertEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 80.00% covered (success) +
+
+
80.00%
4 / 5
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagickRevertEffect
+
+ 80.00% covered (success) +
+
+
80.00%
4 / 5
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2.03
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 process
+
+ 80.00% covered (success) +
+
+
80.00%
4 / 5
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2.03
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Imagick\Effect;
4
5use Charcoal\Image\Effect\AbstractRevertEffect;
6
7/**
8 * Revert Effect for the Imagick driver.
9 */
10class ImagickRevertEffect extends AbstractRevertEffect
11{
12     /**
13      * @param array $data The effect data, if available.
14      * @return ImagickRevertEffect Chainable
15      */
16    public function process(?array $data = null): static
17    {
18        if ($data !== null) {
19            $this->setData($data);
20        }
21
22        $channel = $this->image()->imagickChannel($this->channel());
23        $this->image()->imagick()->negateImage(false, $channel);
24
25        return $this;
26    }
27}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Imagick/Effect/ImagickRotateEffect.php.html b/packages/image/build/report/Imagick/Effect/ImagickRotateEffect.php.html new file mode 100644 index 000000000..cc81fc0a0 --- /dev/null +++ b/packages/image/build/report/Imagick/Effect/ImagickRotateEffect.php.html @@ -0,0 +1,178 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickRotateEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 75.00% covered (warning) +
+
+
75.00%
3 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagickRotateEffect
+
+ 75.00% covered (warning) +
+
+
75.00%
3 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2.06
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 process
+
+ 75.00% covered (warning) +
+
+
75.00%
3 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2.06
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Imagick\Effect;
4
5use Charcoal\Image\Effect\AbstractRotateEffect;
6
7/**
8 * Rotate Effect for the imagick driver.
9 */
10class ImagickRotateEffect extends AbstractRotateEffect
11{
12    /**
13     * @param array $data The effect data, if available.
14     * @return ImagickRotateEffect Chainable
15     */
16    public function process(?array $data = null): static
17    {
18        if ($data !== null) {
19            $this->setData($data);
20        }
21
22        $this->image()->imagick()->rotateImage($this->backgroundColor(), $this->angle());
23        return $this;
24    }
25}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Imagick/Effect/ImagickSepiaEffect.php.html b/packages/image/build/report/Imagick/Effect/ImagickSepiaEffect.php.html new file mode 100644 index 000000000..024ee7d57 --- /dev/null +++ b/packages/image/build/report/Imagick/Effect/ImagickSepiaEffect.php.html @@ -0,0 +1,179 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickSepiaEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 75.00% covered (warning) +
+
+
75.00%
3 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagickSepiaEffect
+
+ 75.00% covered (warning) +
+
+
75.00%
3 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2.06
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 process
+
+ 75.00% covered (warning) +
+
+
75.00%
3 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2.06
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Imagick\Effect;
4
5use Charcoal\Image\Effect\AbstractSepiaEffect;
6
7/**
8 * Sepia Effect for the Imagick driver.
9 */
10class ImagickSepiaEffect extends AbstractSepiaEffect
11{
12    /**
13     * @param array $data The effect data, if available.
14     * @return ImagickSepiaEffect Chainable
15     */
16    public function process(?array $data = null): static
17    {
18        if ($data !== null) {
19            $this->setData($data);
20        }
21
22        $this->image()->imagick()->sepiaToneImage($this->threshold());
23
24        return $this;
25    }
26}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Imagick/Effect/ImagickSharpenEffect.php.html b/packages/image/build/report/Imagick/Effect/ImagickSharpenEffect.php.html new file mode 100644 index 000000000..20900aae2 --- /dev/null +++ b/packages/image/build/report/Imagick/Effect/ImagickSharpenEffect.php.html @@ -0,0 +1,244 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickSharpenEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 76.92% covered (warning) +
+
+
76.92%
10 / 13
+
+ 66.67% covered (warning) +
+
+
66.67%
2 / 3
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagickSharpenEffect
+
+ 76.92% covered (warning) +
+
+
76.92%
10 / 13
+
+ 66.67% covered (warning) +
+
+
66.67%
2 / 3
3.11
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 processAdaptive
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 3
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2
 processUnsharp
+
+ 100.00% covered (success) +
+
+
100.00%
7 / 7
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 processStandard
+
+ 100.00% covered (success) +
+
+
100.00%
3 / 3
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Imagick\Effect;
4
5use Charcoal\Image\Effect\AbstractSharpenEffect;
6
7/**
8 * Sharpen Effect for Imagick driver.
9 */
10class ImagickSharpenEffect extends AbstractSharpenEffect
11{
12    /**
13     * @return ImagickSharpenEffect Chainable
14     */
15    public function processAdaptive(): static
16    {
17        $channel = $this->image()->imagickChannel($this->channel());
18        $this->image()->imagick()->adaptiveAbstractSharpenEffectImage($this->radius(), $this->sigma(), $channel);
19        return $this;
20    }
21
22    /**
23     * @return ImagickSharpenEffect Chainable
24     */
25    public function processUnsharp(): static
26    {
27        $radius = $this->radius();
28        $sigma = $this->sigma();
29        $amount = $this->amount();
30        $threshold = $this->threshold();
31        $channel = $this->image()->imagickChannel($this->channel());
32
33        $this->image()->imagick()->unsharpMaskImage($radius, $sigma, $amount, $threshold, $channel);
34
35        return $this;
36    }
37
38    /**
39     * @return ImagickSharpenEffect Chainable
40     */
41    public function processStandard(): static
42    {
43        $channel = $this->image()->imagickChannel($this->channel());
44        $this->image()->imagick()->sharpenImage($this->radius(), $this->sigma(), $channel);
45        return $this;
46    }
47}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Imagick/Effect/ImagickThresholdEffect.php.html b/packages/image/build/report/Imagick/Effect/ImagickThresholdEffect.php.html new file mode 100644 index 000000000..c97a80dbb --- /dev/null +++ b/packages/image/build/report/Imagick/Effect/ImagickThresholdEffect.php.html @@ -0,0 +1,181 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickThresholdEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 85.71% covered (success) +
+
+
85.71%
6 / 7
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagickThresholdEffect
+
+ 85.71% covered (success) +
+
+
85.71%
6 / 7
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2.01
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 process
+
+ 85.71% covered (success) +
+
+
85.71%
6 / 7
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
2.01
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Imagick\Effect;
4
5use Charcoal\Image\Effect\AbstractThresholdEffect;
6
7/**
8 * Threshold Effect for the Imagick driver.
9 */
10class ImagickThresholdEffect extends AbstractThresholdEffect
11{
12    /**
13     * @param array $data The effect data, if available.
14     */
15    public function process(?array $data = null): static
16    {
17        if ($data !== null) {
18            $this->setData($data);
19        }
20
21        $max = $this->image()->imagick()->getQuantumRange();
22        $max = $max['quantumRangeLong'];
23        $threshold = ($this->threshold() * $max);
24        $this->image()->imagick()->thresholdImage($threshold);
25
26        return $this;
27    }
28}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Imagick/Effect/ImagickTintEffect.php.html b/packages/image/build/report/Imagick/Effect/ImagickTintEffect.php.html new file mode 100644 index 000000000..c5d609a8f --- /dev/null +++ b/packages/image/build/report/Imagick/Effect/ImagickTintEffect.php.html @@ -0,0 +1,185 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickTintEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 87.50% covered (success) +
+
+
87.50%
7 / 8
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagickTintEffect
+
+ 87.50% covered (success) +
+
+
87.50%
7 / 8
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
3.02
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 process
+
+ 87.50% covered (success) +
+
+
87.50%
7 / 8
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
3.02
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Imagick\Effect;
4
5use ImagickPixel;
6use Charcoal\Image\Effect\AbstractTintEffect;
7
8/**
9 * Tint Effect for the Imagick driver.
10 */
11class ImagickTintEffect extends AbstractTintEffect
12{
13    /**
14     * @param array $data The effect data, if available.
15     */
16    public function process(?array $data = null): static
17    {
18        if ($data !== null) {
19            $this->setData($data);
20        }
21        $color = new ImagickPixel($this->color());
22        $opacity = new ImagickPixel(sprintf('rgba(255,255,255,%f)', $this->opacity()));
23
24        if ($this->midtone() === true) {
25            $this->image()->imagick()->tintImage($color, $opacity);
26        } else {
27            $this->image()->imagick()->colorizeImage($color, $opacity);
28        }
29
30        return $this;
31    }
32}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Imagick/Effect/ImagickWatermarkEffect.php.html b/packages/image/build/report/Imagick/Effect/ImagickWatermarkEffect.php.html new file mode 100644 index 000000000..47f752486 --- /dev/null +++ b/packages/image/build/report/Imagick/Effect/ImagickWatermarkEffect.php.html @@ -0,0 +1,246 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickWatermarkEffect.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 82.14% covered (success) +
+
+
82.14%
46 / 56
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagickWatermarkEffect
+
+ 82.14% covered (success) +
+
+
82.14%
46 / 56
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
15.12
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 process
+
+ 82.14% covered (success) +
+
+
82.14%
46 / 56
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
15.12
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Imagick\Effect;
4
5use Exception;
6use Imagick;
7use Charcoal\Image\Effect\AbstractWatermarkEffect;
8use Charcoal\Image\ImageInterface;
9
10/**
11 * Watermark Effect for the Imagick driver.
12 */
13class ImagickWatermarkEffect extends AbstractWatermarkEffect
14{
15    /**
16     * @param array $data The effect data, if available.
17     * @throws Exception If the image data is invalid.
18     */
19    public function process(?array $data = null): static
20    {
21        if ($data !== null) {
22            $this->setData($data);
23        }
24
25        $img = $this->image();
26        $imageWidth = $img->width();
27        $imageHeight = $img->height();
28
29        if ($this->watermark() instanceof ImageInterface) {
30            $watermark = $this->watermark();
31        } else {
32            $imgClass = $img::class;
33            $watermark = new $imgClass();
34            $watermark->open($this->watermark());
35        }
36
37        if (($watermark->width() > $imageWidth) || ($watermark->height() > $imageHeight)) {
38            // Scale-down watermark image, if necessary
39            $watermark->resize([
40                'mode' => 'best_fit',
41                'width' => $imageWidth,
42                'height' => $imageHeight
43            ]);
44        }
45
46        $watermarkWidth = $watermark->width();
47        $watermarkHeight = $watermark->height();
48
49        $gravity = $this->gravity();
50        if ($gravity == 'nw') {
51            $x = $this->x();
52            $y = $this->y();
53        } elseif ($gravity == 'n') {
54            $x = ($imageWidth / 2 - ($watermarkWidth / 2) + $this->x());
55            $y = $this->y();
56        } elseif ($gravity == 'ne') {
57            $x = ($imageWidth - $watermarkHeight - $this->x());
58            $y = $this->y();
59        } elseif ($gravity == 'w') {
60            $x = $this->x();
61            $y = ($imageHeight / 2 - ($watermarkHeight / 2) + $this->y());
62        } elseif ($gravity == 'center') {
63            $x = ($imageWidth / 2 - ($watermarkWidth / 2) + $this->x());
64            $y = ($imageHeight / 2 - ($watermarkHeight / 2) + $this->y());
65        } elseif ($gravity == 'e') {
66            $x = ($imageWidth - $watermarkWidth - $this->x());
67            $y = ($imageHeight / 2 - ($watermarkHeight / 2) + $this->y());
68        } elseif ($gravity == 'sw') {
69            $x = $this->x();
70            $y = ($imageHeight - $watermarkHeight - $this->y());
71        } elseif ($gravity == 's') {
72            $x = ($imageWidth / 2 - ($watermarkWidth / 2) + $this->x());
73            $y = ($imageHeight - $watermarkHeight - $this->y());
74        } elseif ($gravity == 'se') {
75            $x = ($imageWidth - $watermarkWidth - $this->x());
76            $y = ($imageHeight - $watermarkHeight - $this->y());
77        } else {
78            throw new Exception(
79                'Invalid gravity'
80            );
81        }
82
83        $x = max(0, $x);
84        $x = min(($imageWidth - $watermarkWidth), $x);
85        $y = max(0, $y);
86        $y = min(($imageHeight - $watermarkHeight), $y);
87
88        $compositeMode = Imagick::COMPOSITE_MULTIPLY;
89        $this->image()->imagick()->compositeImage($watermark->imagick(), $compositeMode, $x, $y);
90
91        return $this;
92    }
93}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Imagick/Effect/dashboard.html b/packages/image/build/report/Imagick/Effect/dashboard.html new file mode 100644 index 000000000..35a9b8209 --- /dev/null +++ b/packages/image/build/report/Imagick/Effect/dashboard.html @@ -0,0 +1,342 @@ + + + + + Dashboard for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect + + + + + + + +
+
+
+ +
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+ +
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + + + + + + + + + + + + +
MethodCoverage
process0%
processSoft0%
process0%
doCrop0%
process0%
process0%
processAdaptive0%
doResize66%
process75%
process75%
process75%
+
+
+
+

Project Risks

+
+ + + + + + + + + + + + + + + + + + + + + +
MethodCoverageComplexityCRAP
process0.0%11132
doCrop0.0%10110
process0.0%756
process0.0%26
process0.0%26
process75.0%22
doResize66.7%22
process75.0%22
process75.0%22
+
+
+
+ +
+ + + + + + diff --git a/packages/image/build/report/Imagick/Effect/index.html b/packages/image/build/report/Imagick/Effect/index.html new file mode 100644 index 000000000..c16022baa --- /dev/null +++ b/packages/image/build/report/Imagick/Effect/index.html @@ -0,0 +1,596 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect + + + + + + + +
+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 50.21% covered (warning) +
+
+
50.21%
121 / 241
+
+ 28.00% covered (danger) +
+
+
28.00%
7 / 25
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 18
ImagickAutoorientationEffect.php
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 32
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagickBlurEffect.php
+
+ 84.21% covered (success) +
+
+
84.21%
16 / 19
+
+ 83.33% covered (success) +
+
+
83.33%
5 / 6
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagickCompressionEffect.php
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 17
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagickCropEffect.php
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 36
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagickDitherEffect.php
+
+ 81.82% covered (success) +
+
+
81.82%
9 / 11
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagickFormatEffect.php
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 3
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagickGrayscaleEffect.php
+
+ 75.00% covered (warning) +
+
+
75.00%
3 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagickMaskEffect.php
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 5
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagickMirrorEffect.php
+
+ 85.71% covered (success) +
+
+
85.71%
6 / 7
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagickModulateEffect.php
+
+ 85.71% covered (success) +
+
+
85.71%
6 / 7
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagickResizeEffect.php
+
+ 66.67% covered (warning) +
+
+
66.67%
2 / 3
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagickRevertEffect.php
+
+ 80.00% covered (success) +
+
+
80.00%
4 / 5
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagickRotateEffect.php
+
+ 75.00% covered (warning) +
+
+
75.00%
3 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagickSepiaEffect.php
+
+ 75.00% covered (warning) +
+
+
75.00%
3 / 4
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagickSharpenEffect.php
+
+ 76.92% covered (warning) +
+
+
76.92%
10 / 13
+
+ 66.67% covered (warning) +
+
+
66.67%
2 / 3
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagickThresholdEffect.php
+
+ 85.71% covered (success) +
+
+
85.71%
6 / 7
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagickTintEffect.php
+
+ 87.50% covered (success) +
+
+
87.50%
7 / 8
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagickWatermarkEffect.php
+
+ 82.14% covered (success) +
+
+
82.14%
46 / 56
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ +
+ + diff --git a/packages/image/build/report/Imagick/ImagickImage.php.html b/packages/image/build/report/Imagick/ImagickImage.php.html new file mode 100644 index 000000000..49861b7f5 --- /dev/null +++ b/packages/image/build/report/Imagick/ImagickImage.php.html @@ -0,0 +1,537 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/ImagickImage.php + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 88.89% covered (success) +
+
+
88.89%
64 / 72
+
+ 70.00% covered (warning) +
+
+
70.00%
7 / 10
CRAP
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
ImagickImage
+
+ 88.89% covered (success) +
+
+
88.89%
64 / 72
+
+ 70.00% covered (warning) +
+
+
70.00%
7 / 10
25.86
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
 __construct
+
+ 40.00% covered (danger) +
+
+
40.00%
2 / 5
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
4.94
 driverType
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 imagick
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 create
+
+ 100.00% covered (success) +
+
+
100.00%
10 / 10
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
5
 open
+
+ 80.00% covered (success) +
+
+
80.00%
8 / 10
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
5.20
 save
+
+ 66.67% covered (warning) +
+
+
66.67%
6 / 9
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
4.59
 width
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 height
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
1
 imagickChannel
+
+ 100.00% covered (success) +
+
+
100.00%
18 / 18
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
 imagickGravity
+
+ 100.00% covered (success) +
+
+
100.00%
16 / 16
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
2
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1<?php
2
3namespace Charcoal\Image\Imagick;
4
5use Exception;
6use InvalidArgumentException;
7use Imagick;
8use Charcoal\Image\AbstractImage;
9
10/**
11 *
12 */
13class ImagickImage extends AbstractImage
14{
15    private readonly \Imagick $imagick;
16
17    /**
18     * @throws Exception If imagick driver can not be loaded.
19     */
20    public function __construct()
21    {
22        if (!extension_loaded('imagick') || !class_exists('Imagick')) {
23            throw new Exception(
24                'The Imagick PHP extension is required.'
25            );
26        }
27        $this->imagick = new Imagick();
28    }
29
30    public function driverType(): string
31    {
32        return 'imagick';
33    }
34
35    public function imagick(): \Imagick
36    {
37        return $this->imagick;
38    }
39
40    /**
41     * Create a blank canvas of a given size, with a given background color.
42     *
43     * @param integer $width  Image size, in pixels.
44     * @param integer $height Image height, in pixels.
45     * @param string  $color  Default to transparent.
46     * @throws InvalidArgumentException If the size arguments are not valid, positive integers.
47     */
48    public function create($width, $height, $color = 'rgb(100%, 100%, 100%, 0)'): static
49    {
50        if (!is_numeric($width) || $width < 1) {
51            throw new InvalidArgumentException(
52                'Width must be an integer of at least 1 pixel'
53            );
54        }
55        if (!is_numeric($height) || $height < 1) {
56            throw new InvalidArgumentException(
57                'Height must be an integer of at least 1 pixel'
58            );
59        }
60        $this->imagick()->newImage((int)$width, (int)$height, $color);
61        return $this;
62    }
63
64    /**
65     * Open an image file
66     *
67     * @param string $source The source path / filename.
68     * @throws InvalidArgumentException If the source argument is not a string.
69     */
70    public function open($source = null): static
71    {
72        if ($source !== null && !is_string($source)) {
73            throw new InvalidArgumentException(
74                'Source must be a string'
75            );
76        }
77
78        $source = $source ?: $this->source();
79        if (parse_url($source, PHP_URL_HOST)) {
80            $handle = fopen($source, 'rb');
81            $this->imagick()->readImageFile($handle);
82        } else {
83            $this->imagick()->readImage($source);
84        }
85
86        return $this;
87    }
88
89    /**
90     * Save an image to a target.
91     * If no target is set, the original source will be owerwritten
92     *
93     * @param string $target The target path / filename.
94     * @throws InvalidArgumentException If the target argument is not a string.
95     */
96    public function save($target = null): static
97    {
98        if ($target !== null && !is_string($target)) {
99            throw new InvalidArgumentException(
100                'Target must be a string (file path)'
101            );
102        }
103
104        $target  = $target ?: $this->target();
105        $fileExt = pathinfo($target, PATHINFO_EXTENSION);
106
107        $this->imagick()->setImageFormat($fileExt);
108        $this->imagick()->writeImage($target);
109
110        return $this;
111    }
112
113    /**
114     * Get the image's width, in pixels
115     */
116    public function width(): int
117    {
118        return $this->imagick()->getImageWidth();
119    }
120
121    /**
122     * Get the image's height, in pixels
123     */
124    public function height(): int
125    {
126        return $this->imagick()->getImageHeight();
127    }
128
129    /**
130     * Convert a channel name (string) to an `Imagick::CHANNEL_*` constant (integer)
131     *
132     * @param string $channel The standard "channel" string.
133     * @throws InvalidArgumentException If the channel argument is not a valid channel.
134     */
135    public function imagickChannel($channel): int
136    {
137        $channelMap = [
138            // RGB
139            'red'       => Imagick::CHANNEL_RED,
140            'green'     => Imagick::CHANNEL_GREEN,
141            'blue'      => Imagick::CHANNEL_BLUE,
142            // CMYK
143            'cyan'      => Imagick::CHANNEL_CYAN,
144            'magenta'   => Imagick::CHANNEL_MAGENTA,
145            'yellow'    => Imagick::CHANNEL_YELLOW,
146            'black'     => Imagick::CHANNEL_BLACK,
147            // Others
148            'all'       => Imagick::CHANNEL_ALL,
149            'alpha'     => Imagick::CHANNEL_ALPHA,
150            'opacity'   => Imagick::CHANNEL_RED,
151            'gray'      => Imagick::CHANNEL_GRAY
152        ];
153        if (!isset($channelMap[$channel])) {
154            throw new InvalidArgumentException(
155                'Invalid channel'
156            );
157        }
158        return $channelMap[$channel];
159    }
160
161    /**
162     * Convert a gravity name (string) to an `Imagick::GRAVITY_*` constant (integer)
163     *
164     * @param string $gravity The standard gravity name.
165     * @throws InvalidArgumentException If the gravity argument is not a valid gravity type.
166     */
167    public function imagickGravity($gravity): int
168    {
169        $gravityMap = [
170            'center'    => Imagick::GRAVITY_CENTER,
171            'n'         => Imagick::GRAVITY_NORTH,
172            's'         => Imagick::GRAVITY_SOUTH,
173            'e'         => Imagick::GRAVITY_EAST,
174            'w'         => Imagick::GRAVITY_WEST,
175            'ne'        => Imagick::GRAVITY_NORTHEAST,
176            'nw'        => Imagick::GRAVITY_NORTHWEST,
177            'se'        => Imagick::GRAVITY_SOUTHEAST,
178            'sw'        => Imagick::GRAVITY_SOUTHWEST
179        ];
180        if (!isset($gravityMap[$gravity])) {
181            throw new InvalidArgumentException(
182                'Invalid gravity'
183            );
184        }
185        return $gravityMap[$gravity];
186    }
187}
+ + +
+
+

Legend

+

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

+

+ Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. +

+ + + +
+
+ + + + + diff --git a/packages/image/build/report/Imagick/dashboard.html b/packages/image/build/report/Imagick/dashboard.html new file mode 100644 index 000000000..22a263b9a --- /dev/null +++ b/packages/image/build/report/Imagick/dashboard.html @@ -0,0 +1,345 @@ + + + + + Dashboard for /var/www/html/packages/image/src/Charcoal/Image/Imagick + + + + + + + +
+
+
+ +
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+ +
+
+

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+
+

Insufficient Coverage

+
+ + + + + + + + + + + + + + + + + + + + + + + +
MethodCoverage
process0%
processSoft0%
process0%
doCrop0%
process0%
process0%
processAdaptive0%
__construct40%
doResize66%
save66%
process75%
process75%
process75%
+
+
+
+

Project Risks

+
+ + + + + + + + + + + + + + + + + + + + + + + +
MethodCoverageComplexityCRAP
process0.0%11132
doCrop0.0%10110
process0.0%756
process0.0%26
process0.0%26
__construct40.0%34
save66.7%44
process75.0%22
doResize66.7%22
process75.0%22
process75.0%22
+
+
+
+ +
+ + + + + + diff --git a/packages/image/build/report/Imagick/index.html b/packages/image/build/report/Imagick/index.html new file mode 100644 index 000000000..f2c4f8017 --- /dev/null +++ b/packages/image/build/report/Imagick/index.html @@ -0,0 +1,147 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick + + + + + + + +
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 59.11% covered (warning) +
+
+
59.11%
185 / 313
+
+ 40.00% covered (danger) +
+
+
40.00%
14 / 35
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 19
Effect
+
+ 50.21% covered (warning) +
+
+
50.21%
121 / 241
+
+ 28.00% covered (danger) +
+
+
28.00%
7 / 25
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 18
ImagickImage.php
+
+ 88.89% covered (success) +
+
+
88.89%
64 / 72
+
+ 70.00% covered (warning) +
+
+
70.00%
7 / 10
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
+
+ +
+ + diff --git a/packages/image/build/report/_css/billboard.min.css b/packages/image/build/report/_css/billboard.min.css new file mode 100644 index 000000000..16f2baf4e --- /dev/null +++ b/packages/image/build/report/_css/billboard.min.css @@ -0,0 +1,10 @@ +/*! + * Copyright (c) 2017 ~ present NAVER Corp. + * billboard.js project is licensed under the MIT license + * + * billboard.js, JavaScript chart library + * https://naver.github.io/billboard.js/ + * + * @version 3.15.1 + */ +.bb svg{font:10px sans-serif;-webkit-tap-highlight-color:rgba(0,0,0,0)}.bb path,.bb line{fill:none;stroke:#000}.bb text,.bb .bb-button{-webkit-user-select:none;-moz-user-select:none;user-select:none}.bb-legend-item-tile,.bb-xgrid-focus,.bb-ygrid-focus,.bb-ygrid{shape-rendering:crispEdges}.bb-chart-arcs .bb-needle,.bb-chart-arc .bb-gauge-value{fill:#000}.bb-chart-arc path{stroke:#fff}.bb-chart-arc rect{stroke:#fff;stroke-width:1}.bb-chart-arc text{fill:#fff;font-size:13px}.bb-chart-funnels path{stroke-width:0}.bb-chart-funnels+.bb-chart-texts text{font-size:13px;fill:#fff}.bb-axis{shape-rendering:crispEdges}.bb-axis .bb-axis-x-tooltip,.bb-axis .bb-axis-y-tooltip,.bb-axis .bb-axis-y2-tooltip{font-size:1em;fill:#fff;white-space:nowrap}.bb-grid{pointer-events:none}.bb-grid line{stroke:#aaa}.bb-grid text{fill:#aaa}.bb-xgrid,.bb-ygrid{stroke-dasharray:3 3}.bb-text.bb-empty{fill:gray;font-size:2em}.bb-line{stroke-width:1px}.bb-circle._expanded_{stroke-width:1px;stroke:#fff}.bb-selected-circle{fill:#fff;stroke-width:2px}.bb-bar{stroke-width:0}.bb-bar._expanded_{fill-opacity:.75}.bb-candlestick{stroke-width:1px}.bb-candlestick._expanded_{fill-opacity:.75}.bb-target.bb-focused,.bb-circles.bb-focused{opacity:1}.bb-target.bb-focused path.bb-line,.bb-target.bb-focused path.bb-step,.bb-circles.bb-focused path.bb-line,.bb-circles.bb-focused path.bb-step{stroke-width:2px}.bb-target.bb-defocused,.bb-circles.bb-defocused{opacity:.3!important}.bb-target.bb-defocused .text-overlapping,.bb-circles.bb-defocused .text-overlapping{opacity:.05!important}.bb-region{fill:#4682b4}.bb-region rect{fill-opacity:.1}.bb-zoom-brush,.bb-brush .extent{fill-opacity:.1}.bb-legend-item{font-size:12px;user-select:none}.bb-legend-item-hidden{opacity:.15}.bb-legend-background{opacity:.75;fill:#fff;stroke:#d3d3d3;stroke-width:1}.bb-title{font:14px sans-serif}.bb-chart-treemaps rect{stroke:#fff;stroke-width:1px}.bb-tooltip-container{z-index:10;user-select:none}.bb-tooltip{border-collapse:collapse;border-spacing:0;background-color:#fff;empty-cells:show;opacity:.9;box-shadow:7px 7px 12px -9px #777;white-space:nowrap}.bb-tooltip tr{border:1px solid #CCC}.bb-tooltip th{background-color:#aaa;font-size:14px;padding:2px 5px;text-align:left;color:#fff}.bb-tooltip td{font-size:13px;padding:3px 6px;background-color:#fff;border-left:1px dotted #999}.bb-tooltip td>span,.bb-tooltip td>svg{display:inline-block;width:10px;height:10px;margin-right:6px}.bb-tooltip.value{text-align:right}.bb-area{stroke-width:0;opacity:.2}.bb-chart-arcs-title{dominant-baseline:middle;font-size:1.3em}text.bb-chart-arcs-gauge-title{dominant-baseline:middle;font-size:2.7em}.bb-chart-arcs .bb-chart-arcs-background{fill:#e0e0e0;stroke:#fff}.bb-chart-arcs .bb-chart-arcs-gauge-unit{fill:#000;font-size:16px}.bb-chart-arcs .bb-chart-arcs-gauge-max,.bb-chart-arcs .bb-chart-arcs-gauge-min{fill:#777}.bb-chart-arcs .bb-levels circle{fill:none;stroke:#848282;stroke-width:.5px}.bb-chart-arcs .bb-levels text{fill:#848282}.bb-chart-radars .bb-levels polygon{fill:none;stroke:#848282;stroke-width:.5px}.bb-chart-radars .bb-levels text{fill:#848282}.bb-chart-radars .bb-axis line{stroke:#848282;stroke-width:.5px}.bb-chart-radars .bb-axis text{font-size:1.15em;cursor:default}.bb-chart-radars .bb-shapes polygon{fill-opacity:.2;stroke-width:1px}.bb-button{position:absolute;top:10px;right:10px}.bb-button .bb-zoom-reset{font-size:11px;border:solid 1px #ccc;background-color:#fff;padding:5px;border-radius:5px;cursor:pointer} diff --git a/packages/image/build/report/_css/bootstrap.min.css b/packages/image/build/report/_css/bootstrap.min.css new file mode 100644 index 000000000..27d4921cd --- /dev/null +++ b/packages/image/build/report/_css/bootstrap.min.css @@ -0,0 +1,6 @@ +@charset "UTF-8";/*! + * Bootstrap v5.3.6 (https://getbootstrap.com/) + * Copyright 2011-2025 The Bootstrap Authors + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */:root,[data-bs-theme=light]{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-black:#000;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-gray-100:#f8f9fa;--bs-gray-200:#e9ecef;--bs-gray-300:#dee2e6;--bs-gray-400:#ced4da;--bs-gray-500:#adb5bd;--bs-gray-600:#6c757d;--bs-gray-700:#495057;--bs-gray-800:#343a40;--bs-gray-900:#212529;--bs-primary:#0d6efd;--bs-secondary:#6c757d;--bs-success:#198754;--bs-info:#0dcaf0;--bs-warning:#ffc107;--bs-danger:#dc3545;--bs-light:#f8f9fa;--bs-dark:#212529;--bs-primary-rgb:13,110,253;--bs-secondary-rgb:108,117,125;--bs-success-rgb:25,135,84;--bs-info-rgb:13,202,240;--bs-warning-rgb:255,193,7;--bs-danger-rgb:220,53,69;--bs-light-rgb:248,249,250;--bs-dark-rgb:33,37,41;--bs-primary-text-emphasis:#052c65;--bs-secondary-text-emphasis:#2b2f32;--bs-success-text-emphasis:#0a3622;--bs-info-text-emphasis:#055160;--bs-warning-text-emphasis:#664d03;--bs-danger-text-emphasis:#58151c;--bs-light-text-emphasis:#495057;--bs-dark-text-emphasis:#495057;--bs-primary-bg-subtle:#cfe2ff;--bs-secondary-bg-subtle:#e2e3e5;--bs-success-bg-subtle:#d1e7dd;--bs-info-bg-subtle:#cff4fc;--bs-warning-bg-subtle:#fff3cd;--bs-danger-bg-subtle:#f8d7da;--bs-light-bg-subtle:#fcfcfd;--bs-dark-bg-subtle:#ced4da;--bs-primary-border-subtle:#9ec5fe;--bs-secondary-border-subtle:#c4c8cb;--bs-success-border-subtle:#a3cfbb;--bs-info-border-subtle:#9eeaf9;--bs-warning-border-subtle:#ffe69c;--bs-danger-border-subtle:#f1aeb5;--bs-light-border-subtle:#e9ecef;--bs-dark-border-subtle:#adb5bd;--bs-white-rgb:255,255,255;--bs-black-rgb:0,0,0;--bs-font-sans-serif:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue","Noto Sans","Liberation Sans",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--bs-font-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--bs-gradient:linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));--bs-body-font-family:var(--bs-font-sans-serif);--bs-body-font-size:1rem;--bs-body-font-weight:400;--bs-body-line-height:1.5;--bs-body-color:#212529;--bs-body-color-rgb:33,37,41;--bs-body-bg:#fff;--bs-body-bg-rgb:255,255,255;--bs-emphasis-color:#000;--bs-emphasis-color-rgb:0,0,0;--bs-secondary-color:rgba(33, 37, 41, 0.75);--bs-secondary-color-rgb:33,37,41;--bs-secondary-bg:#e9ecef;--bs-secondary-bg-rgb:233,236,239;--bs-tertiary-color:rgba(33, 37, 41, 0.5);--bs-tertiary-color-rgb:33,37,41;--bs-tertiary-bg:#f8f9fa;--bs-tertiary-bg-rgb:248,249,250;--bs-heading-color:inherit;--bs-link-color:#0d6efd;--bs-link-color-rgb:13,110,253;--bs-link-decoration:underline;--bs-link-hover-color:#0a58ca;--bs-link-hover-color-rgb:10,88,202;--bs-code-color:#d63384;--bs-highlight-color:#212529;--bs-highlight-bg:#fff3cd;--bs-border-width:1px;--bs-border-style:solid;--bs-border-color:#dee2e6;--bs-border-color-translucent:rgba(0, 0, 0, 0.175);--bs-border-radius:0.375rem;--bs-border-radius-sm:0.25rem;--bs-border-radius-lg:0.5rem;--bs-border-radius-xl:1rem;--bs-border-radius-xxl:2rem;--bs-border-radius-2xl:var(--bs-border-radius-xxl);--bs-border-radius-pill:50rem;--bs-box-shadow:0 0.5rem 1rem rgba(0, 0, 0, 0.15);--bs-box-shadow-sm:0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);--bs-box-shadow-lg:0 1rem 3rem rgba(0, 0, 0, 0.175);--bs-box-shadow-inset:inset 0 1px 2px rgba(0, 0, 0, 0.075);--bs-focus-ring-width:0.25rem;--bs-focus-ring-opacity:0.25;--bs-focus-ring-color:rgba(13, 110, 253, 0.25);--bs-form-valid-color:#198754;--bs-form-valid-border-color:#198754;--bs-form-invalid-color:#dc3545;--bs-form-invalid-border-color:#dc3545}[data-bs-theme=dark]{color-scheme:dark;--bs-body-color:#dee2e6;--bs-body-color-rgb:222,226,230;--bs-body-bg:#212529;--bs-body-bg-rgb:33,37,41;--bs-emphasis-color:#fff;--bs-emphasis-color-rgb:255,255,255;--bs-secondary-color:rgba(222, 226, 230, 0.75);--bs-secondary-color-rgb:222,226,230;--bs-secondary-bg:#343a40;--bs-secondary-bg-rgb:52,58,64;--bs-tertiary-color:rgba(222, 226, 230, 0.5);--bs-tertiary-color-rgb:222,226,230;--bs-tertiary-bg:#2b3035;--bs-tertiary-bg-rgb:43,48,53;--bs-primary-text-emphasis:#6ea8fe;--bs-secondary-text-emphasis:#a7acb1;--bs-success-text-emphasis:#75b798;--bs-info-text-emphasis:#6edff6;--bs-warning-text-emphasis:#ffda6a;--bs-danger-text-emphasis:#ea868f;--bs-light-text-emphasis:#f8f9fa;--bs-dark-text-emphasis:#dee2e6;--bs-primary-bg-subtle:#031633;--bs-secondary-bg-subtle:#161719;--bs-success-bg-subtle:#051b11;--bs-info-bg-subtle:#032830;--bs-warning-bg-subtle:#332701;--bs-danger-bg-subtle:#2c0b0e;--bs-light-bg-subtle:#343a40;--bs-dark-bg-subtle:#1a1d20;--bs-primary-border-subtle:#084298;--bs-secondary-border-subtle:#41464b;--bs-success-border-subtle:#0f5132;--bs-info-border-subtle:#087990;--bs-warning-border-subtle:#997404;--bs-danger-border-subtle:#842029;--bs-light-border-subtle:#495057;--bs-dark-border-subtle:#343a40;--bs-heading-color:inherit;--bs-link-color:#6ea8fe;--bs-link-hover-color:#8bb9fe;--bs-link-color-rgb:110,168,254;--bs-link-hover-color-rgb:139,185,254;--bs-code-color:#e685b5;--bs-highlight-color:#dee2e6;--bs-highlight-bg:#664d03;--bs-border-color:#495057;--bs-border-color-translucent:rgba(255, 255, 255, 0.15);--bs-form-valid-color:#75b798;--bs-form-valid-border-color:#75b798;--bs-form-invalid-color:#ea868f;--bs-form-invalid-border-color:#ea868f}*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;border:0;border-top:var(--bs-border-width) solid;opacity:.25}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2;color:var(--bs-heading-color)}.h1,h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){.h1,h1{font-size:2.5rem}}.h2,h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){.h2,h2{font-size:2rem}}.h3,h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){.h3,h3{font-size:1.75rem}}.h4,h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){.h4,h4{font-size:1.5rem}}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}.small,small{font-size:.875em}.mark,mark{padding:.1875em;color:var(--bs-highlight-color);background-color:var(--bs-highlight-bg)}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,1));text-decoration:underline}a:hover{--bs-link-color-rgb:var(--bs-link-hover-color-rgb)}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:var(--bs-font-monospace);font-size:1em}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:var(--bs-code-color);word-wrap:break-word}a>code{color:inherit}kbd{padding:.1875rem .375rem;font-size:.875em;color:var(--bs-body-bg);background-color:var(--bs-body-color);border-radius:.25rem}kbd kbd{padding:0;font-size:1em}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:var(--bs-secondary-color);text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator{display:none!important}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;line-height:inherit;font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:left}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}::file-selector-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important}.lead{font-size:1.25rem;font-weight:300}.display-1{font-weight:300;line-height:1.2;font-size:calc(1.625rem + 4.5vw)}@media (min-width:1200px){.display-1{font-size:5rem}}.display-2{font-weight:300;line-height:1.2;font-size:calc(1.575rem + 3.9vw)}@media (min-width:1200px){.display-2{font-size:4.5rem}}.display-3{font-weight:300;line-height:1.2;font-size:calc(1.525rem + 3.3vw)}@media (min-width:1200px){.display-3{font-size:4rem}}.display-4{font-weight:300;line-height:1.2;font-size:calc(1.475rem + 2.7vw)}@media (min-width:1200px){.display-4{font-size:3.5rem}}.display-5{font-weight:300;line-height:1.2;font-size:calc(1.425rem + 2.1vw)}@media (min-width:1200px){.display-5{font-size:3rem}}.display-6{font-weight:300;line-height:1.2;font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){.display-6{font-size:2.5rem}}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:.875em;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote>:last-child{margin-bottom:0}.blockquote-footer{margin-top:-1rem;margin-bottom:1rem;font-size:.875em;color:#6c757d}.blockquote-footer::before{content:"— "}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:var(--bs-body-bg);border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius);max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:.875em;color:var(--bs-secondary-color)}.container,.container-fluid,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{--bs-gutter-x:1.5rem;--bs-gutter-y:0;width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}@media (min-width:1400px){.container,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{max-width:1320px}}:root{--bs-breakpoint-xs:0;--bs-breakpoint-sm:576px;--bs-breakpoint-md:768px;--bs-breakpoint-lg:992px;--bs-breakpoint-xl:1200px;--bs-breakpoint-xxl:1400px}.row{--bs-gutter-x:1.5rem;--bs-gutter-y:0;display:flex;flex-wrap:wrap;margin-top:calc(-1 * var(--bs-gutter-y));margin-right:calc(-.5 * var(--bs-gutter-x));margin-left:calc(-.5 * var(--bs-gutter-x))}.row>*{flex-shrink:0;width:100%;max-width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-top:var(--bs-gutter-y)}.col{flex:1 0 0}.row-cols-auto>*{flex:0 0 auto;width:auto}.row-cols-1>*{flex:0 0 auto;width:100%}.row-cols-2>*{flex:0 0 auto;width:50%}.row-cols-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-4>*{flex:0 0 auto;width:25%}.row-cols-5>*{flex:0 0 auto;width:20%}.row-cols-6>*{flex:0 0 auto;width:16.66666667%}.col-auto{flex:0 0 auto;width:auto}.col-1{flex:0 0 auto;width:8.33333333%}.col-2{flex:0 0 auto;width:16.66666667%}.col-3{flex:0 0 auto;width:25%}.col-4{flex:0 0 auto;width:33.33333333%}.col-5{flex:0 0 auto;width:41.66666667%}.col-6{flex:0 0 auto;width:50%}.col-7{flex:0 0 auto;width:58.33333333%}.col-8{flex:0 0 auto;width:66.66666667%}.col-9{flex:0 0 auto;width:75%}.col-10{flex:0 0 auto;width:83.33333333%}.col-11{flex:0 0 auto;width:91.66666667%}.col-12{flex:0 0 auto;width:100%}.offset-1{margin-left:8.33333333%}.offset-2{margin-left:16.66666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.33333333%}.offset-5{margin-left:41.66666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.33333333%}.offset-8{margin-left:66.66666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.33333333%}.offset-11{margin-left:91.66666667%}.g-0,.gx-0{--bs-gutter-x:0}.g-0,.gy-0{--bs-gutter-y:0}.g-1,.gx-1{--bs-gutter-x:0.25rem}.g-1,.gy-1{--bs-gutter-y:0.25rem}.g-2,.gx-2{--bs-gutter-x:0.5rem}.g-2,.gy-2{--bs-gutter-y:0.5rem}.g-3,.gx-3{--bs-gutter-x:1rem}.g-3,.gy-3{--bs-gutter-y:1rem}.g-4,.gx-4{--bs-gutter-x:1.5rem}.g-4,.gy-4{--bs-gutter-y:1.5rem}.g-5,.gx-5{--bs-gutter-x:3rem}.g-5,.gy-5{--bs-gutter-y:3rem}@media (min-width:576px){.col-sm{flex:1 0 0}.row-cols-sm-auto>*{flex:0 0 auto;width:auto}.row-cols-sm-1>*{flex:0 0 auto;width:100%}.row-cols-sm-2>*{flex:0 0 auto;width:50%}.row-cols-sm-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-sm-4>*{flex:0 0 auto;width:25%}.row-cols-sm-5>*{flex:0 0 auto;width:20%}.row-cols-sm-6>*{flex:0 0 auto;width:16.66666667%}.col-sm-auto{flex:0 0 auto;width:auto}.col-sm-1{flex:0 0 auto;width:8.33333333%}.col-sm-2{flex:0 0 auto;width:16.66666667%}.col-sm-3{flex:0 0 auto;width:25%}.col-sm-4{flex:0 0 auto;width:33.33333333%}.col-sm-5{flex:0 0 auto;width:41.66666667%}.col-sm-6{flex:0 0 auto;width:50%}.col-sm-7{flex:0 0 auto;width:58.33333333%}.col-sm-8{flex:0 0 auto;width:66.66666667%}.col-sm-9{flex:0 0 auto;width:75%}.col-sm-10{flex:0 0 auto;width:83.33333333%}.col-sm-11{flex:0 0 auto;width:91.66666667%}.col-sm-12{flex:0 0 auto;width:100%}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.33333333%}.offset-sm-2{margin-left:16.66666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333333%}.offset-sm-5{margin-left:41.66666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333333%}.offset-sm-8{margin-left:66.66666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333333%}.offset-sm-11{margin-left:91.66666667%}.g-sm-0,.gx-sm-0{--bs-gutter-x:0}.g-sm-0,.gy-sm-0{--bs-gutter-y:0}.g-sm-1,.gx-sm-1{--bs-gutter-x:0.25rem}.g-sm-1,.gy-sm-1{--bs-gutter-y:0.25rem}.g-sm-2,.gx-sm-2{--bs-gutter-x:0.5rem}.g-sm-2,.gy-sm-2{--bs-gutter-y:0.5rem}.g-sm-3,.gx-sm-3{--bs-gutter-x:1rem}.g-sm-3,.gy-sm-3{--bs-gutter-y:1rem}.g-sm-4,.gx-sm-4{--bs-gutter-x:1.5rem}.g-sm-4,.gy-sm-4{--bs-gutter-y:1.5rem}.g-sm-5,.gx-sm-5{--bs-gutter-x:3rem}.g-sm-5,.gy-sm-5{--bs-gutter-y:3rem}}@media (min-width:768px){.col-md{flex:1 0 0}.row-cols-md-auto>*{flex:0 0 auto;width:auto}.row-cols-md-1>*{flex:0 0 auto;width:100%}.row-cols-md-2>*{flex:0 0 auto;width:50%}.row-cols-md-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-md-4>*{flex:0 0 auto;width:25%}.row-cols-md-5>*{flex:0 0 auto;width:20%}.row-cols-md-6>*{flex:0 0 auto;width:16.66666667%}.col-md-auto{flex:0 0 auto;width:auto}.col-md-1{flex:0 0 auto;width:8.33333333%}.col-md-2{flex:0 0 auto;width:16.66666667%}.col-md-3{flex:0 0 auto;width:25%}.col-md-4{flex:0 0 auto;width:33.33333333%}.col-md-5{flex:0 0 auto;width:41.66666667%}.col-md-6{flex:0 0 auto;width:50%}.col-md-7{flex:0 0 auto;width:58.33333333%}.col-md-8{flex:0 0 auto;width:66.66666667%}.col-md-9{flex:0 0 auto;width:75%}.col-md-10{flex:0 0 auto;width:83.33333333%}.col-md-11{flex:0 0 auto;width:91.66666667%}.col-md-12{flex:0 0 auto;width:100%}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.33333333%}.offset-md-2{margin-left:16.66666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333333%}.offset-md-5{margin-left:41.66666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333333%}.offset-md-8{margin-left:66.66666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333333%}.offset-md-11{margin-left:91.66666667%}.g-md-0,.gx-md-0{--bs-gutter-x:0}.g-md-0,.gy-md-0{--bs-gutter-y:0}.g-md-1,.gx-md-1{--bs-gutter-x:0.25rem}.g-md-1,.gy-md-1{--bs-gutter-y:0.25rem}.g-md-2,.gx-md-2{--bs-gutter-x:0.5rem}.g-md-2,.gy-md-2{--bs-gutter-y:0.5rem}.g-md-3,.gx-md-3{--bs-gutter-x:1rem}.g-md-3,.gy-md-3{--bs-gutter-y:1rem}.g-md-4,.gx-md-4{--bs-gutter-x:1.5rem}.g-md-4,.gy-md-4{--bs-gutter-y:1.5rem}.g-md-5,.gx-md-5{--bs-gutter-x:3rem}.g-md-5,.gy-md-5{--bs-gutter-y:3rem}}@media (min-width:992px){.col-lg{flex:1 0 0}.row-cols-lg-auto>*{flex:0 0 auto;width:auto}.row-cols-lg-1>*{flex:0 0 auto;width:100%}.row-cols-lg-2>*{flex:0 0 auto;width:50%}.row-cols-lg-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-lg-4>*{flex:0 0 auto;width:25%}.row-cols-lg-5>*{flex:0 0 auto;width:20%}.row-cols-lg-6>*{flex:0 0 auto;width:16.66666667%}.col-lg-auto{flex:0 0 auto;width:auto}.col-lg-1{flex:0 0 auto;width:8.33333333%}.col-lg-2{flex:0 0 auto;width:16.66666667%}.col-lg-3{flex:0 0 auto;width:25%}.col-lg-4{flex:0 0 auto;width:33.33333333%}.col-lg-5{flex:0 0 auto;width:41.66666667%}.col-lg-6{flex:0 0 auto;width:50%}.col-lg-7{flex:0 0 auto;width:58.33333333%}.col-lg-8{flex:0 0 auto;width:66.66666667%}.col-lg-9{flex:0 0 auto;width:75%}.col-lg-10{flex:0 0 auto;width:83.33333333%}.col-lg-11{flex:0 0 auto;width:91.66666667%}.col-lg-12{flex:0 0 auto;width:100%}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.33333333%}.offset-lg-2{margin-left:16.66666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333333%}.offset-lg-5{margin-left:41.66666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333333%}.offset-lg-8{margin-left:66.66666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333333%}.offset-lg-11{margin-left:91.66666667%}.g-lg-0,.gx-lg-0{--bs-gutter-x:0}.g-lg-0,.gy-lg-0{--bs-gutter-y:0}.g-lg-1,.gx-lg-1{--bs-gutter-x:0.25rem}.g-lg-1,.gy-lg-1{--bs-gutter-y:0.25rem}.g-lg-2,.gx-lg-2{--bs-gutter-x:0.5rem}.g-lg-2,.gy-lg-2{--bs-gutter-y:0.5rem}.g-lg-3,.gx-lg-3{--bs-gutter-x:1rem}.g-lg-3,.gy-lg-3{--bs-gutter-y:1rem}.g-lg-4,.gx-lg-4{--bs-gutter-x:1.5rem}.g-lg-4,.gy-lg-4{--bs-gutter-y:1.5rem}.g-lg-5,.gx-lg-5{--bs-gutter-x:3rem}.g-lg-5,.gy-lg-5{--bs-gutter-y:3rem}}@media (min-width:1200px){.col-xl{flex:1 0 0}.row-cols-xl-auto>*{flex:0 0 auto;width:auto}.row-cols-xl-1>*{flex:0 0 auto;width:100%}.row-cols-xl-2>*{flex:0 0 auto;width:50%}.row-cols-xl-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-xl-4>*{flex:0 0 auto;width:25%}.row-cols-xl-5>*{flex:0 0 auto;width:20%}.row-cols-xl-6>*{flex:0 0 auto;width:16.66666667%}.col-xl-auto{flex:0 0 auto;width:auto}.col-xl-1{flex:0 0 auto;width:8.33333333%}.col-xl-2{flex:0 0 auto;width:16.66666667%}.col-xl-3{flex:0 0 auto;width:25%}.col-xl-4{flex:0 0 auto;width:33.33333333%}.col-xl-5{flex:0 0 auto;width:41.66666667%}.col-xl-6{flex:0 0 auto;width:50%}.col-xl-7{flex:0 0 auto;width:58.33333333%}.col-xl-8{flex:0 0 auto;width:66.66666667%}.col-xl-9{flex:0 0 auto;width:75%}.col-xl-10{flex:0 0 auto;width:83.33333333%}.col-xl-11{flex:0 0 auto;width:91.66666667%}.col-xl-12{flex:0 0 auto;width:100%}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.33333333%}.offset-xl-2{margin-left:16.66666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333333%}.offset-xl-5{margin-left:41.66666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333333%}.offset-xl-8{margin-left:66.66666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333333%}.offset-xl-11{margin-left:91.66666667%}.g-xl-0,.gx-xl-0{--bs-gutter-x:0}.g-xl-0,.gy-xl-0{--bs-gutter-y:0}.g-xl-1,.gx-xl-1{--bs-gutter-x:0.25rem}.g-xl-1,.gy-xl-1{--bs-gutter-y:0.25rem}.g-xl-2,.gx-xl-2{--bs-gutter-x:0.5rem}.g-xl-2,.gy-xl-2{--bs-gutter-y:0.5rem}.g-xl-3,.gx-xl-3{--bs-gutter-x:1rem}.g-xl-3,.gy-xl-3{--bs-gutter-y:1rem}.g-xl-4,.gx-xl-4{--bs-gutter-x:1.5rem}.g-xl-4,.gy-xl-4{--bs-gutter-y:1.5rem}.g-xl-5,.gx-xl-5{--bs-gutter-x:3rem}.g-xl-5,.gy-xl-5{--bs-gutter-y:3rem}}@media (min-width:1400px){.col-xxl{flex:1 0 0}.row-cols-xxl-auto>*{flex:0 0 auto;width:auto}.row-cols-xxl-1>*{flex:0 0 auto;width:100%}.row-cols-xxl-2>*{flex:0 0 auto;width:50%}.row-cols-xxl-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-xxl-4>*{flex:0 0 auto;width:25%}.row-cols-xxl-5>*{flex:0 0 auto;width:20%}.row-cols-xxl-6>*{flex:0 0 auto;width:16.66666667%}.col-xxl-auto{flex:0 0 auto;width:auto}.col-xxl-1{flex:0 0 auto;width:8.33333333%}.col-xxl-2{flex:0 0 auto;width:16.66666667%}.col-xxl-3{flex:0 0 auto;width:25%}.col-xxl-4{flex:0 0 auto;width:33.33333333%}.col-xxl-5{flex:0 0 auto;width:41.66666667%}.col-xxl-6{flex:0 0 auto;width:50%}.col-xxl-7{flex:0 0 auto;width:58.33333333%}.col-xxl-8{flex:0 0 auto;width:66.66666667%}.col-xxl-9{flex:0 0 auto;width:75%}.col-xxl-10{flex:0 0 auto;width:83.33333333%}.col-xxl-11{flex:0 0 auto;width:91.66666667%}.col-xxl-12{flex:0 0 auto;width:100%}.offset-xxl-0{margin-left:0}.offset-xxl-1{margin-left:8.33333333%}.offset-xxl-2{margin-left:16.66666667%}.offset-xxl-3{margin-left:25%}.offset-xxl-4{margin-left:33.33333333%}.offset-xxl-5{margin-left:41.66666667%}.offset-xxl-6{margin-left:50%}.offset-xxl-7{margin-left:58.33333333%}.offset-xxl-8{margin-left:66.66666667%}.offset-xxl-9{margin-left:75%}.offset-xxl-10{margin-left:83.33333333%}.offset-xxl-11{margin-left:91.66666667%}.g-xxl-0,.gx-xxl-0{--bs-gutter-x:0}.g-xxl-0,.gy-xxl-0{--bs-gutter-y:0}.g-xxl-1,.gx-xxl-1{--bs-gutter-x:0.25rem}.g-xxl-1,.gy-xxl-1{--bs-gutter-y:0.25rem}.g-xxl-2,.gx-xxl-2{--bs-gutter-x:0.5rem}.g-xxl-2,.gy-xxl-2{--bs-gutter-y:0.5rem}.g-xxl-3,.gx-xxl-3{--bs-gutter-x:1rem}.g-xxl-3,.gy-xxl-3{--bs-gutter-y:1rem}.g-xxl-4,.gx-xxl-4{--bs-gutter-x:1.5rem}.g-xxl-4,.gy-xxl-4{--bs-gutter-y:1.5rem}.g-xxl-5,.gx-xxl-5{--bs-gutter-x:3rem}.g-xxl-5,.gy-xxl-5{--bs-gutter-y:3rem}}.table{--bs-table-color-type:initial;--bs-table-bg-type:initial;--bs-table-color-state:initial;--bs-table-bg-state:initial;--bs-table-color:var(--bs-emphasis-color);--bs-table-bg:var(--bs-body-bg);--bs-table-border-color:var(--bs-border-color);--bs-table-accent-bg:transparent;--bs-table-striped-color:var(--bs-emphasis-color);--bs-table-striped-bg:rgba(var(--bs-emphasis-color-rgb), 0.05);--bs-table-active-color:var(--bs-emphasis-color);--bs-table-active-bg:rgba(var(--bs-emphasis-color-rgb), 0.1);--bs-table-hover-color:var(--bs-emphasis-color);--bs-table-hover-bg:rgba(var(--bs-emphasis-color-rgb), 0.075);width:100%;margin-bottom:1rem;vertical-align:top;border-color:var(--bs-table-border-color)}.table>:not(caption)>*>*{padding:.5rem .5rem;color:var(--bs-table-color-state,var(--bs-table-color-type,var(--bs-table-color)));background-color:var(--bs-table-bg);border-bottom-width:var(--bs-border-width);box-shadow:inset 0 0 0 9999px var(--bs-table-bg-state,var(--bs-table-bg-type,var(--bs-table-accent-bg)))}.table>tbody{vertical-align:inherit}.table>thead{vertical-align:bottom}.table-group-divider{border-top:calc(var(--bs-border-width) * 2) solid currentcolor}.caption-top{caption-side:top}.table-sm>:not(caption)>*>*{padding:.25rem .25rem}.table-bordered>:not(caption)>*{border-width:var(--bs-border-width) 0}.table-bordered>:not(caption)>*>*{border-width:0 var(--bs-border-width)}.table-borderless>:not(caption)>*>*{border-bottom-width:0}.table-borderless>:not(:first-child){border-top-width:0}.table-striped>tbody>tr:nth-of-type(odd)>*{--bs-table-color-type:var(--bs-table-striped-color);--bs-table-bg-type:var(--bs-table-striped-bg)}.table-striped-columns>:not(caption)>tr>:nth-child(2n){--bs-table-color-type:var(--bs-table-striped-color);--bs-table-bg-type:var(--bs-table-striped-bg)}.table-active{--bs-table-color-state:var(--bs-table-active-color);--bs-table-bg-state:var(--bs-table-active-bg)}.table-hover>tbody>tr:hover>*{--bs-table-color-state:var(--bs-table-hover-color);--bs-table-bg-state:var(--bs-table-hover-bg)}.table-primary{--bs-table-color:#000;--bs-table-bg:#cfe2ff;--bs-table-border-color:#a6b5cc;--bs-table-striped-bg:#c5d7f2;--bs-table-striped-color:#000;--bs-table-active-bg:#bacbe6;--bs-table-active-color:#000;--bs-table-hover-bg:#bfd1ec;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-secondary{--bs-table-color:#000;--bs-table-bg:#e2e3e5;--bs-table-border-color:#b5b6b7;--bs-table-striped-bg:#d7d8da;--bs-table-striped-color:#000;--bs-table-active-bg:#cbccce;--bs-table-active-color:#000;--bs-table-hover-bg:#d1d2d4;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-success{--bs-table-color:#000;--bs-table-bg:#d1e7dd;--bs-table-border-color:#a7b9b1;--bs-table-striped-bg:#c7dbd2;--bs-table-striped-color:#000;--bs-table-active-bg:#bcd0c7;--bs-table-active-color:#000;--bs-table-hover-bg:#c1d6cc;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-info{--bs-table-color:#000;--bs-table-bg:#cff4fc;--bs-table-border-color:#a6c3ca;--bs-table-striped-bg:#c5e8ef;--bs-table-striped-color:#000;--bs-table-active-bg:#badce3;--bs-table-active-color:#000;--bs-table-hover-bg:#bfe2e9;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-warning{--bs-table-color:#000;--bs-table-bg:#fff3cd;--bs-table-border-color:#ccc2a4;--bs-table-striped-bg:#f2e7c3;--bs-table-striped-color:#000;--bs-table-active-bg:#e6dbb9;--bs-table-active-color:#000;--bs-table-hover-bg:#ece1be;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-danger{--bs-table-color:#000;--bs-table-bg:#f8d7da;--bs-table-border-color:#c6acae;--bs-table-striped-bg:#eccccf;--bs-table-striped-color:#000;--bs-table-active-bg:#dfc2c4;--bs-table-active-color:#000;--bs-table-hover-bg:#e5c7ca;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-light{--bs-table-color:#000;--bs-table-bg:#f8f9fa;--bs-table-border-color:#c6c7c8;--bs-table-striped-bg:#ecedee;--bs-table-striped-color:#000;--bs-table-active-bg:#dfe0e1;--bs-table-active-color:#000;--bs-table-hover-bg:#e5e6e7;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-dark{--bs-table-color:#fff;--bs-table-bg:#212529;--bs-table-border-color:#4d5154;--bs-table-striped-bg:#2c3034;--bs-table-striped-color:#fff;--bs-table-active-bg:#373b3e;--bs-table-active-color:#fff;--bs-table-hover-bg:#323539;--bs-table-hover-color:#fff;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-responsive{overflow-x:auto;-webkit-overflow-scrolling:touch}@media (max-width:575.98px){.table-responsive-sm{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:767.98px){.table-responsive-md{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:991.98px){.table-responsive-lg{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1199.98px){.table-responsive-xl{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1399.98px){.table-responsive-xxl{overflow-x:auto;-webkit-overflow-scrolling:touch}}.form-label{margin-bottom:.5rem}.col-form-label{padding-top:calc(.375rem + var(--bs-border-width));padding-bottom:calc(.375rem + var(--bs-border-width));margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + var(--bs-border-width));padding-bottom:calc(.5rem + var(--bs-border-width));font-size:1.25rem}.col-form-label-sm{padding-top:calc(.25rem + var(--bs-border-width));padding-bottom:calc(.25rem + var(--bs-border-width));font-size:.875rem}.form-text{margin-top:.25rem;font-size:.875em;color:var(--bs-secondary-color)}.form-control{display:block;width:100%;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--bs-body-bg);background-clip:padding-box;border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control[type=file]{overflow:hidden}.form-control[type=file]:not(:disabled):not([readonly]){cursor:pointer}.form-control:focus{color:var(--bs-body-color);background-color:var(--bs-body-bg);border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-control::-webkit-date-and-time-value{min-width:85px;height:1.5em;margin:0}.form-control::-webkit-datetime-edit{display:block;padding:0}.form-control::placeholder{color:var(--bs-secondary-color);opacity:1}.form-control:disabled{background-color:var(--bs-secondary-bg);opacity:1}.form-control::-webkit-file-upload-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:var(--bs-body-color);background-color:var(--bs-tertiary-bg);pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:var(--bs-border-width);border-radius:0;-webkit-transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}.form-control::file-selector-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:var(--bs-body-color);background-color:var(--bs-tertiary-bg);pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:var(--bs-border-width);border-radius:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control::-webkit-file-upload-button{-webkit-transition:none;transition:none}.form-control::file-selector-button{transition:none}}.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button{background-color:var(--bs-secondary-bg)}.form-control:hover:not(:disabled):not([readonly])::file-selector-button{background-color:var(--bs-secondary-bg)}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;line-height:1.5;color:var(--bs-body-color);background-color:transparent;border:solid transparent;border-width:var(--bs-border-width) 0}.form-control-plaintext:focus{outline:0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{min-height:calc(1.5em + .5rem + calc(var(--bs-border-width) * 2));padding:.25rem .5rem;font-size:.875rem;border-radius:var(--bs-border-radius-sm)}.form-control-sm::-webkit-file-upload-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-sm::file-selector-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-lg{min-height:calc(1.5em + 1rem + calc(var(--bs-border-width) * 2));padding:.5rem 1rem;font-size:1.25rem;border-radius:var(--bs-border-radius-lg)}.form-control-lg::-webkit-file-upload-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}.form-control-lg::file-selector-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}textarea.form-control{min-height:calc(1.5em + .75rem + calc(var(--bs-border-width) * 2))}textarea.form-control-sm{min-height:calc(1.5em + .5rem + calc(var(--bs-border-width) * 2))}textarea.form-control-lg{min-height:calc(1.5em + 1rem + calc(var(--bs-border-width) * 2))}.form-control-color{width:3rem;height:calc(1.5em + .75rem + calc(var(--bs-border-width) * 2));padding:.375rem}.form-control-color:not(:disabled):not([readonly]){cursor:pointer}.form-control-color::-moz-color-swatch{border:0!important;border-radius:var(--bs-border-radius)}.form-control-color::-webkit-color-swatch{border:0!important;border-radius:var(--bs-border-radius)}.form-control-color.form-control-sm{height:calc(1.5em + .5rem + calc(var(--bs-border-width) * 2))}.form-control-color.form-control-lg{height:calc(1.5em + 1rem + calc(var(--bs-border-width) * 2))}.form-select{--bs-form-select-bg-img:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");display:block;width:100%;padding:.375rem 2.25rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--bs-body-bg);background-image:var(--bs-form-select-bg-img),var(--bs-form-select-bg-icon,none);background-repeat:no-repeat;background-position:right .75rem center;background-size:16px 12px;border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-select{transition:none}}.form-select:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-select[multiple],.form-select[size]:not([size="1"]){padding-right:.75rem;background-image:none}.form-select:disabled{background-color:var(--bs-secondary-bg)}.form-select:-moz-focusring{color:transparent;text-shadow:0 0 0 var(--bs-body-color)}.form-select-sm{padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem;border-radius:var(--bs-border-radius-sm)}.form-select-lg{padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem;border-radius:var(--bs-border-radius-lg)}[data-bs-theme=dark] .form-select{--bs-form-select-bg-img:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23dee2e6' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e")}.form-check{display:block;min-height:1.5rem;padding-left:1.5em;margin-bottom:.125rem}.form-check .form-check-input{float:left;margin-left:-1.5em}.form-check-reverse{padding-right:1.5em;padding-left:0;text-align:right}.form-check-reverse .form-check-input{float:right;margin-right:-1.5em;margin-left:0}.form-check-input{--bs-form-check-bg:var(--bs-body-bg);flex-shrink:0;width:1em;height:1em;margin-top:.25em;vertical-align:top;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--bs-form-check-bg);background-image:var(--bs-form-check-bg-image);background-repeat:no-repeat;background-position:center;background-size:contain;border:var(--bs-border-width) solid var(--bs-border-color);-webkit-print-color-adjust:exact;color-adjust:exact;print-color-adjust:exact}.form-check-input[type=checkbox]{border-radius:.25em}.form-check-input[type=radio]{border-radius:50%}.form-check-input:active{filter:brightness(90%)}.form-check-input:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-check-input:checked{background-color:#0d6efd;border-color:#0d6efd}.form-check-input:checked[type=checkbox]{--bs-form-check-bg-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e")}.form-check-input:checked[type=radio]{--bs-form-check-bg-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e")}.form-check-input[type=checkbox]:indeterminate{background-color:#0d6efd;border-color:#0d6efd;--bs-form-check-bg-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e")}.form-check-input:disabled{pointer-events:none;filter:none;opacity:.5}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{cursor:default;opacity:.5}.form-switch{padding-left:2.5em}.form-switch .form-check-input{--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e");width:2em;margin-left:-2.5em;background-image:var(--bs-form-switch-bg);background-position:left center;border-radius:2em;transition:background-position .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-switch .form-check-input{transition:none}}.form-switch .form-check-input:focus{--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2386b7fe'/%3e%3c/svg%3e")}.form-switch .form-check-input:checked{background-position:right center;--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.form-switch.form-check-reverse{padding-right:2.5em;padding-left:0}.form-switch.form-check-reverse .form-check-input{margin-right:-2.5em;margin-left:0}.form-check-inline{display:inline-block;margin-right:1rem}.btn-check{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.btn-check:disabled+.btn,.btn-check[disabled]+.btn{pointer-events:none;filter:none;opacity:.65}[data-bs-theme=dark] .form-switch .form-check-input:not(:checked):not(:focus){--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%28255, 255, 255, 0.25%29'/%3e%3c/svg%3e")}.form-range{width:100%;height:1.5rem;padding:0;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:transparent}.form-range:focus{outline:0}.form-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range::-moz-focus-outer{border:0}.form-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;-webkit-appearance:none;appearance:none;background-color:#0d6efd;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.form-range::-webkit-slider-thumb:active{background-color:#b6d4fe}.form-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:var(--bs-secondary-bg);border-color:transparent;border-radius:1rem}.form-range::-moz-range-thumb{width:1rem;height:1rem;-moz-appearance:none;appearance:none;background-color:#0d6efd;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-range::-moz-range-thumb{-moz-transition:none;transition:none}}.form-range::-moz-range-thumb:active{background-color:#b6d4fe}.form-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:var(--bs-secondary-bg);border-color:transparent;border-radius:1rem}.form-range:disabled{pointer-events:none}.form-range:disabled::-webkit-slider-thumb{background-color:var(--bs-secondary-color)}.form-range:disabled::-moz-range-thumb{background-color:var(--bs-secondary-color)}.form-floating{position:relative}.form-floating>.form-control,.form-floating>.form-control-plaintext,.form-floating>.form-select{height:calc(3.5rem + calc(var(--bs-border-width) * 2));min-height:calc(3.5rem + calc(var(--bs-border-width) * 2));line-height:1.25}.form-floating>label{position:absolute;top:0;left:0;z-index:2;max-width:100%;height:100%;padding:1rem .75rem;overflow:hidden;color:rgba(var(--bs-body-color-rgb),.65);text-align:start;text-overflow:ellipsis;white-space:nowrap;pointer-events:none;border:var(--bs-border-width) solid transparent;transform-origin:0 0;transition:opacity .1s ease-in-out,transform .1s ease-in-out}@media (prefers-reduced-motion:reduce){.form-floating>label{transition:none}}.form-floating>.form-control,.form-floating>.form-control-plaintext{padding:1rem .75rem}.form-floating>.form-control-plaintext::placeholder,.form-floating>.form-control::placeholder{color:transparent}.form-floating>.form-control-plaintext:focus,.form-floating>.form-control-plaintext:not(:placeholder-shown),.form-floating>.form-control:focus,.form-floating>.form-control:not(:placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control-plaintext:-webkit-autofill,.form-floating>.form-control:-webkit-autofill{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-select{padding-top:1.625rem;padding-bottom:.625rem;padding-left:.75rem}.form-floating>.form-control-plaintext~label,.form-floating>.form-control:focus~label,.form-floating>.form-control:not(:placeholder-shown)~label,.form-floating>.form-select~label{transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control:-webkit-autofill~label{transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>textarea:focus~label::after,.form-floating>textarea:not(:placeholder-shown)~label::after{position:absolute;inset:1rem 0.375rem;z-index:-1;height:1.5em;content:"";background-color:var(--bs-body-bg);border-radius:var(--bs-border-radius)}.form-floating>textarea:disabled~label::after{background-color:var(--bs-secondary-bg)}.form-floating>.form-control-plaintext~label{border-width:var(--bs-border-width) 0}.form-floating>.form-control:disabled~label,.form-floating>:disabled~label{color:#6c757d}.input-group{position:relative;display:flex;flex-wrap:wrap;align-items:stretch;width:100%}.input-group>.form-control,.input-group>.form-floating,.input-group>.form-select{position:relative;flex:1 1 auto;width:1%;min-width:0}.input-group>.form-control:focus,.input-group>.form-floating:focus-within,.input-group>.form-select:focus{z-index:5}.input-group .btn{position:relative;z-index:2}.input-group .btn:focus{z-index:5}.input-group-text{display:flex;align-items:center;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);text-align:center;white-space:nowrap;background-color:var(--bs-tertiary-bg);border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius)}.input-group-lg>.btn,.input-group-lg>.form-control,.input-group-lg>.form-select,.input-group-lg>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;border-radius:var(--bs-border-radius-lg)}.input-group-sm>.btn,.input-group-sm>.form-control,.input-group-sm>.form-select,.input-group-sm>.input-group-text{padding:.25rem .5rem;font-size:.875rem;border-radius:var(--bs-border-radius-sm)}.input-group-lg>.form-select,.input-group-sm>.form-select{padding-right:3rem}.input-group:not(.has-validation)>.dropdown-toggle:nth-last-child(n+3),.input-group:not(.has-validation)>.form-floating:not(:last-child)>.form-control,.input-group:not(.has-validation)>.form-floating:not(:last-child)>.form-select,.input-group:not(.has-validation)>:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating){border-top-right-radius:0;border-bottom-right-radius:0}.input-group.has-validation>.dropdown-toggle:nth-last-child(n+4),.input-group.has-validation>.form-floating:nth-last-child(n+3)>.form-control,.input-group.has-validation>.form-floating:nth-last-child(n+3)>.form-select,.input-group.has-validation>:nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback){margin-left:calc(-1 * var(--bs-border-width));border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.form-floating:not(:first-child)>.form-control,.input-group>.form-floating:not(:first-child)>.form-select{border-top-left-radius:0;border-bottom-left-radius:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:var(--bs-form-valid-color)}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:var(--bs-success);border-radius:var(--bs-border-radius)}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:var(--bs-form-valid-border-color);padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:var(--bs-form-valid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb),.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.form-select.is-valid,.was-validated .form-select:valid{border-color:var(--bs-form-valid-border-color)}.form-select.is-valid:not([multiple]):not([size]),.form-select.is-valid:not([multiple])[size="1"],.was-validated .form-select:valid:not([multiple]):not([size]),.was-validated .form-select:valid:not([multiple])[size="1"]{--bs-form-select-bg-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1'/%3e%3c/svg%3e");padding-right:4.125rem;background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-valid:focus,.was-validated .form-select:valid:focus{border-color:var(--bs-form-valid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb),.25)}.form-control-color.is-valid,.was-validated .form-control-color:valid{width:calc(3rem + calc(1.5em + .75rem))}.form-check-input.is-valid,.was-validated .form-check-input:valid{border-color:var(--bs-form-valid-border-color)}.form-check-input.is-valid:checked,.was-validated .form-check-input:valid:checked{background-color:var(--bs-form-valid-color)}.form-check-input.is-valid:focus,.was-validated .form-check-input:valid:focus{box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb),.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:var(--bs-form-valid-color)}.form-check-inline .form-check-input~.valid-feedback{margin-left:.5em}.input-group>.form-control:not(:focus).is-valid,.input-group>.form-floating:not(:focus-within).is-valid,.input-group>.form-select:not(:focus).is-valid,.was-validated .input-group>.form-control:not(:focus):valid,.was-validated .input-group>.form-floating:not(:focus-within):valid,.was-validated .input-group>.form-select:not(:focus):valid{z-index:3}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:var(--bs-form-invalid-color)}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:var(--bs-danger);border-radius:var(--bs-border-radius)}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:var(--bs-form-invalid-border-color);padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:var(--bs-form-invalid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb),.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.form-select.is-invalid,.was-validated .form-select:invalid{border-color:var(--bs-form-invalid-border-color)}.form-select.is-invalid:not([multiple]):not([size]),.form-select.is-invalid:not([multiple])[size="1"],.was-validated .form-select:invalid:not([multiple]):not([size]),.was-validated .form-select:invalid:not([multiple])[size="1"]{--bs-form-select-bg-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");padding-right:4.125rem;background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-invalid:focus,.was-validated .form-select:invalid:focus{border-color:var(--bs-form-invalid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb),.25)}.form-control-color.is-invalid,.was-validated .form-control-color:invalid{width:calc(3rem + calc(1.5em + .75rem))}.form-check-input.is-invalid,.was-validated .form-check-input:invalid{border-color:var(--bs-form-invalid-border-color)}.form-check-input.is-invalid:checked,.was-validated .form-check-input:invalid:checked{background-color:var(--bs-form-invalid-color)}.form-check-input.is-invalid:focus,.was-validated .form-check-input:invalid:focus{box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb),.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:var(--bs-form-invalid-color)}.form-check-inline .form-check-input~.invalid-feedback{margin-left:.5em}.input-group>.form-control:not(:focus).is-invalid,.input-group>.form-floating:not(:focus-within).is-invalid,.input-group>.form-select:not(:focus).is-invalid,.was-validated .input-group>.form-control:not(:focus):invalid,.was-validated .input-group>.form-floating:not(:focus-within):invalid,.was-validated .input-group>.form-select:not(:focus):invalid{z-index:4}.btn{--bs-btn-padding-x:0.75rem;--bs-btn-padding-y:0.375rem;--bs-btn-font-family: ;--bs-btn-font-size:1rem;--bs-btn-font-weight:400;--bs-btn-line-height:1.5;--bs-btn-color:var(--bs-body-color);--bs-btn-bg:transparent;--bs-btn-border-width:var(--bs-border-width);--bs-btn-border-color:transparent;--bs-btn-border-radius:var(--bs-border-radius);--bs-btn-hover-border-color:transparent;--bs-btn-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.15),0 1px 1px rgba(0, 0, 0, 0.075);--bs-btn-disabled-opacity:0.65;--bs-btn-focus-box-shadow:0 0 0 0.25rem rgba(var(--bs-btn-focus-shadow-rgb), .5);display:inline-block;padding:var(--bs-btn-padding-y) var(--bs-btn-padding-x);font-family:var(--bs-btn-font-family);font-size:var(--bs-btn-font-size);font-weight:var(--bs-btn-font-weight);line-height:var(--bs-btn-line-height);color:var(--bs-btn-color);text-align:center;text-decoration:none;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;border:var(--bs-btn-border-width) solid var(--bs-btn-border-color);border-radius:var(--bs-btn-border-radius);background-color:var(--bs-btn-bg);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color)}.btn-check+.btn:hover{color:var(--bs-btn-color);background-color:var(--bs-btn-bg);border-color:var(--bs-btn-border-color)}.btn:focus-visible{color:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color);outline:0;box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:focus-visible+.btn{border-color:var(--bs-btn-hover-border-color);outline:0;box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:checked+.btn,.btn.active,.btn.show,.btn:first-child:active,:not(.btn-check)+.btn:active{color:var(--bs-btn-active-color);background-color:var(--bs-btn-active-bg);border-color:var(--bs-btn-active-border-color)}.btn-check:checked+.btn:focus-visible,.btn.active:focus-visible,.btn.show:focus-visible,.btn:first-child:active:focus-visible,:not(.btn-check)+.btn:active:focus-visible{box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:checked:focus-visible+.btn{box-shadow:var(--bs-btn-focus-box-shadow)}.btn.disabled,.btn:disabled,fieldset:disabled .btn{color:var(--bs-btn-disabled-color);pointer-events:none;background-color:var(--bs-btn-disabled-bg);border-color:var(--bs-btn-disabled-border-color);opacity:var(--bs-btn-disabled-opacity)}.btn-primary{--bs-btn-color:#fff;--bs-btn-bg:#0d6efd;--bs-btn-border-color:#0d6efd;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#0b5ed7;--bs-btn-hover-border-color:#0a58ca;--bs-btn-focus-shadow-rgb:49,132,253;--bs-btn-active-color:#fff;--bs-btn-active-bg:#0a58ca;--bs-btn-active-border-color:#0a53be;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#0d6efd;--bs-btn-disabled-border-color:#0d6efd}.btn-secondary{--bs-btn-color:#fff;--bs-btn-bg:#6c757d;--bs-btn-border-color:#6c757d;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#5c636a;--bs-btn-hover-border-color:#565e64;--bs-btn-focus-shadow-rgb:130,138,145;--bs-btn-active-color:#fff;--bs-btn-active-bg:#565e64;--bs-btn-active-border-color:#51585e;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#6c757d;--bs-btn-disabled-border-color:#6c757d}.btn-success{--bs-btn-color:#fff;--bs-btn-bg:#198754;--bs-btn-border-color:#198754;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#157347;--bs-btn-hover-border-color:#146c43;--bs-btn-focus-shadow-rgb:60,153,110;--bs-btn-active-color:#fff;--bs-btn-active-bg:#146c43;--bs-btn-active-border-color:#13653f;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#198754;--bs-btn-disabled-border-color:#198754}.btn-info{--bs-btn-color:#000;--bs-btn-bg:#0dcaf0;--bs-btn-border-color:#0dcaf0;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#31d2f2;--bs-btn-hover-border-color:#25cff2;--bs-btn-focus-shadow-rgb:11,172,204;--bs-btn-active-color:#000;--bs-btn-active-bg:#3dd5f3;--bs-btn-active-border-color:#25cff2;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#0dcaf0;--bs-btn-disabled-border-color:#0dcaf0}.btn-warning{--bs-btn-color:#000;--bs-btn-bg:#ffc107;--bs-btn-border-color:#ffc107;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#ffca2c;--bs-btn-hover-border-color:#ffc720;--bs-btn-focus-shadow-rgb:217,164,6;--bs-btn-active-color:#000;--bs-btn-active-bg:#ffcd39;--bs-btn-active-border-color:#ffc720;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#ffc107;--bs-btn-disabled-border-color:#ffc107}.btn-danger{--bs-btn-color:#fff;--bs-btn-bg:#dc3545;--bs-btn-border-color:#dc3545;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#bb2d3b;--bs-btn-hover-border-color:#b02a37;--bs-btn-focus-shadow-rgb:225,83,97;--bs-btn-active-color:#fff;--bs-btn-active-bg:#b02a37;--bs-btn-active-border-color:#a52834;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#dc3545;--bs-btn-disabled-border-color:#dc3545}.btn-light{--bs-btn-color:#000;--bs-btn-bg:#f8f9fa;--bs-btn-border-color:#f8f9fa;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#d3d4d5;--bs-btn-hover-border-color:#c6c7c8;--bs-btn-focus-shadow-rgb:211,212,213;--bs-btn-active-color:#000;--bs-btn-active-bg:#c6c7c8;--bs-btn-active-border-color:#babbbc;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#f8f9fa;--bs-btn-disabled-border-color:#f8f9fa}.btn-dark{--bs-btn-color:#fff;--bs-btn-bg:#212529;--bs-btn-border-color:#212529;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#424649;--bs-btn-hover-border-color:#373b3e;--bs-btn-focus-shadow-rgb:66,70,73;--bs-btn-active-color:#fff;--bs-btn-active-bg:#4d5154;--bs-btn-active-border-color:#373b3e;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#212529;--bs-btn-disabled-border-color:#212529}.btn-outline-primary{--bs-btn-color:#0d6efd;--bs-btn-border-color:#0d6efd;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#0d6efd;--bs-btn-hover-border-color:#0d6efd;--bs-btn-focus-shadow-rgb:13,110,253;--bs-btn-active-color:#fff;--bs-btn-active-bg:#0d6efd;--bs-btn-active-border-color:#0d6efd;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#0d6efd;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#0d6efd;--bs-gradient:none}.btn-outline-secondary{--bs-btn-color:#6c757d;--bs-btn-border-color:#6c757d;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#6c757d;--bs-btn-hover-border-color:#6c757d;--bs-btn-focus-shadow-rgb:108,117,125;--bs-btn-active-color:#fff;--bs-btn-active-bg:#6c757d;--bs-btn-active-border-color:#6c757d;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#6c757d;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#6c757d;--bs-gradient:none}.btn-outline-success{--bs-btn-color:#198754;--bs-btn-border-color:#198754;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#198754;--bs-btn-hover-border-color:#198754;--bs-btn-focus-shadow-rgb:25,135,84;--bs-btn-active-color:#fff;--bs-btn-active-bg:#198754;--bs-btn-active-border-color:#198754;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#198754;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#198754;--bs-gradient:none}.btn-outline-info{--bs-btn-color:#0dcaf0;--bs-btn-border-color:#0dcaf0;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#0dcaf0;--bs-btn-hover-border-color:#0dcaf0;--bs-btn-focus-shadow-rgb:13,202,240;--bs-btn-active-color:#000;--bs-btn-active-bg:#0dcaf0;--bs-btn-active-border-color:#0dcaf0;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#0dcaf0;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#0dcaf0;--bs-gradient:none}.btn-outline-warning{--bs-btn-color:#ffc107;--bs-btn-border-color:#ffc107;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#ffc107;--bs-btn-hover-border-color:#ffc107;--bs-btn-focus-shadow-rgb:255,193,7;--bs-btn-active-color:#000;--bs-btn-active-bg:#ffc107;--bs-btn-active-border-color:#ffc107;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#ffc107;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#ffc107;--bs-gradient:none}.btn-outline-danger{--bs-btn-color:#dc3545;--bs-btn-border-color:#dc3545;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#dc3545;--bs-btn-hover-border-color:#dc3545;--bs-btn-focus-shadow-rgb:220,53,69;--bs-btn-active-color:#fff;--bs-btn-active-bg:#dc3545;--bs-btn-active-border-color:#dc3545;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#dc3545;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#dc3545;--bs-gradient:none}.btn-outline-light{--bs-btn-color:#f8f9fa;--bs-btn-border-color:#f8f9fa;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#f8f9fa;--bs-btn-hover-border-color:#f8f9fa;--bs-btn-focus-shadow-rgb:248,249,250;--bs-btn-active-color:#000;--bs-btn-active-bg:#f8f9fa;--bs-btn-active-border-color:#f8f9fa;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#f8f9fa;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#f8f9fa;--bs-gradient:none}.btn-outline-dark{--bs-btn-color:#212529;--bs-btn-border-color:#212529;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#212529;--bs-btn-hover-border-color:#212529;--bs-btn-focus-shadow-rgb:33,37,41;--bs-btn-active-color:#fff;--bs-btn-active-bg:#212529;--bs-btn-active-border-color:#212529;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#212529;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#212529;--bs-gradient:none}.btn-link{--bs-btn-font-weight:400;--bs-btn-color:var(--bs-link-color);--bs-btn-bg:transparent;--bs-btn-border-color:transparent;--bs-btn-hover-color:var(--bs-link-hover-color);--bs-btn-hover-border-color:transparent;--bs-btn-active-color:var(--bs-link-hover-color);--bs-btn-active-border-color:transparent;--bs-btn-disabled-color:#6c757d;--bs-btn-disabled-border-color:transparent;--bs-btn-box-shadow:0 0 0 #000;--bs-btn-focus-shadow-rgb:49,132,253;text-decoration:underline}.btn-link:focus-visible{color:var(--bs-btn-color)}.btn-link:hover{color:var(--bs-btn-hover-color)}.btn-group-lg>.btn,.btn-lg{--bs-btn-padding-y:0.5rem;--bs-btn-padding-x:1rem;--bs-btn-font-size:1.25rem;--bs-btn-border-radius:var(--bs-border-radius-lg)}.btn-group-sm>.btn,.btn-sm{--bs-btn-padding-y:0.25rem;--bs-btn-padding-x:0.5rem;--bs-btn-font-size:0.875rem;--bs-btn-border-radius:var(--bs-border-radius-sm)}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.collapsing.collapse-horizontal{width:0;height:auto;transition:width .35s ease}@media (prefers-reduced-motion:reduce){.collapsing.collapse-horizontal{transition:none}}.dropdown,.dropdown-center,.dropend,.dropstart,.dropup,.dropup-center{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{--bs-dropdown-zindex:1000;--bs-dropdown-min-width:10rem;--bs-dropdown-padding-x:0;--bs-dropdown-padding-y:0.5rem;--bs-dropdown-spacer:0.125rem;--bs-dropdown-font-size:1rem;--bs-dropdown-color:var(--bs-body-color);--bs-dropdown-bg:var(--bs-body-bg);--bs-dropdown-border-color:var(--bs-border-color-translucent);--bs-dropdown-border-radius:var(--bs-border-radius);--bs-dropdown-border-width:var(--bs-border-width);--bs-dropdown-inner-border-radius:calc(var(--bs-border-radius) - var(--bs-border-width));--bs-dropdown-divider-bg:var(--bs-border-color-translucent);--bs-dropdown-divider-margin-y:0.5rem;--bs-dropdown-box-shadow:var(--bs-box-shadow);--bs-dropdown-link-color:var(--bs-body-color);--bs-dropdown-link-hover-color:var(--bs-body-color);--bs-dropdown-link-hover-bg:var(--bs-tertiary-bg);--bs-dropdown-link-active-color:#fff;--bs-dropdown-link-active-bg:#0d6efd;--bs-dropdown-link-disabled-color:var(--bs-tertiary-color);--bs-dropdown-item-padding-x:1rem;--bs-dropdown-item-padding-y:0.25rem;--bs-dropdown-header-color:#6c757d;--bs-dropdown-header-padding-x:1rem;--bs-dropdown-header-padding-y:0.5rem;position:absolute;z-index:var(--bs-dropdown-zindex);display:none;min-width:var(--bs-dropdown-min-width);padding:var(--bs-dropdown-padding-y) var(--bs-dropdown-padding-x);margin:0;font-size:var(--bs-dropdown-font-size);color:var(--bs-dropdown-color);text-align:left;list-style:none;background-color:var(--bs-dropdown-bg);background-clip:padding-box;border:var(--bs-dropdown-border-width) solid var(--bs-dropdown-border-color);border-radius:var(--bs-dropdown-border-radius)}.dropdown-menu[data-bs-popper]{top:100%;left:0;margin-top:var(--bs-dropdown-spacer)}.dropdown-menu-start{--bs-position:start}.dropdown-menu-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-end{--bs-position:end}.dropdown-menu-end[data-bs-popper]{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-start{--bs-position:start}.dropdown-menu-sm-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-sm-end{--bs-position:end}.dropdown-menu-sm-end[data-bs-popper]{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-start{--bs-position:start}.dropdown-menu-md-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-md-end{--bs-position:end}.dropdown-menu-md-end[data-bs-popper]{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-start{--bs-position:start}.dropdown-menu-lg-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-lg-end{--bs-position:end}.dropdown-menu-lg-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-start{--bs-position:start}.dropdown-menu-xl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xl-end{--bs-position:end}.dropdown-menu-xl-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1400px){.dropdown-menu-xxl-start{--bs-position:start}.dropdown-menu-xxl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xxl-end{--bs-position:end}.dropdown-menu-xxl-end[data-bs-popper]{right:0;left:auto}}.dropup .dropdown-menu[data-bs-popper]{top:auto;bottom:100%;margin-top:0;margin-bottom:var(--bs-dropdown-spacer)}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-menu[data-bs-popper]{top:0;right:auto;left:100%;margin-top:0;margin-left:var(--bs-dropdown-spacer)}.dropend .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropend .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-toggle::after{vertical-align:0}.dropstart .dropdown-menu[data-bs-popper]{top:0;right:100%;left:auto;margin-top:0;margin-right:var(--bs-dropdown-spacer)}.dropstart .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropstart .dropdown-toggle::after{display:none}.dropstart .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropstart .dropdown-toggle:empty::after{margin-left:0}.dropstart .dropdown-toggle::before{vertical-align:0}.dropdown-divider{height:0;margin:var(--bs-dropdown-divider-margin-y) 0;overflow:hidden;border-top:1px solid var(--bs-dropdown-divider-bg);opacity:1}.dropdown-item{display:block;width:100%;padding:var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);clear:both;font-weight:400;color:var(--bs-dropdown-link-color);text-align:inherit;text-decoration:none;white-space:nowrap;background-color:transparent;border:0;border-radius:var(--bs-dropdown-item-border-radius,0)}.dropdown-item:focus,.dropdown-item:hover{color:var(--bs-dropdown-link-hover-color);background-color:var(--bs-dropdown-link-hover-bg)}.dropdown-item.active,.dropdown-item:active{color:var(--bs-dropdown-link-active-color);text-decoration:none;background-color:var(--bs-dropdown-link-active-bg)}.dropdown-item.disabled,.dropdown-item:disabled{color:var(--bs-dropdown-link-disabled-color);pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:var(--bs-dropdown-header-padding-y) var(--bs-dropdown-header-padding-x);margin-bottom:0;font-size:.875rem;color:var(--bs-dropdown-header-color);white-space:nowrap}.dropdown-item-text{display:block;padding:var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);color:var(--bs-dropdown-link-color)}.dropdown-menu-dark{--bs-dropdown-color:#dee2e6;--bs-dropdown-bg:#343a40;--bs-dropdown-border-color:var(--bs-border-color-translucent);--bs-dropdown-box-shadow: ;--bs-dropdown-link-color:#dee2e6;--bs-dropdown-link-hover-color:#fff;--bs-dropdown-divider-bg:var(--bs-border-color-translucent);--bs-dropdown-link-hover-bg:rgba(255, 255, 255, 0.15);--bs-dropdown-link-active-color:#fff;--bs-dropdown-link-active-bg:#0d6efd;--bs-dropdown-link-disabled-color:#adb5bd;--bs-dropdown-header-color:#adb5bd}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;flex:1 1 auto}.btn-group-vertical>.btn-check:checked+.btn,.btn-group-vertical>.btn-check:focus+.btn,.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn-check:checked+.btn,.btn-group>.btn-check:focus+.btn,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:1}.btn-toolbar{display:flex;flex-wrap:wrap;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group{border-radius:var(--bs-border-radius)}.btn-group>.btn-group:not(:first-child),.btn-group>:not(.btn-check:first-child)+.btn{margin-left:calc(-1 * var(--bs-border-width))}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn.dropdown-toggle-split:first-child,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:nth-child(n+3),.btn-group>:not(.btn-check)+.btn{border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropend .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropstart .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{flex-direction:column;align-items:flex-start;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:calc(-1 * var(--bs-border-width))}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:nth-child(n+3),.btn-group-vertical>:not(.btn-check)+.btn{border-top-left-radius:0;border-top-right-radius:0}.nav{--bs-nav-link-padding-x:1rem;--bs-nav-link-padding-y:0.5rem;--bs-nav-link-font-weight: ;--bs-nav-link-color:var(--bs-link-color);--bs-nav-link-hover-color:var(--bs-link-hover-color);--bs-nav-link-disabled-color:var(--bs-secondary-color);display:flex;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:var(--bs-nav-link-padding-y) var(--bs-nav-link-padding-x);font-size:var(--bs-nav-link-font-size);font-weight:var(--bs-nav-link-font-weight);color:var(--bs-nav-link-color);text-decoration:none;background:0 0;border:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out}@media (prefers-reduced-motion:reduce){.nav-link{transition:none}}.nav-link:focus,.nav-link:hover{color:var(--bs-nav-link-hover-color)}.nav-link:focus-visible{outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.nav-link.disabled,.nav-link:disabled{color:var(--bs-nav-link-disabled-color);pointer-events:none;cursor:default}.nav-tabs{--bs-nav-tabs-border-width:var(--bs-border-width);--bs-nav-tabs-border-color:var(--bs-border-color);--bs-nav-tabs-border-radius:var(--bs-border-radius);--bs-nav-tabs-link-hover-border-color:var(--bs-secondary-bg) var(--bs-secondary-bg) var(--bs-border-color);--bs-nav-tabs-link-active-color:var(--bs-emphasis-color);--bs-nav-tabs-link-active-bg:var(--bs-body-bg);--bs-nav-tabs-link-active-border-color:var(--bs-border-color) var(--bs-border-color) var(--bs-body-bg);border-bottom:var(--bs-nav-tabs-border-width) solid var(--bs-nav-tabs-border-color)}.nav-tabs .nav-link{margin-bottom:calc(-1 * var(--bs-nav-tabs-border-width));border:var(--bs-nav-tabs-border-width) solid transparent;border-top-left-radius:var(--bs-nav-tabs-border-radius);border-top-right-radius:var(--bs-nav-tabs-border-radius)}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{isolation:isolate;border-color:var(--bs-nav-tabs-link-hover-border-color)}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:var(--bs-nav-tabs-link-active-color);background-color:var(--bs-nav-tabs-link-active-bg);border-color:var(--bs-nav-tabs-link-active-border-color)}.nav-tabs .dropdown-menu{margin-top:calc(-1 * var(--bs-nav-tabs-border-width));border-top-left-radius:0;border-top-right-radius:0}.nav-pills{--bs-nav-pills-border-radius:var(--bs-border-radius);--bs-nav-pills-link-active-color:#fff;--bs-nav-pills-link-active-bg:#0d6efd}.nav-pills .nav-link{border-radius:var(--bs-nav-pills-border-radius)}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:var(--bs-nav-pills-link-active-color);background-color:var(--bs-nav-pills-link-active-bg)}.nav-underline{--bs-nav-underline-gap:1rem;--bs-nav-underline-border-width:0.125rem;--bs-nav-underline-link-active-color:var(--bs-emphasis-color);gap:var(--bs-nav-underline-gap)}.nav-underline .nav-link{padding-right:0;padding-left:0;border-bottom:var(--bs-nav-underline-border-width) solid transparent}.nav-underline .nav-link:focus,.nav-underline .nav-link:hover{border-bottom-color:currentcolor}.nav-underline .nav-link.active,.nav-underline .show>.nav-link{font-weight:700;color:var(--bs-nav-underline-link-active-color);border-bottom-color:currentcolor}.nav-fill .nav-item,.nav-fill>.nav-link{flex:1 1 auto;text-align:center}.nav-justified .nav-item,.nav-justified>.nav-link{flex-grow:1;flex-basis:0;text-align:center}.nav-fill .nav-item .nav-link,.nav-justified .nav-item .nav-link{width:100%}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{--bs-navbar-padding-x:0;--bs-navbar-padding-y:0.5rem;--bs-navbar-color:rgba(var(--bs-emphasis-color-rgb), 0.65);--bs-navbar-hover-color:rgba(var(--bs-emphasis-color-rgb), 0.8);--bs-navbar-disabled-color:rgba(var(--bs-emphasis-color-rgb), 0.3);--bs-navbar-active-color:rgba(var(--bs-emphasis-color-rgb), 1);--bs-navbar-brand-padding-y:0.3125rem;--bs-navbar-brand-margin-end:1rem;--bs-navbar-brand-font-size:1.25rem;--bs-navbar-brand-color:rgba(var(--bs-emphasis-color-rgb), 1);--bs-navbar-brand-hover-color:rgba(var(--bs-emphasis-color-rgb), 1);--bs-navbar-nav-link-padding-x:0.5rem;--bs-navbar-toggler-padding-y:0.25rem;--bs-navbar-toggler-padding-x:0.75rem;--bs-navbar-toggler-font-size:1.25rem;--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%2833, 37, 41, 0.75%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");--bs-navbar-toggler-border-color:rgba(var(--bs-emphasis-color-rgb), 0.15);--bs-navbar-toggler-border-radius:var(--bs-border-radius);--bs-navbar-toggler-focus-width:0.25rem;--bs-navbar-toggler-transition:box-shadow 0.15s ease-in-out;position:relative;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;padding:var(--bs-navbar-padding-y) var(--bs-navbar-padding-x)}.navbar>.container,.navbar>.container-fluid,.navbar>.container-lg,.navbar>.container-md,.navbar>.container-sm,.navbar>.container-xl,.navbar>.container-xxl{display:flex;flex-wrap:inherit;align-items:center;justify-content:space-between}.navbar-brand{padding-top:var(--bs-navbar-brand-padding-y);padding-bottom:var(--bs-navbar-brand-padding-y);margin-right:var(--bs-navbar-brand-margin-end);font-size:var(--bs-navbar-brand-font-size);color:var(--bs-navbar-brand-color);text-decoration:none;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{color:var(--bs-navbar-brand-hover-color)}.navbar-nav{--bs-nav-link-padding-x:0;--bs-nav-link-padding-y:0.5rem;--bs-nav-link-font-weight: ;--bs-nav-link-color:var(--bs-navbar-color);--bs-nav-link-hover-color:var(--bs-navbar-hover-color);--bs-nav-link-disabled-color:var(--bs-navbar-disabled-color);display:flex;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link.active,.navbar-nav .nav-link.show{color:var(--bs-navbar-active-color)}.navbar-nav .dropdown-menu{position:static}.navbar-text{padding-top:.5rem;padding-bottom:.5rem;color:var(--bs-navbar-color)}.navbar-text a,.navbar-text a:focus,.navbar-text a:hover{color:var(--bs-navbar-active-color)}.navbar-collapse{flex-grow:1;flex-basis:100%;align-items:center}.navbar-toggler{padding:var(--bs-navbar-toggler-padding-y) var(--bs-navbar-toggler-padding-x);font-size:var(--bs-navbar-toggler-font-size);line-height:1;color:var(--bs-navbar-color);background-color:transparent;border:var(--bs-border-width) solid var(--bs-navbar-toggler-border-color);border-radius:var(--bs-navbar-toggler-border-radius);transition:var(--bs-navbar-toggler-transition)}@media (prefers-reduced-motion:reduce){.navbar-toggler{transition:none}}.navbar-toggler:hover{text-decoration:none}.navbar-toggler:focus{text-decoration:none;outline:0;box-shadow:0 0 0 var(--bs-navbar-toggler-focus-width)}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;background-image:var(--bs-navbar-toggler-icon-bg);background-repeat:no-repeat;background-position:center;background-size:100%}.navbar-nav-scroll{max-height:var(--bs-scroll-height,75vh);overflow-y:auto}@media (min-width:576px){.navbar-expand-sm{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-sm .navbar-nav{flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-sm .navbar-nav-scroll{overflow:visible}.navbar-expand-sm .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}.navbar-expand-sm .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-sm .offcanvas .offcanvas-header{display:none}.navbar-expand-sm .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:768px){.navbar-expand-md{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-md .navbar-nav{flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-md .navbar-nav-scroll{overflow:visible}.navbar-expand-md .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}.navbar-expand-md .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-md .offcanvas .offcanvas-header{display:none}.navbar-expand-md .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:992px){.navbar-expand-lg{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-lg .navbar-nav{flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-lg .navbar-nav-scroll{overflow:visible}.navbar-expand-lg .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}.navbar-expand-lg .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-lg .offcanvas .offcanvas-header{display:none}.navbar-expand-lg .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:1200px){.navbar-expand-xl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xl .navbar-nav{flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-xl .navbar-nav-scroll{overflow:visible}.navbar-expand-xl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}.navbar-expand-xl .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-xl .offcanvas .offcanvas-header{display:none}.navbar-expand-xl .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:1400px){.navbar-expand-xxl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xxl .navbar-nav{flex-direction:row}.navbar-expand-xxl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xxl .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-xxl .navbar-nav-scroll{overflow:visible}.navbar-expand-xxl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xxl .navbar-toggler{display:none}.navbar-expand-xxl .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-xxl .offcanvas .offcanvas-header{display:none}.navbar-expand-xxl .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}.navbar-expand{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand .navbar-nav{flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand .navbar-nav-scroll{overflow:visible}.navbar-expand .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-expand .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand .offcanvas .offcanvas-header{display:none}.navbar-expand .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}.navbar-dark,.navbar[data-bs-theme=dark]{--bs-navbar-color:rgba(255, 255, 255, 0.55);--bs-navbar-hover-color:rgba(255, 255, 255, 0.75);--bs-navbar-disabled-color:rgba(255, 255, 255, 0.25);--bs-navbar-active-color:#fff;--bs-navbar-brand-color:#fff;--bs-navbar-brand-hover-color:#fff;--bs-navbar-toggler-border-color:rgba(255, 255, 255, 0.1);--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}[data-bs-theme=dark] .navbar-toggler-icon{--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.card{--bs-card-spacer-y:1rem;--bs-card-spacer-x:1rem;--bs-card-title-spacer-y:0.5rem;--bs-card-title-color: ;--bs-card-subtitle-color: ;--bs-card-border-width:var(--bs-border-width);--bs-card-border-color:var(--bs-border-color-translucent);--bs-card-border-radius:var(--bs-border-radius);--bs-card-box-shadow: ;--bs-card-inner-border-radius:calc(var(--bs-border-radius) - (var(--bs-border-width)));--bs-card-cap-padding-y:0.5rem;--bs-card-cap-padding-x:1rem;--bs-card-cap-bg:rgba(var(--bs-body-color-rgb), 0.03);--bs-card-cap-color: ;--bs-card-height: ;--bs-card-color: ;--bs-card-bg:var(--bs-body-bg);--bs-card-img-overlay-padding:1rem;--bs-card-group-margin:0.75rem;position:relative;display:flex;flex-direction:column;min-width:0;height:var(--bs-card-height);color:var(--bs-body-color);word-wrap:break-word;background-color:var(--bs-card-bg);background-clip:border-box;border:var(--bs-card-border-width) solid var(--bs-card-border-color);border-radius:var(--bs-card-border-radius)}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:var(--bs-card-inner-border-radius);border-top-right-radius:var(--bs-card-inner-border-radius)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:var(--bs-card-inner-border-radius);border-bottom-left-radius:var(--bs-card-inner-border-radius)}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{flex:1 1 auto;padding:var(--bs-card-spacer-y) var(--bs-card-spacer-x);color:var(--bs-card-color)}.card-title{margin-bottom:var(--bs-card-title-spacer-y);color:var(--bs-card-title-color)}.card-subtitle{margin-top:calc(-.5 * var(--bs-card-title-spacer-y));margin-bottom:0;color:var(--bs-card-subtitle-color)}.card-text:last-child{margin-bottom:0}.card-link+.card-link{margin-left:var(--bs-card-spacer-x)}.card-header{padding:var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);margin-bottom:0;color:var(--bs-card-cap-color);background-color:var(--bs-card-cap-bg);border-bottom:var(--bs-card-border-width) solid var(--bs-card-border-color)}.card-header:first-child{border-radius:var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius) 0 0}.card-footer{padding:var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);color:var(--bs-card-cap-color);background-color:var(--bs-card-cap-bg);border-top:var(--bs-card-border-width) solid var(--bs-card-border-color)}.card-footer:last-child{border-radius:0 0 var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius)}.card-header-tabs{margin-right:calc(-.5 * var(--bs-card-cap-padding-x));margin-bottom:calc(-1 * var(--bs-card-cap-padding-y));margin-left:calc(-.5 * var(--bs-card-cap-padding-x));border-bottom:0}.card-header-tabs .nav-link.active{background-color:var(--bs-card-bg);border-bottom-color:var(--bs-card-bg)}.card-header-pills{margin-right:calc(-.5 * var(--bs-card-cap-padding-x));margin-left:calc(-.5 * var(--bs-card-cap-padding-x))}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:var(--bs-card-img-overlay-padding);border-radius:var(--bs-card-inner-border-radius)}.card-img,.card-img-bottom,.card-img-top{width:100%}.card-img,.card-img-top{border-top-left-radius:var(--bs-card-inner-border-radius);border-top-right-radius:var(--bs-card-inner-border-radius)}.card-img,.card-img-bottom{border-bottom-right-radius:var(--bs-card-inner-border-radius);border-bottom-left-radius:var(--bs-card-inner-border-radius)}.card-group>.card{margin-bottom:var(--bs-card-group-margin)}@media (min-width:576px){.card-group{display:flex;flex-flow:row wrap}.card-group>.card{flex:1 0 0;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child)>.card-header,.card-group>.card:not(:last-child)>.card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child)>.card-footer,.card-group>.card:not(:last-child)>.card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child)>.card-header,.card-group>.card:not(:first-child)>.card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child)>.card-footer,.card-group>.card:not(:first-child)>.card-img-bottom{border-bottom-left-radius:0}}.accordion{--bs-accordion-color:var(--bs-body-color);--bs-accordion-bg:var(--bs-body-bg);--bs-accordion-transition:color 0.15s ease-in-out,background-color 0.15s ease-in-out,border-color 0.15s ease-in-out,box-shadow 0.15s ease-in-out,border-radius 0.15s ease;--bs-accordion-border-color:var(--bs-border-color);--bs-accordion-border-width:var(--bs-border-width);--bs-accordion-border-radius:var(--bs-border-radius);--bs-accordion-inner-border-radius:calc(var(--bs-border-radius) - (var(--bs-border-width)));--bs-accordion-btn-padding-x:1.25rem;--bs-accordion-btn-padding-y:1rem;--bs-accordion-btn-color:var(--bs-body-color);--bs-accordion-btn-bg:var(--bs-accordion-bg);--bs-accordion-btn-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23212529' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='m2 5 6 6 6-6'/%3e%3c/svg%3e");--bs-accordion-btn-icon-width:1.25rem;--bs-accordion-btn-icon-transform:rotate(-180deg);--bs-accordion-btn-icon-transition:transform 0.2s ease-in-out;--bs-accordion-btn-active-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23052c65' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='m2 5 6 6 6-6'/%3e%3c/svg%3e");--bs-accordion-btn-focus-box-shadow:0 0 0 0.25rem rgba(13, 110, 253, 0.25);--bs-accordion-body-padding-x:1.25rem;--bs-accordion-body-padding-y:1rem;--bs-accordion-active-color:var(--bs-primary-text-emphasis);--bs-accordion-active-bg:var(--bs-primary-bg-subtle)}.accordion-button{position:relative;display:flex;align-items:center;width:100%;padding:var(--bs-accordion-btn-padding-y) var(--bs-accordion-btn-padding-x);font-size:1rem;color:var(--bs-accordion-btn-color);text-align:left;background-color:var(--bs-accordion-btn-bg);border:0;border-radius:0;overflow-anchor:none;transition:var(--bs-accordion-transition)}@media (prefers-reduced-motion:reduce){.accordion-button{transition:none}}.accordion-button:not(.collapsed){color:var(--bs-accordion-active-color);background-color:var(--bs-accordion-active-bg);box-shadow:inset 0 calc(-1 * var(--bs-accordion-border-width)) 0 var(--bs-accordion-border-color)}.accordion-button:not(.collapsed)::after{background-image:var(--bs-accordion-btn-active-icon);transform:var(--bs-accordion-btn-icon-transform)}.accordion-button::after{flex-shrink:0;width:var(--bs-accordion-btn-icon-width);height:var(--bs-accordion-btn-icon-width);margin-left:auto;content:"";background-image:var(--bs-accordion-btn-icon);background-repeat:no-repeat;background-size:var(--bs-accordion-btn-icon-width);transition:var(--bs-accordion-btn-icon-transition)}@media (prefers-reduced-motion:reduce){.accordion-button::after{transition:none}}.accordion-button:hover{z-index:2}.accordion-button:focus{z-index:3;outline:0;box-shadow:var(--bs-accordion-btn-focus-box-shadow)}.accordion-header{margin-bottom:0}.accordion-item{color:var(--bs-accordion-color);background-color:var(--bs-accordion-bg);border:var(--bs-accordion-border-width) solid var(--bs-accordion-border-color)}.accordion-item:first-of-type{border-top-left-radius:var(--bs-accordion-border-radius);border-top-right-radius:var(--bs-accordion-border-radius)}.accordion-item:first-of-type>.accordion-header .accordion-button{border-top-left-radius:var(--bs-accordion-inner-border-radius);border-top-right-radius:var(--bs-accordion-inner-border-radius)}.accordion-item:not(:first-of-type){border-top:0}.accordion-item:last-of-type{border-bottom-right-radius:var(--bs-accordion-border-radius);border-bottom-left-radius:var(--bs-accordion-border-radius)}.accordion-item:last-of-type>.accordion-header .accordion-button.collapsed{border-bottom-right-radius:var(--bs-accordion-inner-border-radius);border-bottom-left-radius:var(--bs-accordion-inner-border-radius)}.accordion-item:last-of-type>.accordion-collapse{border-bottom-right-radius:var(--bs-accordion-border-radius);border-bottom-left-radius:var(--bs-accordion-border-radius)}.accordion-body{padding:var(--bs-accordion-body-padding-y) var(--bs-accordion-body-padding-x)}.accordion-flush>.accordion-item{border-right:0;border-left:0;border-radius:0}.accordion-flush>.accordion-item:first-child{border-top:0}.accordion-flush>.accordion-item:last-child{border-bottom:0}.accordion-flush>.accordion-item>.accordion-collapse,.accordion-flush>.accordion-item>.accordion-header .accordion-button,.accordion-flush>.accordion-item>.accordion-header .accordion-button.collapsed{border-radius:0}[data-bs-theme=dark] .accordion-button::after{--bs-accordion-btn-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708'/%3e%3c/svg%3e");--bs-accordion-btn-active-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708'/%3e%3c/svg%3e")}.breadcrumb{--bs-breadcrumb-padding-x:0;--bs-breadcrumb-padding-y:0;--bs-breadcrumb-margin-bottom:1rem;--bs-breadcrumb-bg: ;--bs-breadcrumb-border-radius: ;--bs-breadcrumb-divider-color:var(--bs-secondary-color);--bs-breadcrumb-item-padding-x:0.5rem;--bs-breadcrumb-item-active-color:var(--bs-secondary-color);display:flex;flex-wrap:wrap;padding:var(--bs-breadcrumb-padding-y) var(--bs-breadcrumb-padding-x);margin-bottom:var(--bs-breadcrumb-margin-bottom);font-size:var(--bs-breadcrumb-font-size);list-style:none;background-color:var(--bs-breadcrumb-bg);border-radius:var(--bs-breadcrumb-border-radius)}.breadcrumb-item+.breadcrumb-item{padding-left:var(--bs-breadcrumb-item-padding-x)}.breadcrumb-item+.breadcrumb-item::before{float:left;padding-right:var(--bs-breadcrumb-item-padding-x);color:var(--bs-breadcrumb-divider-color);content:var(--bs-breadcrumb-divider, "/")}.breadcrumb-item.active{color:var(--bs-breadcrumb-item-active-color)}.pagination{--bs-pagination-padding-x:0.75rem;--bs-pagination-padding-y:0.375rem;--bs-pagination-font-size:1rem;--bs-pagination-color:var(--bs-link-color);--bs-pagination-bg:var(--bs-body-bg);--bs-pagination-border-width:var(--bs-border-width);--bs-pagination-border-color:var(--bs-border-color);--bs-pagination-border-radius:var(--bs-border-radius);--bs-pagination-hover-color:var(--bs-link-hover-color);--bs-pagination-hover-bg:var(--bs-tertiary-bg);--bs-pagination-hover-border-color:var(--bs-border-color);--bs-pagination-focus-color:var(--bs-link-hover-color);--bs-pagination-focus-bg:var(--bs-secondary-bg);--bs-pagination-focus-box-shadow:0 0 0 0.25rem rgba(13, 110, 253, 0.25);--bs-pagination-active-color:#fff;--bs-pagination-active-bg:#0d6efd;--bs-pagination-active-border-color:#0d6efd;--bs-pagination-disabled-color:var(--bs-secondary-color);--bs-pagination-disabled-bg:var(--bs-secondary-bg);--bs-pagination-disabled-border-color:var(--bs-border-color);display:flex;padding-left:0;list-style:none}.page-link{position:relative;display:block;padding:var(--bs-pagination-padding-y) var(--bs-pagination-padding-x);font-size:var(--bs-pagination-font-size);color:var(--bs-pagination-color);text-decoration:none;background-color:var(--bs-pagination-bg);border:var(--bs-pagination-border-width) solid var(--bs-pagination-border-color);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.page-link{transition:none}}.page-link:hover{z-index:2;color:var(--bs-pagination-hover-color);background-color:var(--bs-pagination-hover-bg);border-color:var(--bs-pagination-hover-border-color)}.page-link:focus{z-index:3;color:var(--bs-pagination-focus-color);background-color:var(--bs-pagination-focus-bg);outline:0;box-shadow:var(--bs-pagination-focus-box-shadow)}.active>.page-link,.page-link.active{z-index:3;color:var(--bs-pagination-active-color);background-color:var(--bs-pagination-active-bg);border-color:var(--bs-pagination-active-border-color)}.disabled>.page-link,.page-link.disabled{color:var(--bs-pagination-disabled-color);pointer-events:none;background-color:var(--bs-pagination-disabled-bg);border-color:var(--bs-pagination-disabled-border-color)}.page-item:not(:first-child) .page-link{margin-left:calc(-1 * var(--bs-border-width))}.page-item:first-child .page-link{border-top-left-radius:var(--bs-pagination-border-radius);border-bottom-left-radius:var(--bs-pagination-border-radius)}.page-item:last-child .page-link{border-top-right-radius:var(--bs-pagination-border-radius);border-bottom-right-radius:var(--bs-pagination-border-radius)}.pagination-lg{--bs-pagination-padding-x:1.5rem;--bs-pagination-padding-y:0.75rem;--bs-pagination-font-size:1.25rem;--bs-pagination-border-radius:var(--bs-border-radius-lg)}.pagination-sm{--bs-pagination-padding-x:0.5rem;--bs-pagination-padding-y:0.25rem;--bs-pagination-font-size:0.875rem;--bs-pagination-border-radius:var(--bs-border-radius-sm)}.badge{--bs-badge-padding-x:0.65em;--bs-badge-padding-y:0.35em;--bs-badge-font-size:0.75em;--bs-badge-font-weight:700;--bs-badge-color:#fff;--bs-badge-border-radius:var(--bs-border-radius);display:inline-block;padding:var(--bs-badge-padding-y) var(--bs-badge-padding-x);font-size:var(--bs-badge-font-size);font-weight:var(--bs-badge-font-weight);line-height:1;color:var(--bs-badge-color);text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:var(--bs-badge-border-radius)}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.alert{--bs-alert-bg:transparent;--bs-alert-padding-x:1rem;--bs-alert-padding-y:1rem;--bs-alert-margin-bottom:1rem;--bs-alert-color:inherit;--bs-alert-border-color:transparent;--bs-alert-border:var(--bs-border-width) solid var(--bs-alert-border-color);--bs-alert-border-radius:var(--bs-border-radius);--bs-alert-link-color:inherit;position:relative;padding:var(--bs-alert-padding-y) var(--bs-alert-padding-x);margin-bottom:var(--bs-alert-margin-bottom);color:var(--bs-alert-color);background-color:var(--bs-alert-bg);border:var(--bs-alert-border);border-radius:var(--bs-alert-border-radius)}.alert-heading{color:inherit}.alert-link{font-weight:700;color:var(--bs-alert-link-color)}.alert-dismissible{padding-right:3rem}.alert-dismissible .btn-close{position:absolute;top:0;right:0;z-index:2;padding:1.25rem 1rem}.alert-primary{--bs-alert-color:var(--bs-primary-text-emphasis);--bs-alert-bg:var(--bs-primary-bg-subtle);--bs-alert-border-color:var(--bs-primary-border-subtle);--bs-alert-link-color:var(--bs-primary-text-emphasis)}.alert-secondary{--bs-alert-color:var(--bs-secondary-text-emphasis);--bs-alert-bg:var(--bs-secondary-bg-subtle);--bs-alert-border-color:var(--bs-secondary-border-subtle);--bs-alert-link-color:var(--bs-secondary-text-emphasis)}.alert-success{--bs-alert-color:var(--bs-success-text-emphasis);--bs-alert-bg:var(--bs-success-bg-subtle);--bs-alert-border-color:var(--bs-success-border-subtle);--bs-alert-link-color:var(--bs-success-text-emphasis)}.alert-info{--bs-alert-color:var(--bs-info-text-emphasis);--bs-alert-bg:var(--bs-info-bg-subtle);--bs-alert-border-color:var(--bs-info-border-subtle);--bs-alert-link-color:var(--bs-info-text-emphasis)}.alert-warning{--bs-alert-color:var(--bs-warning-text-emphasis);--bs-alert-bg:var(--bs-warning-bg-subtle);--bs-alert-border-color:var(--bs-warning-border-subtle);--bs-alert-link-color:var(--bs-warning-text-emphasis)}.alert-danger{--bs-alert-color:var(--bs-danger-text-emphasis);--bs-alert-bg:var(--bs-danger-bg-subtle);--bs-alert-border-color:var(--bs-danger-border-subtle);--bs-alert-link-color:var(--bs-danger-text-emphasis)}.alert-light{--bs-alert-color:var(--bs-light-text-emphasis);--bs-alert-bg:var(--bs-light-bg-subtle);--bs-alert-border-color:var(--bs-light-border-subtle);--bs-alert-link-color:var(--bs-light-text-emphasis)}.alert-dark{--bs-alert-color:var(--bs-dark-text-emphasis);--bs-alert-bg:var(--bs-dark-bg-subtle);--bs-alert-border-color:var(--bs-dark-border-subtle);--bs-alert-link-color:var(--bs-dark-text-emphasis)}@keyframes progress-bar-stripes{0%{background-position-x:var(--bs-progress-height)}}.progress,.progress-stacked{--bs-progress-height:1rem;--bs-progress-font-size:0.75rem;--bs-progress-bg:var(--bs-secondary-bg);--bs-progress-border-radius:var(--bs-border-radius);--bs-progress-box-shadow:var(--bs-box-shadow-inset);--bs-progress-bar-color:#fff;--bs-progress-bar-bg:#0d6efd;--bs-progress-bar-transition:width 0.6s ease;display:flex;height:var(--bs-progress-height);overflow:hidden;font-size:var(--bs-progress-font-size);background-color:var(--bs-progress-bg);border-radius:var(--bs-progress-border-radius)}.progress-bar{display:flex;flex-direction:column;justify-content:center;overflow:hidden;color:var(--bs-progress-bar-color);text-align:center;white-space:nowrap;background-color:var(--bs-progress-bar-bg);transition:var(--bs-progress-bar-transition)}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:var(--bs-progress-height) var(--bs-progress-height)}.progress-stacked>.progress{overflow:visible}.progress-stacked>.progress>.progress-bar{width:100%}.progress-bar-animated{animation:1s linear infinite progress-bar-stripes}@media (prefers-reduced-motion:reduce){.progress-bar-animated{animation:none}}.list-group{--bs-list-group-color:var(--bs-body-color);--bs-list-group-bg:var(--bs-body-bg);--bs-list-group-border-color:var(--bs-border-color);--bs-list-group-border-width:var(--bs-border-width);--bs-list-group-border-radius:var(--bs-border-radius);--bs-list-group-item-padding-x:1rem;--bs-list-group-item-padding-y:0.5rem;--bs-list-group-action-color:var(--bs-secondary-color);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-tertiary-bg);--bs-list-group-action-active-color:var(--bs-body-color);--bs-list-group-action-active-bg:var(--bs-secondary-bg);--bs-list-group-disabled-color:var(--bs-secondary-color);--bs-list-group-disabled-bg:var(--bs-body-bg);--bs-list-group-active-color:#fff;--bs-list-group-active-bg:#0d6efd;--bs-list-group-active-border-color:#0d6efd;display:flex;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:var(--bs-list-group-border-radius)}.list-group-numbered{list-style-type:none;counter-reset:section}.list-group-numbered>.list-group-item::before{content:counters(section, ".") ". ";counter-increment:section}.list-group-item{position:relative;display:block;padding:var(--bs-list-group-item-padding-y) var(--bs-list-group-item-padding-x);color:var(--bs-list-group-color);text-decoration:none;background-color:var(--bs-list-group-bg);border:var(--bs-list-group-border-width) solid var(--bs-list-group-border-color)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:var(--bs-list-group-disabled-color);pointer-events:none;background-color:var(--bs-list-group-disabled-bg)}.list-group-item.active{z-index:2;color:var(--bs-list-group-active-color);background-color:var(--bs-list-group-active-bg);border-color:var(--bs-list-group-active-border-color)}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:calc(-1 * var(--bs-list-group-border-width));border-top-width:var(--bs-list-group-border-width)}.list-group-item-action{width:100%;color:var(--bs-list-group-action-color);text-align:inherit}.list-group-item-action:not(.active):focus,.list-group-item-action:not(.active):hover{z-index:1;color:var(--bs-list-group-action-hover-color);text-decoration:none;background-color:var(--bs-list-group-action-hover-bg)}.list-group-item-action:not(.active):active{color:var(--bs-list-group-action-active-color);background-color:var(--bs-list-group-action-active-bg)}.list-group-horizontal{flex-direction:row}.list-group-horizontal>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}@media (min-width:576px){.list-group-horizontal-sm{flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:768px){.list-group-horizontal-md{flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:992px){.list-group-horizontal-lg{flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:1200px){.list-group-horizontal-xl{flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:1400px){.list-group-horizontal-xxl{flex-direction:row}.list-group-horizontal-xxl>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-xxl>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-xxl>.list-group-item.active{margin-top:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 var(--bs-list-group-border-width)}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{--bs-list-group-color:var(--bs-primary-text-emphasis);--bs-list-group-bg:var(--bs-primary-bg-subtle);--bs-list-group-border-color:var(--bs-primary-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-primary-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-primary-border-subtle);--bs-list-group-active-color:var(--bs-primary-bg-subtle);--bs-list-group-active-bg:var(--bs-primary-text-emphasis);--bs-list-group-active-border-color:var(--bs-primary-text-emphasis)}.list-group-item-secondary{--bs-list-group-color:var(--bs-secondary-text-emphasis);--bs-list-group-bg:var(--bs-secondary-bg-subtle);--bs-list-group-border-color:var(--bs-secondary-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-secondary-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-secondary-border-subtle);--bs-list-group-active-color:var(--bs-secondary-bg-subtle);--bs-list-group-active-bg:var(--bs-secondary-text-emphasis);--bs-list-group-active-border-color:var(--bs-secondary-text-emphasis)}.list-group-item-success{--bs-list-group-color:var(--bs-success-text-emphasis);--bs-list-group-bg:var(--bs-success-bg-subtle);--bs-list-group-border-color:var(--bs-success-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-success-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-success-border-subtle);--bs-list-group-active-color:var(--bs-success-bg-subtle);--bs-list-group-active-bg:var(--bs-success-text-emphasis);--bs-list-group-active-border-color:var(--bs-success-text-emphasis)}.list-group-item-info{--bs-list-group-color:var(--bs-info-text-emphasis);--bs-list-group-bg:var(--bs-info-bg-subtle);--bs-list-group-border-color:var(--bs-info-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-info-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-info-border-subtle);--bs-list-group-active-color:var(--bs-info-bg-subtle);--bs-list-group-active-bg:var(--bs-info-text-emphasis);--bs-list-group-active-border-color:var(--bs-info-text-emphasis)}.list-group-item-warning{--bs-list-group-color:var(--bs-warning-text-emphasis);--bs-list-group-bg:var(--bs-warning-bg-subtle);--bs-list-group-border-color:var(--bs-warning-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-warning-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-warning-border-subtle);--bs-list-group-active-color:var(--bs-warning-bg-subtle);--bs-list-group-active-bg:var(--bs-warning-text-emphasis);--bs-list-group-active-border-color:var(--bs-warning-text-emphasis)}.list-group-item-danger{--bs-list-group-color:var(--bs-danger-text-emphasis);--bs-list-group-bg:var(--bs-danger-bg-subtle);--bs-list-group-border-color:var(--bs-danger-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-danger-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-danger-border-subtle);--bs-list-group-active-color:var(--bs-danger-bg-subtle);--bs-list-group-active-bg:var(--bs-danger-text-emphasis);--bs-list-group-active-border-color:var(--bs-danger-text-emphasis)}.list-group-item-light{--bs-list-group-color:var(--bs-light-text-emphasis);--bs-list-group-bg:var(--bs-light-bg-subtle);--bs-list-group-border-color:var(--bs-light-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-light-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-light-border-subtle);--bs-list-group-active-color:var(--bs-light-bg-subtle);--bs-list-group-active-bg:var(--bs-light-text-emphasis);--bs-list-group-active-border-color:var(--bs-light-text-emphasis)}.list-group-item-dark{--bs-list-group-color:var(--bs-dark-text-emphasis);--bs-list-group-bg:var(--bs-dark-bg-subtle);--bs-list-group-border-color:var(--bs-dark-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-dark-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-dark-border-subtle);--bs-list-group-active-color:var(--bs-dark-bg-subtle);--bs-list-group-active-bg:var(--bs-dark-text-emphasis);--bs-list-group-active-border-color:var(--bs-dark-text-emphasis)}.btn-close{--bs-btn-close-color:#000;--bs-btn-close-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414'/%3e%3c/svg%3e");--bs-btn-close-opacity:0.5;--bs-btn-close-hover-opacity:0.75;--bs-btn-close-focus-shadow:0 0 0 0.25rem rgba(13, 110, 253, 0.25);--bs-btn-close-focus-opacity:1;--bs-btn-close-disabled-opacity:0.25;box-sizing:content-box;width:1em;height:1em;padding:.25em .25em;color:var(--bs-btn-close-color);background:transparent var(--bs-btn-close-bg) center/1em auto no-repeat;filter:var(--bs-btn-close-filter);border:0;border-radius:.375rem;opacity:var(--bs-btn-close-opacity)}.btn-close:hover{color:var(--bs-btn-close-color);text-decoration:none;opacity:var(--bs-btn-close-hover-opacity)}.btn-close:focus{outline:0;box-shadow:var(--bs-btn-close-focus-shadow);opacity:var(--bs-btn-close-focus-opacity)}.btn-close.disabled,.btn-close:disabled{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;opacity:var(--bs-btn-close-disabled-opacity)}.btn-close-white{--bs-btn-close-filter:invert(1) grayscale(100%) brightness(200%)}:root,[data-bs-theme=light]{--bs-btn-close-filter: }[data-bs-theme=dark]{--bs-btn-close-filter:invert(1) grayscale(100%) brightness(200%)}.toast{--bs-toast-zindex:1090;--bs-toast-padding-x:0.75rem;--bs-toast-padding-y:0.5rem;--bs-toast-spacing:1.5rem;--bs-toast-max-width:350px;--bs-toast-font-size:0.875rem;--bs-toast-color: ;--bs-toast-bg:rgba(var(--bs-body-bg-rgb), 0.85);--bs-toast-border-width:var(--bs-border-width);--bs-toast-border-color:var(--bs-border-color-translucent);--bs-toast-border-radius:var(--bs-border-radius);--bs-toast-box-shadow:var(--bs-box-shadow);--bs-toast-header-color:var(--bs-secondary-color);--bs-toast-header-bg:rgba(var(--bs-body-bg-rgb), 0.85);--bs-toast-header-border-color:var(--bs-border-color-translucent);width:var(--bs-toast-max-width);max-width:100%;font-size:var(--bs-toast-font-size);color:var(--bs-toast-color);pointer-events:auto;background-color:var(--bs-toast-bg);background-clip:padding-box;border:var(--bs-toast-border-width) solid var(--bs-toast-border-color);box-shadow:var(--bs-toast-box-shadow);border-radius:var(--bs-toast-border-radius)}.toast.showing{opacity:0}.toast:not(.show){display:none}.toast-container{--bs-toast-zindex:1090;position:absolute;z-index:var(--bs-toast-zindex);width:-webkit-max-content;width:-moz-max-content;width:max-content;max-width:100%;pointer-events:none}.toast-container>:not(:last-child){margin-bottom:var(--bs-toast-spacing)}.toast-header{display:flex;align-items:center;padding:var(--bs-toast-padding-y) var(--bs-toast-padding-x);color:var(--bs-toast-header-color);background-color:var(--bs-toast-header-bg);background-clip:padding-box;border-bottom:var(--bs-toast-border-width) solid var(--bs-toast-header-border-color);border-top-left-radius:calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width));border-top-right-radius:calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width))}.toast-header .btn-close{margin-right:calc(-.5 * var(--bs-toast-padding-x));margin-left:var(--bs-toast-padding-x)}.toast-body{padding:var(--bs-toast-padding-x);word-wrap:break-word}.modal{--bs-modal-zindex:1055;--bs-modal-width:500px;--bs-modal-padding:1rem;--bs-modal-margin:0.5rem;--bs-modal-color:var(--bs-body-color);--bs-modal-bg:var(--bs-body-bg);--bs-modal-border-color:var(--bs-border-color-translucent);--bs-modal-border-width:var(--bs-border-width);--bs-modal-border-radius:var(--bs-border-radius-lg);--bs-modal-box-shadow:var(--bs-box-shadow-sm);--bs-modal-inner-border-radius:calc(var(--bs-border-radius-lg) - (var(--bs-border-width)));--bs-modal-header-padding-x:1rem;--bs-modal-header-padding-y:1rem;--bs-modal-header-padding:1rem 1rem;--bs-modal-header-border-color:var(--bs-border-color);--bs-modal-header-border-width:var(--bs-border-width);--bs-modal-title-line-height:1.5;--bs-modal-footer-gap:0.5rem;--bs-modal-footer-bg: ;--bs-modal-footer-border-color:var(--bs-border-color);--bs-modal-footer-border-width:var(--bs-border-width);position:fixed;top:0;left:0;z-index:var(--bs-modal-zindex);display:none;width:100%;height:100%;overflow-x:hidden;overflow-y:auto;outline:0}.modal-dialog{position:relative;width:auto;margin:var(--bs-modal-margin);pointer-events:none}.modal.fade .modal-dialog{transform:translate(0,-50px);transition:transform .3s ease-out}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{transform:none}.modal.modal-static .modal-dialog{transform:scale(1.02)}.modal-dialog-scrollable{height:calc(100% - var(--bs-modal-margin) * 2)}.modal-dialog-scrollable .modal-content{max-height:100%;overflow:hidden}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:flex;align-items:center;min-height:calc(100% - var(--bs-modal-margin) * 2)}.modal-content{position:relative;display:flex;flex-direction:column;width:100%;color:var(--bs-modal-color);pointer-events:auto;background-color:var(--bs-modal-bg);background-clip:padding-box;border:var(--bs-modal-border-width) solid var(--bs-modal-border-color);border-radius:var(--bs-modal-border-radius);outline:0}.modal-backdrop{--bs-backdrop-zindex:1050;--bs-backdrop-bg:#000;--bs-backdrop-opacity:0.5;position:fixed;top:0;left:0;z-index:var(--bs-backdrop-zindex);width:100vw;height:100vh;background-color:var(--bs-backdrop-bg)}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:var(--bs-backdrop-opacity)}.modal-header{display:flex;flex-shrink:0;align-items:center;padding:var(--bs-modal-header-padding);border-bottom:var(--bs-modal-header-border-width) solid var(--bs-modal-header-border-color);border-top-left-radius:var(--bs-modal-inner-border-radius);border-top-right-radius:var(--bs-modal-inner-border-radius)}.modal-header .btn-close{padding:calc(var(--bs-modal-header-padding-y) * .5) calc(var(--bs-modal-header-padding-x) * .5);margin-top:calc(-.5 * var(--bs-modal-header-padding-y));margin-right:calc(-.5 * var(--bs-modal-header-padding-x));margin-bottom:calc(-.5 * var(--bs-modal-header-padding-y));margin-left:auto}.modal-title{margin-bottom:0;line-height:var(--bs-modal-title-line-height)}.modal-body{position:relative;flex:1 1 auto;padding:var(--bs-modal-padding)}.modal-footer{display:flex;flex-shrink:0;flex-wrap:wrap;align-items:center;justify-content:flex-end;padding:calc(var(--bs-modal-padding) - var(--bs-modal-footer-gap) * .5);background-color:var(--bs-modal-footer-bg);border-top:var(--bs-modal-footer-border-width) solid var(--bs-modal-footer-border-color);border-bottom-right-radius:var(--bs-modal-inner-border-radius);border-bottom-left-radius:var(--bs-modal-inner-border-radius)}.modal-footer>*{margin:calc(var(--bs-modal-footer-gap) * .5)}@media (min-width:576px){.modal{--bs-modal-margin:1.75rem;--bs-modal-box-shadow:var(--bs-box-shadow)}.modal-dialog{max-width:var(--bs-modal-width);margin-right:auto;margin-left:auto}.modal-sm{--bs-modal-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{--bs-modal-width:800px}}@media (min-width:1200px){.modal-xl{--bs-modal-width:1140px}}.modal-fullscreen{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen .modal-footer,.modal-fullscreen .modal-header{border-radius:0}.modal-fullscreen .modal-body{overflow-y:auto}@media (max-width:575.98px){.modal-fullscreen-sm-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-sm-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-sm-down .modal-footer,.modal-fullscreen-sm-down .modal-header{border-radius:0}.modal-fullscreen-sm-down .modal-body{overflow-y:auto}}@media (max-width:767.98px){.modal-fullscreen-md-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-md-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-md-down .modal-footer,.modal-fullscreen-md-down .modal-header{border-radius:0}.modal-fullscreen-md-down .modal-body{overflow-y:auto}}@media (max-width:991.98px){.modal-fullscreen-lg-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-lg-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-lg-down .modal-footer,.modal-fullscreen-lg-down .modal-header{border-radius:0}.modal-fullscreen-lg-down .modal-body{overflow-y:auto}}@media (max-width:1199.98px){.modal-fullscreen-xl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xl-down .modal-footer,.modal-fullscreen-xl-down .modal-header{border-radius:0}.modal-fullscreen-xl-down .modal-body{overflow-y:auto}}@media (max-width:1399.98px){.modal-fullscreen-xxl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xxl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xxl-down .modal-footer,.modal-fullscreen-xxl-down .modal-header{border-radius:0}.modal-fullscreen-xxl-down .modal-body{overflow-y:auto}}.tooltip{--bs-tooltip-zindex:1080;--bs-tooltip-max-width:200px;--bs-tooltip-padding-x:0.5rem;--bs-tooltip-padding-y:0.25rem;--bs-tooltip-margin: ;--bs-tooltip-font-size:0.875rem;--bs-tooltip-color:var(--bs-body-bg);--bs-tooltip-bg:var(--bs-emphasis-color);--bs-tooltip-border-radius:var(--bs-border-radius);--bs-tooltip-opacity:0.9;--bs-tooltip-arrow-width:0.8rem;--bs-tooltip-arrow-height:0.4rem;z-index:var(--bs-tooltip-zindex);display:block;margin:var(--bs-tooltip-margin);font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--bs-tooltip-font-size);word-wrap:break-word;opacity:0}.tooltip.show{opacity:var(--bs-tooltip-opacity)}.tooltip .tooltip-arrow{display:block;width:var(--bs-tooltip-arrow-width);height:var(--bs-tooltip-arrow-height)}.tooltip .tooltip-arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow,.bs-tooltip-top .tooltip-arrow{bottom:calc(-1 * var(--bs-tooltip-arrow-height))}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before,.bs-tooltip-top .tooltip-arrow::before{top:-1px;border-width:var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * .5) 0;border-top-color:var(--bs-tooltip-bg)}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow,.bs-tooltip-end .tooltip-arrow{left:calc(-1 * var(--bs-tooltip-arrow-height));width:var(--bs-tooltip-arrow-height);height:var(--bs-tooltip-arrow-width)}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before,.bs-tooltip-end .tooltip-arrow::before{right:-1px;border-width:calc(var(--bs-tooltip-arrow-width) * .5) var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * .5) 0;border-right-color:var(--bs-tooltip-bg)}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow,.bs-tooltip-bottom .tooltip-arrow{top:calc(-1 * var(--bs-tooltip-arrow-height))}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before,.bs-tooltip-bottom .tooltip-arrow::before{bottom:-1px;border-width:0 calc(var(--bs-tooltip-arrow-width) * .5) var(--bs-tooltip-arrow-height);border-bottom-color:var(--bs-tooltip-bg)}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow,.bs-tooltip-start .tooltip-arrow{right:calc(-1 * var(--bs-tooltip-arrow-height));width:var(--bs-tooltip-arrow-height);height:var(--bs-tooltip-arrow-width)}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before,.bs-tooltip-start .tooltip-arrow::before{left:-1px;border-width:calc(var(--bs-tooltip-arrow-width) * .5) 0 calc(var(--bs-tooltip-arrow-width) * .5) var(--bs-tooltip-arrow-height);border-left-color:var(--bs-tooltip-bg)}.tooltip-inner{max-width:var(--bs-tooltip-max-width);padding:var(--bs-tooltip-padding-y) var(--bs-tooltip-padding-x);color:var(--bs-tooltip-color);text-align:center;background-color:var(--bs-tooltip-bg);border-radius:var(--bs-tooltip-border-radius)}.popover{--bs-popover-zindex:1070;--bs-popover-max-width:276px;--bs-popover-font-size:0.875rem;--bs-popover-bg:var(--bs-body-bg);--bs-popover-border-width:var(--bs-border-width);--bs-popover-border-color:var(--bs-border-color-translucent);--bs-popover-border-radius:var(--bs-border-radius-lg);--bs-popover-inner-border-radius:calc(var(--bs-border-radius-lg) - var(--bs-border-width));--bs-popover-box-shadow:var(--bs-box-shadow);--bs-popover-header-padding-x:1rem;--bs-popover-header-padding-y:0.5rem;--bs-popover-header-font-size:1rem;--bs-popover-header-color:inherit;--bs-popover-header-bg:var(--bs-secondary-bg);--bs-popover-body-padding-x:1rem;--bs-popover-body-padding-y:1rem;--bs-popover-body-color:var(--bs-body-color);--bs-popover-arrow-width:1rem;--bs-popover-arrow-height:0.5rem;--bs-popover-arrow-border:var(--bs-popover-border-color);z-index:var(--bs-popover-zindex);display:block;max-width:var(--bs-popover-max-width);font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--bs-popover-font-size);word-wrap:break-word;background-color:var(--bs-popover-bg);background-clip:padding-box;border:var(--bs-popover-border-width) solid var(--bs-popover-border-color);border-radius:var(--bs-popover-border-radius)}.popover .popover-arrow{display:block;width:var(--bs-popover-arrow-width);height:var(--bs-popover-arrow-height)}.popover .popover-arrow::after,.popover .popover-arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid;border-width:0}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow,.bs-popover-top>.popover-arrow{bottom:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width))}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::after,.bs-popover-top>.popover-arrow::before{border-width:var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * .5) 0}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::before{bottom:0;border-top-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after,.bs-popover-top>.popover-arrow::after{bottom:var(--bs-popover-border-width);border-top-color:var(--bs-popover-bg)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow,.bs-popover-end>.popover-arrow{left:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));width:var(--bs-popover-arrow-height);height:var(--bs-popover-arrow-width)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::after,.bs-popover-end>.popover-arrow::before{border-width:calc(var(--bs-popover-arrow-width) * .5) var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * .5) 0}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::before{left:0;border-right-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after,.bs-popover-end>.popover-arrow::after{left:var(--bs-popover-border-width);border-right-color:var(--bs-popover-bg)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow,.bs-popover-bottom>.popover-arrow{top:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width))}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::after,.bs-popover-bottom>.popover-arrow::before{border-width:0 calc(var(--bs-popover-arrow-width) * .5) var(--bs-popover-arrow-height)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::before{top:0;border-bottom-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after,.bs-popover-bottom>.popover-arrow::after{top:var(--bs-popover-border-width);border-bottom-color:var(--bs-popover-bg)}.bs-popover-auto[data-popper-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:var(--bs-popover-arrow-width);margin-left:calc(-.5 * var(--bs-popover-arrow-width));content:"";border-bottom:var(--bs-popover-border-width) solid var(--bs-popover-header-bg)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow,.bs-popover-start>.popover-arrow{right:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));width:var(--bs-popover-arrow-height);height:var(--bs-popover-arrow-width)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::after,.bs-popover-start>.popover-arrow::before{border-width:calc(var(--bs-popover-arrow-width) * .5) 0 calc(var(--bs-popover-arrow-width) * .5) var(--bs-popover-arrow-height)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::before{right:0;border-left-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after,.bs-popover-start>.popover-arrow::after{right:var(--bs-popover-border-width);border-left-color:var(--bs-popover-bg)}.popover-header{padding:var(--bs-popover-header-padding-y) var(--bs-popover-header-padding-x);margin-bottom:0;font-size:var(--bs-popover-header-font-size);color:var(--bs-popover-header-color);background-color:var(--bs-popover-header-bg);border-bottom:var(--bs-popover-border-width) solid var(--bs-popover-border-color);border-top-left-radius:var(--bs-popover-inner-border-radius);border-top-right-radius:var(--bs-popover-inner-border-radius)}.popover-header:empty{display:none}.popover-body{padding:var(--bs-popover-body-padding-y) var(--bs-popover-body-padding-x);color:var(--bs-popover-body-color)}.carousel{position:relative}.carousel.pointer-event{touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-end,.carousel-item-next:not(.carousel-item-start){transform:translateX(100%)}.active.carousel-item-start,.carousel-item-prev:not(.carousel-item-end){transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;transform:none}.carousel-fade .carousel-item-next.carousel-item-start,.carousel-fade .carousel-item-prev.carousel-item-end,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:flex;align-items:center;justify-content:center;width:15%;padding:0;color:#fff;text-align:center;background:0 0;filter:var(--bs-carousel-control-icon-filter);border:0;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:2rem;height:2rem;background-repeat:no-repeat;background-position:50%;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:2;display:flex;justify-content:center;padding:0;margin-right:15%;margin-bottom:1rem;margin-left:15%}.carousel-indicators [data-bs-target]{box-sizing:content-box;flex:0 1 auto;width:30px;height:3px;padding:0;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:var(--bs-carousel-indicator-active-bg);background-clip:padding-box;border:0;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators [data-bs-target]{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:1.25rem;left:15%;padding-top:1.25rem;padding-bottom:1.25rem;color:var(--bs-carousel-caption-color);text-align:center}.carousel-dark{--bs-carousel-indicator-active-bg:#000;--bs-carousel-caption-color:#000;--bs-carousel-control-icon-filter:invert(1) grayscale(100)}:root,[data-bs-theme=light]{--bs-carousel-indicator-active-bg:#fff;--bs-carousel-caption-color:#fff;--bs-carousel-control-icon-filter: }[data-bs-theme=dark]{--bs-carousel-indicator-active-bg:#000;--bs-carousel-caption-color:#000;--bs-carousel-control-icon-filter:invert(1) grayscale(100)}.spinner-border,.spinner-grow{display:inline-block;width:var(--bs-spinner-width);height:var(--bs-spinner-height);vertical-align:var(--bs-spinner-vertical-align);border-radius:50%;animation:var(--bs-spinner-animation-speed) linear infinite var(--bs-spinner-animation-name)}@keyframes spinner-border{to{transform:rotate(360deg)}}.spinner-border{--bs-spinner-width:2rem;--bs-spinner-height:2rem;--bs-spinner-vertical-align:-0.125em;--bs-spinner-border-width:0.25em;--bs-spinner-animation-speed:0.75s;--bs-spinner-animation-name:spinner-border;border:var(--bs-spinner-border-width) solid currentcolor;border-right-color:transparent}.spinner-border-sm{--bs-spinner-width:1rem;--bs-spinner-height:1rem;--bs-spinner-border-width:0.2em}@keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}.spinner-grow{--bs-spinner-width:2rem;--bs-spinner-height:2rem;--bs-spinner-vertical-align:-0.125em;--bs-spinner-animation-speed:0.75s;--bs-spinner-animation-name:spinner-grow;background-color:currentcolor;opacity:0}.spinner-grow-sm{--bs-spinner-width:1rem;--bs-spinner-height:1rem}@media (prefers-reduced-motion:reduce){.spinner-border,.spinner-grow{--bs-spinner-animation-speed:1.5s}}.offcanvas,.offcanvas-lg,.offcanvas-md,.offcanvas-sm,.offcanvas-xl,.offcanvas-xxl{--bs-offcanvas-zindex:1045;--bs-offcanvas-width:400px;--bs-offcanvas-height:30vh;--bs-offcanvas-padding-x:1rem;--bs-offcanvas-padding-y:1rem;--bs-offcanvas-color:var(--bs-body-color);--bs-offcanvas-bg:var(--bs-body-bg);--bs-offcanvas-border-width:var(--bs-border-width);--bs-offcanvas-border-color:var(--bs-border-color-translucent);--bs-offcanvas-box-shadow:var(--bs-box-shadow-sm);--bs-offcanvas-transition:transform 0.3s ease-in-out;--bs-offcanvas-title-line-height:1.5}@media (max-width:575.98px){.offcanvas-sm{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:575.98px) and (prefers-reduced-motion:reduce){.offcanvas-sm{transition:none}}@media (max-width:575.98px){.offcanvas-sm.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-sm.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-sm.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-sm.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-sm.show:not(.hiding),.offcanvas-sm.showing{transform:none}.offcanvas-sm.hiding,.offcanvas-sm.show,.offcanvas-sm.showing{visibility:visible}}@media (min-width:576px){.offcanvas-sm{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-sm .offcanvas-header{display:none}.offcanvas-sm .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:767.98px){.offcanvas-md{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:767.98px) and (prefers-reduced-motion:reduce){.offcanvas-md{transition:none}}@media (max-width:767.98px){.offcanvas-md.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-md.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-md.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-md.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-md.show:not(.hiding),.offcanvas-md.showing{transform:none}.offcanvas-md.hiding,.offcanvas-md.show,.offcanvas-md.showing{visibility:visible}}@media (min-width:768px){.offcanvas-md{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-md .offcanvas-header{display:none}.offcanvas-md .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:991.98px){.offcanvas-lg{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:991.98px) and (prefers-reduced-motion:reduce){.offcanvas-lg{transition:none}}@media (max-width:991.98px){.offcanvas-lg.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-lg.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-lg.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-lg.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-lg.show:not(.hiding),.offcanvas-lg.showing{transform:none}.offcanvas-lg.hiding,.offcanvas-lg.show,.offcanvas-lg.showing{visibility:visible}}@media (min-width:992px){.offcanvas-lg{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-lg .offcanvas-header{display:none}.offcanvas-lg .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:1199.98px){.offcanvas-xl{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:1199.98px) and (prefers-reduced-motion:reduce){.offcanvas-xl{transition:none}}@media (max-width:1199.98px){.offcanvas-xl.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-xl.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-xl.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-xl.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-xl.show:not(.hiding),.offcanvas-xl.showing{transform:none}.offcanvas-xl.hiding,.offcanvas-xl.show,.offcanvas-xl.showing{visibility:visible}}@media (min-width:1200px){.offcanvas-xl{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-xl .offcanvas-header{display:none}.offcanvas-xl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:1399.98px){.offcanvas-xxl{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:1399.98px) and (prefers-reduced-motion:reduce){.offcanvas-xxl{transition:none}}@media (max-width:1399.98px){.offcanvas-xxl.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-xxl.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-xxl.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-xxl.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-xxl.show:not(.hiding),.offcanvas-xxl.showing{transform:none}.offcanvas-xxl.hiding,.offcanvas-xxl.show,.offcanvas-xxl.showing{visibility:visible}}@media (min-width:1400px){.offcanvas-xxl{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-xxl .offcanvas-header{display:none}.offcanvas-xxl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}.offcanvas{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}@media (prefers-reduced-motion:reduce){.offcanvas{transition:none}}.offcanvas.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas.show:not(.hiding),.offcanvas.showing{transform:none}.offcanvas.hiding,.offcanvas.show,.offcanvas.showing{visibility:visible}.offcanvas-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.offcanvas-backdrop.fade{opacity:0}.offcanvas-backdrop.show{opacity:.5}.offcanvas-header{display:flex;align-items:center;padding:var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x)}.offcanvas-header .btn-close{padding:calc(var(--bs-offcanvas-padding-y) * .5) calc(var(--bs-offcanvas-padding-x) * .5);margin-top:calc(-.5 * var(--bs-offcanvas-padding-y));margin-right:calc(-.5 * var(--bs-offcanvas-padding-x));margin-bottom:calc(-.5 * var(--bs-offcanvas-padding-y));margin-left:auto}.offcanvas-title{margin-bottom:0;line-height:var(--bs-offcanvas-title-line-height)}.offcanvas-body{flex-grow:1;padding:var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x);overflow-y:auto}.placeholder{display:inline-block;min-height:1em;vertical-align:middle;cursor:wait;background-color:currentcolor;opacity:.5}.placeholder.btn::before{display:inline-block;content:""}.placeholder-xs{min-height:.6em}.placeholder-sm{min-height:.8em}.placeholder-lg{min-height:1.2em}.placeholder-glow .placeholder{animation:placeholder-glow 2s ease-in-out infinite}@keyframes placeholder-glow{50%{opacity:.2}}.placeholder-wave{-webkit-mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,0.8) 75%,#000 95%);mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,0.8) 75%,#000 95%);-webkit-mask-size:200% 100%;mask-size:200% 100%;animation:placeholder-wave 2s linear infinite}@keyframes placeholder-wave{100%{-webkit-mask-position:-200% 0%;mask-position:-200% 0%}}.clearfix::after{display:block;clear:both;content:""}.text-bg-primary{color:#fff!important;background-color:RGBA(var(--bs-primary-rgb),var(--bs-bg-opacity,1))!important}.text-bg-secondary{color:#fff!important;background-color:RGBA(var(--bs-secondary-rgb),var(--bs-bg-opacity,1))!important}.text-bg-success{color:#fff!important;background-color:RGBA(var(--bs-success-rgb),var(--bs-bg-opacity,1))!important}.text-bg-info{color:#000!important;background-color:RGBA(var(--bs-info-rgb),var(--bs-bg-opacity,1))!important}.text-bg-warning{color:#000!important;background-color:RGBA(var(--bs-warning-rgb),var(--bs-bg-opacity,1))!important}.text-bg-danger{color:#fff!important;background-color:RGBA(var(--bs-danger-rgb),var(--bs-bg-opacity,1))!important}.text-bg-light{color:#000!important;background-color:RGBA(var(--bs-light-rgb),var(--bs-bg-opacity,1))!important}.text-bg-dark{color:#fff!important;background-color:RGBA(var(--bs-dark-rgb),var(--bs-bg-opacity,1))!important}.link-primary{color:RGBA(var(--bs-primary-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-primary-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-primary-rgb),var(--bs-link-underline-opacity,1))!important}.link-primary:focus,.link-primary:hover{color:RGBA(10,88,202,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(10,88,202,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(10,88,202,var(--bs-link-underline-opacity,1))!important}.link-secondary{color:RGBA(var(--bs-secondary-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-secondary-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-secondary-rgb),var(--bs-link-underline-opacity,1))!important}.link-secondary:focus,.link-secondary:hover{color:RGBA(86,94,100,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(86,94,100,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(86,94,100,var(--bs-link-underline-opacity,1))!important}.link-success{color:RGBA(var(--bs-success-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-success-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-success-rgb),var(--bs-link-underline-opacity,1))!important}.link-success:focus,.link-success:hover{color:RGBA(20,108,67,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(20,108,67,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(20,108,67,var(--bs-link-underline-opacity,1))!important}.link-info{color:RGBA(var(--bs-info-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-info-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-info-rgb),var(--bs-link-underline-opacity,1))!important}.link-info:focus,.link-info:hover{color:RGBA(61,213,243,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(61,213,243,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(61,213,243,var(--bs-link-underline-opacity,1))!important}.link-warning{color:RGBA(var(--bs-warning-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-warning-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-warning-rgb),var(--bs-link-underline-opacity,1))!important}.link-warning:focus,.link-warning:hover{color:RGBA(255,205,57,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(255,205,57,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(255,205,57,var(--bs-link-underline-opacity,1))!important}.link-danger{color:RGBA(var(--bs-danger-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-danger-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-danger-rgb),var(--bs-link-underline-opacity,1))!important}.link-danger:focus,.link-danger:hover{color:RGBA(176,42,55,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(176,42,55,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(176,42,55,var(--bs-link-underline-opacity,1))!important}.link-light{color:RGBA(var(--bs-light-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-light-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-light-rgb),var(--bs-link-underline-opacity,1))!important}.link-light:focus,.link-light:hover{color:RGBA(249,250,251,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(249,250,251,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(249,250,251,var(--bs-link-underline-opacity,1))!important}.link-dark{color:RGBA(var(--bs-dark-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-dark-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-dark-rgb),var(--bs-link-underline-opacity,1))!important}.link-dark:focus,.link-dark:hover{color:RGBA(26,30,33,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(26,30,33,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(26,30,33,var(--bs-link-underline-opacity,1))!important}.link-body-emphasis{color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,1))!important}.link-body-emphasis:focus,.link-body-emphasis:hover{color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-opacity,.75))!important;-webkit-text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,0.75))!important;text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,0.75))!important}.focus-ring:focus{outline:0;box-shadow:var(--bs-focus-ring-x,0) var(--bs-focus-ring-y,0) var(--bs-focus-ring-blur,0) var(--bs-focus-ring-width) var(--bs-focus-ring-color)}.icon-link{display:inline-flex;gap:.375rem;align-items:center;-webkit-text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,0.5));text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,0.5));text-underline-offset:0.25em;-webkit-backface-visibility:hidden;backface-visibility:hidden}.icon-link>.bi{flex-shrink:0;width:1em;height:1em;fill:currentcolor;transition:.2s ease-in-out transform}@media (prefers-reduced-motion:reduce){.icon-link>.bi{transition:none}}.icon-link-hover:focus-visible>.bi,.icon-link-hover:hover>.bi{transform:var(--bs-icon-link-transform,translate3d(.25em,0,0))}.ratio{position:relative;width:100%}.ratio::before{display:block;padding-top:var(--bs-aspect-ratio);content:""}.ratio>*{position:absolute;top:0;left:0;width:100%;height:100%}.ratio-1x1{--bs-aspect-ratio:100%}.ratio-4x3{--bs-aspect-ratio:75%}.ratio-16x9{--bs-aspect-ratio:56.25%}.ratio-21x9{--bs-aspect-ratio:42.8571428571%}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}@media (min-width:576px){.sticky-sm-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-sm-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:768px){.sticky-md-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-md-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:992px){.sticky-lg-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-lg-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:1200px){.sticky-xl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-xl-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:1400px){.sticky-xxl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-xxl-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}.hstack{display:flex;flex-direction:row;align-items:center;align-self:stretch}.vstack{display:flex;flex:1 1 auto;flex-direction:column;align-self:stretch}.visually-hidden,.visually-hidden-focusable:not(:focus):not(:focus-within){width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}.visually-hidden-focusable:not(:focus):not(:focus-within):not(caption),.visually-hidden:not(caption){position:absolute!important}.visually-hidden *,.visually-hidden-focusable:not(:focus):not(:focus-within) *{overflow:hidden!important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;content:""}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.vr{display:inline-block;align-self:stretch;width:var(--bs-border-width);min-height:1em;background-color:currentcolor;opacity:.25}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.float-start{float:left!important}.float-end{float:right!important}.float-none{float:none!important}.object-fit-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-none{-o-object-fit:none!important;object-fit:none!important}.opacity-0{opacity:0!important}.opacity-25{opacity:.25!important}.opacity-50{opacity:.5!important}.opacity-75{opacity:.75!important}.opacity-100{opacity:1!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.overflow-visible{overflow:visible!important}.overflow-scroll{overflow:scroll!important}.overflow-x-auto{overflow-x:auto!important}.overflow-x-hidden{overflow-x:hidden!important}.overflow-x-visible{overflow-x:visible!important}.overflow-x-scroll{overflow-x:scroll!important}.overflow-y-auto{overflow-y:auto!important}.overflow-y-hidden{overflow-y:hidden!important}.overflow-y-visible{overflow-y:visible!important}.overflow-y-scroll{overflow-y:scroll!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-grid{display:grid!important}.d-inline-grid{display:inline-grid!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:flex!important}.d-inline-flex{display:inline-flex!important}.d-none{display:none!important}.shadow{box-shadow:var(--bs-box-shadow)!important}.shadow-sm{box-shadow:var(--bs-box-shadow-sm)!important}.shadow-lg{box-shadow:var(--bs-box-shadow-lg)!important}.shadow-none{box-shadow:none!important}.focus-ring-primary{--bs-focus-ring-color:rgba(var(--bs-primary-rgb), var(--bs-focus-ring-opacity))}.focus-ring-secondary{--bs-focus-ring-color:rgba(var(--bs-secondary-rgb), var(--bs-focus-ring-opacity))}.focus-ring-success{--bs-focus-ring-color:rgba(var(--bs-success-rgb), var(--bs-focus-ring-opacity))}.focus-ring-info{--bs-focus-ring-color:rgba(var(--bs-info-rgb), var(--bs-focus-ring-opacity))}.focus-ring-warning{--bs-focus-ring-color:rgba(var(--bs-warning-rgb), var(--bs-focus-ring-opacity))}.focus-ring-danger{--bs-focus-ring-color:rgba(var(--bs-danger-rgb), var(--bs-focus-ring-opacity))}.focus-ring-light{--bs-focus-ring-color:rgba(var(--bs-light-rgb), var(--bs-focus-ring-opacity))}.focus-ring-dark{--bs-focus-ring-color:rgba(var(--bs-dark-rgb), var(--bs-focus-ring-opacity))}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.top-0{top:0!important}.top-50{top:50%!important}.top-100{top:100%!important}.bottom-0{bottom:0!important}.bottom-50{bottom:50%!important}.bottom-100{bottom:100%!important}.start-0{left:0!important}.start-50{left:50%!important}.start-100{left:100%!important}.end-0{right:0!important}.end-50{right:50%!important}.end-100{right:100%!important}.translate-middle{transform:translate(-50%,-50%)!important}.translate-middle-x{transform:translateX(-50%)!important}.translate-middle-y{transform:translateY(-50%)!important}.border{border:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-0{border:0!important}.border-top{border-top:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-top-0{border-top:0!important}.border-end{border-right:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-end-0{border-right:0!important}.border-bottom{border-bottom:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-bottom-0{border-bottom:0!important}.border-start{border-left:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-start-0{border-left:0!important}.border-primary{--bs-border-opacity:1;border-color:rgba(var(--bs-primary-rgb),var(--bs-border-opacity))!important}.border-secondary{--bs-border-opacity:1;border-color:rgba(var(--bs-secondary-rgb),var(--bs-border-opacity))!important}.border-success{--bs-border-opacity:1;border-color:rgba(var(--bs-success-rgb),var(--bs-border-opacity))!important}.border-info{--bs-border-opacity:1;border-color:rgba(var(--bs-info-rgb),var(--bs-border-opacity))!important}.border-warning{--bs-border-opacity:1;border-color:rgba(var(--bs-warning-rgb),var(--bs-border-opacity))!important}.border-danger{--bs-border-opacity:1;border-color:rgba(var(--bs-danger-rgb),var(--bs-border-opacity))!important}.border-light{--bs-border-opacity:1;border-color:rgba(var(--bs-light-rgb),var(--bs-border-opacity))!important}.border-dark{--bs-border-opacity:1;border-color:rgba(var(--bs-dark-rgb),var(--bs-border-opacity))!important}.border-black{--bs-border-opacity:1;border-color:rgba(var(--bs-black-rgb),var(--bs-border-opacity))!important}.border-white{--bs-border-opacity:1;border-color:rgba(var(--bs-white-rgb),var(--bs-border-opacity))!important}.border-primary-subtle{border-color:var(--bs-primary-border-subtle)!important}.border-secondary-subtle{border-color:var(--bs-secondary-border-subtle)!important}.border-success-subtle{border-color:var(--bs-success-border-subtle)!important}.border-info-subtle{border-color:var(--bs-info-border-subtle)!important}.border-warning-subtle{border-color:var(--bs-warning-border-subtle)!important}.border-danger-subtle{border-color:var(--bs-danger-border-subtle)!important}.border-light-subtle{border-color:var(--bs-light-border-subtle)!important}.border-dark-subtle{border-color:var(--bs-dark-border-subtle)!important}.border-1{border-width:1px!important}.border-2{border-width:2px!important}.border-3{border-width:3px!important}.border-4{border-width:4px!important}.border-5{border-width:5px!important}.border-opacity-10{--bs-border-opacity:0.1}.border-opacity-25{--bs-border-opacity:0.25}.border-opacity-50{--bs-border-opacity:0.5}.border-opacity-75{--bs-border-opacity:0.75}.border-opacity-100{--bs-border-opacity:1}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.mw-100{max-width:100%!important}.vw-100{width:100vw!important}.min-vw-100{min-width:100vw!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mh-100{max-height:100%!important}.vh-100{height:100vh!important}.min-vh-100{min-height:100vh!important}.flex-fill{flex:1 1 auto!important}.flex-row{flex-direction:row!important}.flex-column{flex-direction:column!important}.flex-row-reverse{flex-direction:row-reverse!important}.flex-column-reverse{flex-direction:column-reverse!important}.flex-grow-0{flex-grow:0!important}.flex-grow-1{flex-grow:1!important}.flex-shrink-0{flex-shrink:0!important}.flex-shrink-1{flex-shrink:1!important}.flex-wrap{flex-wrap:wrap!important}.flex-nowrap{flex-wrap:nowrap!important}.flex-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-start{justify-content:flex-start!important}.justify-content-end{justify-content:flex-end!important}.justify-content-center{justify-content:center!important}.justify-content-between{justify-content:space-between!important}.justify-content-around{justify-content:space-around!important}.justify-content-evenly{justify-content:space-evenly!important}.align-items-start{align-items:flex-start!important}.align-items-end{align-items:flex-end!important}.align-items-center{align-items:center!important}.align-items-baseline{align-items:baseline!important}.align-items-stretch{align-items:stretch!important}.align-content-start{align-content:flex-start!important}.align-content-end{align-content:flex-end!important}.align-content-center{align-content:center!important}.align-content-between{align-content:space-between!important}.align-content-around{align-content:space-around!important}.align-content-stretch{align-content:stretch!important}.align-self-auto{align-self:auto!important}.align-self-start{align-self:flex-start!important}.align-self-end{align-self:flex-end!important}.align-self-center{align-self:center!important}.align-self-baseline{align-self:baseline!important}.align-self-stretch{align-self:stretch!important}.order-first{order:-1!important}.order-0{order:0!important}.order-1{order:1!important}.order-2{order:2!important}.order-3{order:3!important}.order-4{order:4!important}.order-5{order:5!important}.order-last{order:6!important}.m-0{margin:0!important}.m-1{margin:.25rem!important}.m-2{margin:.5rem!important}.m-3{margin:1rem!important}.m-4{margin:1.5rem!important}.m-5{margin:3rem!important}.m-auto{margin:auto!important}.mx-0{margin-right:0!important;margin-left:0!important}.mx-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-3{margin-right:1rem!important;margin-left:1rem!important}.mx-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-5{margin-right:3rem!important;margin-left:3rem!important}.mx-auto{margin-right:auto!important;margin-left:auto!important}.my-0{margin-top:0!important;margin-bottom:0!important}.my-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-0{margin-top:0!important}.mt-1{margin-top:.25rem!important}.mt-2{margin-top:.5rem!important}.mt-3{margin-top:1rem!important}.mt-4{margin-top:1.5rem!important}.mt-5{margin-top:3rem!important}.mt-auto{margin-top:auto!important}.me-0{margin-right:0!important}.me-1{margin-right:.25rem!important}.me-2{margin-right:.5rem!important}.me-3{margin-right:1rem!important}.me-4{margin-right:1.5rem!important}.me-5{margin-right:3rem!important}.me-auto{margin-right:auto!important}.mb-0{margin-bottom:0!important}.mb-1{margin-bottom:.25rem!important}.mb-2{margin-bottom:.5rem!important}.mb-3{margin-bottom:1rem!important}.mb-4{margin-bottom:1.5rem!important}.mb-5{margin-bottom:3rem!important}.mb-auto{margin-bottom:auto!important}.ms-0{margin-left:0!important}.ms-1{margin-left:.25rem!important}.ms-2{margin-left:.5rem!important}.ms-3{margin-left:1rem!important}.ms-4{margin-left:1.5rem!important}.ms-5{margin-left:3rem!important}.ms-auto{margin-left:auto!important}.p-0{padding:0!important}.p-1{padding:.25rem!important}.p-2{padding:.5rem!important}.p-3{padding:1rem!important}.p-4{padding:1.5rem!important}.p-5{padding:3rem!important}.px-0{padding-right:0!important;padding-left:0!important}.px-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-3{padding-right:1rem!important;padding-left:1rem!important}.px-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-5{padding-right:3rem!important;padding-left:3rem!important}.py-0{padding-top:0!important;padding-bottom:0!important}.py-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-0{padding-top:0!important}.pt-1{padding-top:.25rem!important}.pt-2{padding-top:.5rem!important}.pt-3{padding-top:1rem!important}.pt-4{padding-top:1.5rem!important}.pt-5{padding-top:3rem!important}.pe-0{padding-right:0!important}.pe-1{padding-right:.25rem!important}.pe-2{padding-right:.5rem!important}.pe-3{padding-right:1rem!important}.pe-4{padding-right:1.5rem!important}.pe-5{padding-right:3rem!important}.pb-0{padding-bottom:0!important}.pb-1{padding-bottom:.25rem!important}.pb-2{padding-bottom:.5rem!important}.pb-3{padding-bottom:1rem!important}.pb-4{padding-bottom:1.5rem!important}.pb-5{padding-bottom:3rem!important}.ps-0{padding-left:0!important}.ps-1{padding-left:.25rem!important}.ps-2{padding-left:.5rem!important}.ps-3{padding-left:1rem!important}.ps-4{padding-left:1.5rem!important}.ps-5{padding-left:3rem!important}.gap-0{gap:0!important}.gap-1{gap:.25rem!important}.gap-2{gap:.5rem!important}.gap-3{gap:1rem!important}.gap-4{gap:1.5rem!important}.gap-5{gap:3rem!important}.row-gap-0{row-gap:0!important}.row-gap-1{row-gap:.25rem!important}.row-gap-2{row-gap:.5rem!important}.row-gap-3{row-gap:1rem!important}.row-gap-4{row-gap:1.5rem!important}.row-gap-5{row-gap:3rem!important}.column-gap-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.font-monospace{font-family:var(--bs-font-monospace)!important}.fs-1{font-size:calc(1.375rem + 1.5vw)!important}.fs-2{font-size:calc(1.325rem + .9vw)!important}.fs-3{font-size:calc(1.3rem + .6vw)!important}.fs-4{font-size:calc(1.275rem + .3vw)!important}.fs-5{font-size:1.25rem!important}.fs-6{font-size:1rem!important}.fst-italic{font-style:italic!important}.fst-normal{font-style:normal!important}.fw-lighter{font-weight:lighter!important}.fw-light{font-weight:300!important}.fw-normal{font-weight:400!important}.fw-medium{font-weight:500!important}.fw-semibold{font-weight:600!important}.fw-bold{font-weight:700!important}.fw-bolder{font-weight:bolder!important}.lh-1{line-height:1!important}.lh-sm{line-height:1.25!important}.lh-base{line-height:1.5!important}.lh-lg{line-height:2!important}.text-start{text-align:left!important}.text-end{text-align:right!important}.text-center{text-align:center!important}.text-decoration-none{text-decoration:none!important}.text-decoration-underline{text-decoration:underline!important}.text-decoration-line-through{text-decoration:line-through!important}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-break{word-wrap:break-word!important;word-break:break-word!important}.text-primary{--bs-text-opacity:1;color:rgba(var(--bs-primary-rgb),var(--bs-text-opacity))!important}.text-secondary{--bs-text-opacity:1;color:rgba(var(--bs-secondary-rgb),var(--bs-text-opacity))!important}.text-success{--bs-text-opacity:1;color:rgba(var(--bs-success-rgb),var(--bs-text-opacity))!important}.text-info{--bs-text-opacity:1;color:rgba(var(--bs-info-rgb),var(--bs-text-opacity))!important}.text-warning{--bs-text-opacity:1;color:rgba(var(--bs-warning-rgb),var(--bs-text-opacity))!important}.text-danger{--bs-text-opacity:1;color:rgba(var(--bs-danger-rgb),var(--bs-text-opacity))!important}.text-light{--bs-text-opacity:1;color:rgba(var(--bs-light-rgb),var(--bs-text-opacity))!important}.text-dark{--bs-text-opacity:1;color:rgba(var(--bs-dark-rgb),var(--bs-text-opacity))!important}.text-black{--bs-text-opacity:1;color:rgba(var(--bs-black-rgb),var(--bs-text-opacity))!important}.text-white{--bs-text-opacity:1;color:rgba(var(--bs-white-rgb),var(--bs-text-opacity))!important}.text-body{--bs-text-opacity:1;color:rgba(var(--bs-body-color-rgb),var(--bs-text-opacity))!important}.text-muted{--bs-text-opacity:1;color:var(--bs-secondary-color)!important}.text-black-50{--bs-text-opacity:1;color:rgba(0,0,0,.5)!important}.text-white-50{--bs-text-opacity:1;color:rgba(255,255,255,.5)!important}.text-body-secondary{--bs-text-opacity:1;color:var(--bs-secondary-color)!important}.text-body-tertiary{--bs-text-opacity:1;color:var(--bs-tertiary-color)!important}.text-body-emphasis{--bs-text-opacity:1;color:var(--bs-emphasis-color)!important}.text-reset{--bs-text-opacity:1;color:inherit!important}.text-opacity-25{--bs-text-opacity:0.25}.text-opacity-50{--bs-text-opacity:0.5}.text-opacity-75{--bs-text-opacity:0.75}.text-opacity-100{--bs-text-opacity:1}.text-primary-emphasis{color:var(--bs-primary-text-emphasis)!important}.text-secondary-emphasis{color:var(--bs-secondary-text-emphasis)!important}.text-success-emphasis{color:var(--bs-success-text-emphasis)!important}.text-info-emphasis{color:var(--bs-info-text-emphasis)!important}.text-warning-emphasis{color:var(--bs-warning-text-emphasis)!important}.text-danger-emphasis{color:var(--bs-danger-text-emphasis)!important}.text-light-emphasis{color:var(--bs-light-text-emphasis)!important}.text-dark-emphasis{color:var(--bs-dark-text-emphasis)!important}.link-opacity-10{--bs-link-opacity:0.1}.link-opacity-10-hover:hover{--bs-link-opacity:0.1}.link-opacity-25{--bs-link-opacity:0.25}.link-opacity-25-hover:hover{--bs-link-opacity:0.25}.link-opacity-50{--bs-link-opacity:0.5}.link-opacity-50-hover:hover{--bs-link-opacity:0.5}.link-opacity-75{--bs-link-opacity:0.75}.link-opacity-75-hover:hover{--bs-link-opacity:0.75}.link-opacity-100{--bs-link-opacity:1}.link-opacity-100-hover:hover{--bs-link-opacity:1}.link-offset-1{text-underline-offset:0.125em!important}.link-offset-1-hover:hover{text-underline-offset:0.125em!important}.link-offset-2{text-underline-offset:0.25em!important}.link-offset-2-hover:hover{text-underline-offset:0.25em!important}.link-offset-3{text-underline-offset:0.375em!important}.link-offset-3-hover:hover{text-underline-offset:0.375em!important}.link-underline-primary{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-primary-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-primary-rgb),var(--bs-link-underline-opacity))!important}.link-underline-secondary{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-secondary-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-secondary-rgb),var(--bs-link-underline-opacity))!important}.link-underline-success{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-success-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-success-rgb),var(--bs-link-underline-opacity))!important}.link-underline-info{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-info-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-info-rgb),var(--bs-link-underline-opacity))!important}.link-underline-warning{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-warning-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-warning-rgb),var(--bs-link-underline-opacity))!important}.link-underline-danger{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-danger-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-danger-rgb),var(--bs-link-underline-opacity))!important}.link-underline-light{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-light-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-light-rgb),var(--bs-link-underline-opacity))!important}.link-underline-dark{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-dark-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-dark-rgb),var(--bs-link-underline-opacity))!important}.link-underline{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-underline-opacity,1))!important}.link-underline-opacity-0{--bs-link-underline-opacity:0}.link-underline-opacity-0-hover:hover{--bs-link-underline-opacity:0}.link-underline-opacity-10{--bs-link-underline-opacity:0.1}.link-underline-opacity-10-hover:hover{--bs-link-underline-opacity:0.1}.link-underline-opacity-25{--bs-link-underline-opacity:0.25}.link-underline-opacity-25-hover:hover{--bs-link-underline-opacity:0.25}.link-underline-opacity-50{--bs-link-underline-opacity:0.5}.link-underline-opacity-50-hover:hover{--bs-link-underline-opacity:0.5}.link-underline-opacity-75{--bs-link-underline-opacity:0.75}.link-underline-opacity-75-hover:hover{--bs-link-underline-opacity:0.75}.link-underline-opacity-100{--bs-link-underline-opacity:1}.link-underline-opacity-100-hover:hover{--bs-link-underline-opacity:1}.bg-primary{--bs-bg-opacity:1;background-color:rgba(var(--bs-primary-rgb),var(--bs-bg-opacity))!important}.bg-secondary{--bs-bg-opacity:1;background-color:rgba(var(--bs-secondary-rgb),var(--bs-bg-opacity))!important}.bg-success{--bs-bg-opacity:1;background-color:rgba(var(--bs-success-rgb),var(--bs-bg-opacity))!important}.bg-info{--bs-bg-opacity:1;background-color:rgba(var(--bs-info-rgb),var(--bs-bg-opacity))!important}.bg-warning{--bs-bg-opacity:1;background-color:rgba(var(--bs-warning-rgb),var(--bs-bg-opacity))!important}.bg-danger{--bs-bg-opacity:1;background-color:rgba(var(--bs-danger-rgb),var(--bs-bg-opacity))!important}.bg-light{--bs-bg-opacity:1;background-color:rgba(var(--bs-light-rgb),var(--bs-bg-opacity))!important}.bg-dark{--bs-bg-opacity:1;background-color:rgba(var(--bs-dark-rgb),var(--bs-bg-opacity))!important}.bg-black{--bs-bg-opacity:1;background-color:rgba(var(--bs-black-rgb),var(--bs-bg-opacity))!important}.bg-white{--bs-bg-opacity:1;background-color:rgba(var(--bs-white-rgb),var(--bs-bg-opacity))!important}.bg-body{--bs-bg-opacity:1;background-color:rgba(var(--bs-body-bg-rgb),var(--bs-bg-opacity))!important}.bg-transparent{--bs-bg-opacity:1;background-color:transparent!important}.bg-body-secondary{--bs-bg-opacity:1;background-color:rgba(var(--bs-secondary-bg-rgb),var(--bs-bg-opacity))!important}.bg-body-tertiary{--bs-bg-opacity:1;background-color:rgba(var(--bs-tertiary-bg-rgb),var(--bs-bg-opacity))!important}.bg-opacity-10{--bs-bg-opacity:0.1}.bg-opacity-25{--bs-bg-opacity:0.25}.bg-opacity-50{--bs-bg-opacity:0.5}.bg-opacity-75{--bs-bg-opacity:0.75}.bg-opacity-100{--bs-bg-opacity:1}.bg-primary-subtle{background-color:var(--bs-primary-bg-subtle)!important}.bg-secondary-subtle{background-color:var(--bs-secondary-bg-subtle)!important}.bg-success-subtle{background-color:var(--bs-success-bg-subtle)!important}.bg-info-subtle{background-color:var(--bs-info-bg-subtle)!important}.bg-warning-subtle{background-color:var(--bs-warning-bg-subtle)!important}.bg-danger-subtle{background-color:var(--bs-danger-bg-subtle)!important}.bg-light-subtle{background-color:var(--bs-light-bg-subtle)!important}.bg-dark-subtle{background-color:var(--bs-dark-bg-subtle)!important}.bg-gradient{background-image:var(--bs-gradient)!important}.user-select-all{-webkit-user-select:all!important;-moz-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;user-select:none!important}.pe-none{pointer-events:none!important}.pe-auto{pointer-events:auto!important}.rounded{border-radius:var(--bs-border-radius)!important}.rounded-0{border-radius:0!important}.rounded-1{border-radius:var(--bs-border-radius-sm)!important}.rounded-2{border-radius:var(--bs-border-radius)!important}.rounded-3{border-radius:var(--bs-border-radius-lg)!important}.rounded-4{border-radius:var(--bs-border-radius-xl)!important}.rounded-5{border-radius:var(--bs-border-radius-xxl)!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:var(--bs-border-radius-pill)!important}.rounded-top{border-top-left-radius:var(--bs-border-radius)!important;border-top-right-radius:var(--bs-border-radius)!important}.rounded-top-0{border-top-left-radius:0!important;border-top-right-radius:0!important}.rounded-top-1{border-top-left-radius:var(--bs-border-radius-sm)!important;border-top-right-radius:var(--bs-border-radius-sm)!important}.rounded-top-2{border-top-left-radius:var(--bs-border-radius)!important;border-top-right-radius:var(--bs-border-radius)!important}.rounded-top-3{border-top-left-radius:var(--bs-border-radius-lg)!important;border-top-right-radius:var(--bs-border-radius-lg)!important}.rounded-top-4{border-top-left-radius:var(--bs-border-radius-xl)!important;border-top-right-radius:var(--bs-border-radius-xl)!important}.rounded-top-5{border-top-left-radius:var(--bs-border-radius-xxl)!important;border-top-right-radius:var(--bs-border-radius-xxl)!important}.rounded-top-circle{border-top-left-radius:50%!important;border-top-right-radius:50%!important}.rounded-top-pill{border-top-left-radius:var(--bs-border-radius-pill)!important;border-top-right-radius:var(--bs-border-radius-pill)!important}.rounded-end{border-top-right-radius:var(--bs-border-radius)!important;border-bottom-right-radius:var(--bs-border-radius)!important}.rounded-end-0{border-top-right-radius:0!important;border-bottom-right-radius:0!important}.rounded-end-1{border-top-right-radius:var(--bs-border-radius-sm)!important;border-bottom-right-radius:var(--bs-border-radius-sm)!important}.rounded-end-2{border-top-right-radius:var(--bs-border-radius)!important;border-bottom-right-radius:var(--bs-border-radius)!important}.rounded-end-3{border-top-right-radius:var(--bs-border-radius-lg)!important;border-bottom-right-radius:var(--bs-border-radius-lg)!important}.rounded-end-4{border-top-right-radius:var(--bs-border-radius-xl)!important;border-bottom-right-radius:var(--bs-border-radius-xl)!important}.rounded-end-5{border-top-right-radius:var(--bs-border-radius-xxl)!important;border-bottom-right-radius:var(--bs-border-radius-xxl)!important}.rounded-end-circle{border-top-right-radius:50%!important;border-bottom-right-radius:50%!important}.rounded-end-pill{border-top-right-radius:var(--bs-border-radius-pill)!important;border-bottom-right-radius:var(--bs-border-radius-pill)!important}.rounded-bottom{border-bottom-right-radius:var(--bs-border-radius)!important;border-bottom-left-radius:var(--bs-border-radius)!important}.rounded-bottom-0{border-bottom-right-radius:0!important;border-bottom-left-radius:0!important}.rounded-bottom-1{border-bottom-right-radius:var(--bs-border-radius-sm)!important;border-bottom-left-radius:var(--bs-border-radius-sm)!important}.rounded-bottom-2{border-bottom-right-radius:var(--bs-border-radius)!important;border-bottom-left-radius:var(--bs-border-radius)!important}.rounded-bottom-3{border-bottom-right-radius:var(--bs-border-radius-lg)!important;border-bottom-left-radius:var(--bs-border-radius-lg)!important}.rounded-bottom-4{border-bottom-right-radius:var(--bs-border-radius-xl)!important;border-bottom-left-radius:var(--bs-border-radius-xl)!important}.rounded-bottom-5{border-bottom-right-radius:var(--bs-border-radius-xxl)!important;border-bottom-left-radius:var(--bs-border-radius-xxl)!important}.rounded-bottom-circle{border-bottom-right-radius:50%!important;border-bottom-left-radius:50%!important}.rounded-bottom-pill{border-bottom-right-radius:var(--bs-border-radius-pill)!important;border-bottom-left-radius:var(--bs-border-radius-pill)!important}.rounded-start{border-bottom-left-radius:var(--bs-border-radius)!important;border-top-left-radius:var(--bs-border-radius)!important}.rounded-start-0{border-bottom-left-radius:0!important;border-top-left-radius:0!important}.rounded-start-1{border-bottom-left-radius:var(--bs-border-radius-sm)!important;border-top-left-radius:var(--bs-border-radius-sm)!important}.rounded-start-2{border-bottom-left-radius:var(--bs-border-radius)!important;border-top-left-radius:var(--bs-border-radius)!important}.rounded-start-3{border-bottom-left-radius:var(--bs-border-radius-lg)!important;border-top-left-radius:var(--bs-border-radius-lg)!important}.rounded-start-4{border-bottom-left-radius:var(--bs-border-radius-xl)!important;border-top-left-radius:var(--bs-border-radius-xl)!important}.rounded-start-5{border-bottom-left-radius:var(--bs-border-radius-xxl)!important;border-top-left-radius:var(--bs-border-radius-xxl)!important}.rounded-start-circle{border-bottom-left-radius:50%!important;border-top-left-radius:50%!important}.rounded-start-pill{border-bottom-left-radius:var(--bs-border-radius-pill)!important;border-top-left-radius:var(--bs-border-radius-pill)!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}.z-n1{z-index:-1!important}.z-0{z-index:0!important}.z-1{z-index:1!important}.z-2{z-index:2!important}.z-3{z-index:3!important}@media (min-width:576px){.float-sm-start{float:left!important}.float-sm-end{float:right!important}.float-sm-none{float:none!important}.object-fit-sm-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-sm-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-sm-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-sm-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-sm-none{-o-object-fit:none!important;object-fit:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-grid{display:grid!important}.d-sm-inline-grid{display:inline-grid!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:flex!important}.d-sm-inline-flex{display:inline-flex!important}.d-sm-none{display:none!important}.flex-sm-fill{flex:1 1 auto!important}.flex-sm-row{flex-direction:row!important}.flex-sm-column{flex-direction:column!important}.flex-sm-row-reverse{flex-direction:row-reverse!important}.flex-sm-column-reverse{flex-direction:column-reverse!important}.flex-sm-grow-0{flex-grow:0!important}.flex-sm-grow-1{flex-grow:1!important}.flex-sm-shrink-0{flex-shrink:0!important}.flex-sm-shrink-1{flex-shrink:1!important}.flex-sm-wrap{flex-wrap:wrap!important}.flex-sm-nowrap{flex-wrap:nowrap!important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-sm-start{justify-content:flex-start!important}.justify-content-sm-end{justify-content:flex-end!important}.justify-content-sm-center{justify-content:center!important}.justify-content-sm-between{justify-content:space-between!important}.justify-content-sm-around{justify-content:space-around!important}.justify-content-sm-evenly{justify-content:space-evenly!important}.align-items-sm-start{align-items:flex-start!important}.align-items-sm-end{align-items:flex-end!important}.align-items-sm-center{align-items:center!important}.align-items-sm-baseline{align-items:baseline!important}.align-items-sm-stretch{align-items:stretch!important}.align-content-sm-start{align-content:flex-start!important}.align-content-sm-end{align-content:flex-end!important}.align-content-sm-center{align-content:center!important}.align-content-sm-between{align-content:space-between!important}.align-content-sm-around{align-content:space-around!important}.align-content-sm-stretch{align-content:stretch!important}.align-self-sm-auto{align-self:auto!important}.align-self-sm-start{align-self:flex-start!important}.align-self-sm-end{align-self:flex-end!important}.align-self-sm-center{align-self:center!important}.align-self-sm-baseline{align-self:baseline!important}.align-self-sm-stretch{align-self:stretch!important}.order-sm-first{order:-1!important}.order-sm-0{order:0!important}.order-sm-1{order:1!important}.order-sm-2{order:2!important}.order-sm-3{order:3!important}.order-sm-4{order:4!important}.order-sm-5{order:5!important}.order-sm-last{order:6!important}.m-sm-0{margin:0!important}.m-sm-1{margin:.25rem!important}.m-sm-2{margin:.5rem!important}.m-sm-3{margin:1rem!important}.m-sm-4{margin:1.5rem!important}.m-sm-5{margin:3rem!important}.m-sm-auto{margin:auto!important}.mx-sm-0{margin-right:0!important;margin-left:0!important}.mx-sm-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-sm-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-sm-3{margin-right:1rem!important;margin-left:1rem!important}.mx-sm-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-sm-5{margin-right:3rem!important;margin-left:3rem!important}.mx-sm-auto{margin-right:auto!important;margin-left:auto!important}.my-sm-0{margin-top:0!important;margin-bottom:0!important}.my-sm-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-sm-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-sm-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-sm-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-sm-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-sm-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-sm-0{margin-top:0!important}.mt-sm-1{margin-top:.25rem!important}.mt-sm-2{margin-top:.5rem!important}.mt-sm-3{margin-top:1rem!important}.mt-sm-4{margin-top:1.5rem!important}.mt-sm-5{margin-top:3rem!important}.mt-sm-auto{margin-top:auto!important}.me-sm-0{margin-right:0!important}.me-sm-1{margin-right:.25rem!important}.me-sm-2{margin-right:.5rem!important}.me-sm-3{margin-right:1rem!important}.me-sm-4{margin-right:1.5rem!important}.me-sm-5{margin-right:3rem!important}.me-sm-auto{margin-right:auto!important}.mb-sm-0{margin-bottom:0!important}.mb-sm-1{margin-bottom:.25rem!important}.mb-sm-2{margin-bottom:.5rem!important}.mb-sm-3{margin-bottom:1rem!important}.mb-sm-4{margin-bottom:1.5rem!important}.mb-sm-5{margin-bottom:3rem!important}.mb-sm-auto{margin-bottom:auto!important}.ms-sm-0{margin-left:0!important}.ms-sm-1{margin-left:.25rem!important}.ms-sm-2{margin-left:.5rem!important}.ms-sm-3{margin-left:1rem!important}.ms-sm-4{margin-left:1.5rem!important}.ms-sm-5{margin-left:3rem!important}.ms-sm-auto{margin-left:auto!important}.p-sm-0{padding:0!important}.p-sm-1{padding:.25rem!important}.p-sm-2{padding:.5rem!important}.p-sm-3{padding:1rem!important}.p-sm-4{padding:1.5rem!important}.p-sm-5{padding:3rem!important}.px-sm-0{padding-right:0!important;padding-left:0!important}.px-sm-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-sm-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-sm-3{padding-right:1rem!important;padding-left:1rem!important}.px-sm-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-sm-5{padding-right:3rem!important;padding-left:3rem!important}.py-sm-0{padding-top:0!important;padding-bottom:0!important}.py-sm-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-sm-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-sm-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-sm-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-sm-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-sm-0{padding-top:0!important}.pt-sm-1{padding-top:.25rem!important}.pt-sm-2{padding-top:.5rem!important}.pt-sm-3{padding-top:1rem!important}.pt-sm-4{padding-top:1.5rem!important}.pt-sm-5{padding-top:3rem!important}.pe-sm-0{padding-right:0!important}.pe-sm-1{padding-right:.25rem!important}.pe-sm-2{padding-right:.5rem!important}.pe-sm-3{padding-right:1rem!important}.pe-sm-4{padding-right:1.5rem!important}.pe-sm-5{padding-right:3rem!important}.pb-sm-0{padding-bottom:0!important}.pb-sm-1{padding-bottom:.25rem!important}.pb-sm-2{padding-bottom:.5rem!important}.pb-sm-3{padding-bottom:1rem!important}.pb-sm-4{padding-bottom:1.5rem!important}.pb-sm-5{padding-bottom:3rem!important}.ps-sm-0{padding-left:0!important}.ps-sm-1{padding-left:.25rem!important}.ps-sm-2{padding-left:.5rem!important}.ps-sm-3{padding-left:1rem!important}.ps-sm-4{padding-left:1.5rem!important}.ps-sm-5{padding-left:3rem!important}.gap-sm-0{gap:0!important}.gap-sm-1{gap:.25rem!important}.gap-sm-2{gap:.5rem!important}.gap-sm-3{gap:1rem!important}.gap-sm-4{gap:1.5rem!important}.gap-sm-5{gap:3rem!important}.row-gap-sm-0{row-gap:0!important}.row-gap-sm-1{row-gap:.25rem!important}.row-gap-sm-2{row-gap:.5rem!important}.row-gap-sm-3{row-gap:1rem!important}.row-gap-sm-4{row-gap:1.5rem!important}.row-gap-sm-5{row-gap:3rem!important}.column-gap-sm-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-sm-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-sm-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-sm-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-sm-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-sm-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-sm-start{text-align:left!important}.text-sm-end{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.float-md-start{float:left!important}.float-md-end{float:right!important}.float-md-none{float:none!important}.object-fit-md-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-md-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-md-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-md-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-md-none{-o-object-fit:none!important;object-fit:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-grid{display:grid!important}.d-md-inline-grid{display:inline-grid!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:flex!important}.d-md-inline-flex{display:inline-flex!important}.d-md-none{display:none!important}.flex-md-fill{flex:1 1 auto!important}.flex-md-row{flex-direction:row!important}.flex-md-column{flex-direction:column!important}.flex-md-row-reverse{flex-direction:row-reverse!important}.flex-md-column-reverse{flex-direction:column-reverse!important}.flex-md-grow-0{flex-grow:0!important}.flex-md-grow-1{flex-grow:1!important}.flex-md-shrink-0{flex-shrink:0!important}.flex-md-shrink-1{flex-shrink:1!important}.flex-md-wrap{flex-wrap:wrap!important}.flex-md-nowrap{flex-wrap:nowrap!important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-md-start{justify-content:flex-start!important}.justify-content-md-end{justify-content:flex-end!important}.justify-content-md-center{justify-content:center!important}.justify-content-md-between{justify-content:space-between!important}.justify-content-md-around{justify-content:space-around!important}.justify-content-md-evenly{justify-content:space-evenly!important}.align-items-md-start{align-items:flex-start!important}.align-items-md-end{align-items:flex-end!important}.align-items-md-center{align-items:center!important}.align-items-md-baseline{align-items:baseline!important}.align-items-md-stretch{align-items:stretch!important}.align-content-md-start{align-content:flex-start!important}.align-content-md-end{align-content:flex-end!important}.align-content-md-center{align-content:center!important}.align-content-md-between{align-content:space-between!important}.align-content-md-around{align-content:space-around!important}.align-content-md-stretch{align-content:stretch!important}.align-self-md-auto{align-self:auto!important}.align-self-md-start{align-self:flex-start!important}.align-self-md-end{align-self:flex-end!important}.align-self-md-center{align-self:center!important}.align-self-md-baseline{align-self:baseline!important}.align-self-md-stretch{align-self:stretch!important}.order-md-first{order:-1!important}.order-md-0{order:0!important}.order-md-1{order:1!important}.order-md-2{order:2!important}.order-md-3{order:3!important}.order-md-4{order:4!important}.order-md-5{order:5!important}.order-md-last{order:6!important}.m-md-0{margin:0!important}.m-md-1{margin:.25rem!important}.m-md-2{margin:.5rem!important}.m-md-3{margin:1rem!important}.m-md-4{margin:1.5rem!important}.m-md-5{margin:3rem!important}.m-md-auto{margin:auto!important}.mx-md-0{margin-right:0!important;margin-left:0!important}.mx-md-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-md-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-md-3{margin-right:1rem!important;margin-left:1rem!important}.mx-md-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-md-5{margin-right:3rem!important;margin-left:3rem!important}.mx-md-auto{margin-right:auto!important;margin-left:auto!important}.my-md-0{margin-top:0!important;margin-bottom:0!important}.my-md-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-md-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-md-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-md-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-md-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-md-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-md-0{margin-top:0!important}.mt-md-1{margin-top:.25rem!important}.mt-md-2{margin-top:.5rem!important}.mt-md-3{margin-top:1rem!important}.mt-md-4{margin-top:1.5rem!important}.mt-md-5{margin-top:3rem!important}.mt-md-auto{margin-top:auto!important}.me-md-0{margin-right:0!important}.me-md-1{margin-right:.25rem!important}.me-md-2{margin-right:.5rem!important}.me-md-3{margin-right:1rem!important}.me-md-4{margin-right:1.5rem!important}.me-md-5{margin-right:3rem!important}.me-md-auto{margin-right:auto!important}.mb-md-0{margin-bottom:0!important}.mb-md-1{margin-bottom:.25rem!important}.mb-md-2{margin-bottom:.5rem!important}.mb-md-3{margin-bottom:1rem!important}.mb-md-4{margin-bottom:1.5rem!important}.mb-md-5{margin-bottom:3rem!important}.mb-md-auto{margin-bottom:auto!important}.ms-md-0{margin-left:0!important}.ms-md-1{margin-left:.25rem!important}.ms-md-2{margin-left:.5rem!important}.ms-md-3{margin-left:1rem!important}.ms-md-4{margin-left:1.5rem!important}.ms-md-5{margin-left:3rem!important}.ms-md-auto{margin-left:auto!important}.p-md-0{padding:0!important}.p-md-1{padding:.25rem!important}.p-md-2{padding:.5rem!important}.p-md-3{padding:1rem!important}.p-md-4{padding:1.5rem!important}.p-md-5{padding:3rem!important}.px-md-0{padding-right:0!important;padding-left:0!important}.px-md-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-md-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-md-3{padding-right:1rem!important;padding-left:1rem!important}.px-md-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-md-5{padding-right:3rem!important;padding-left:3rem!important}.py-md-0{padding-top:0!important;padding-bottom:0!important}.py-md-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-md-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-md-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-md-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-md-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-md-0{padding-top:0!important}.pt-md-1{padding-top:.25rem!important}.pt-md-2{padding-top:.5rem!important}.pt-md-3{padding-top:1rem!important}.pt-md-4{padding-top:1.5rem!important}.pt-md-5{padding-top:3rem!important}.pe-md-0{padding-right:0!important}.pe-md-1{padding-right:.25rem!important}.pe-md-2{padding-right:.5rem!important}.pe-md-3{padding-right:1rem!important}.pe-md-4{padding-right:1.5rem!important}.pe-md-5{padding-right:3rem!important}.pb-md-0{padding-bottom:0!important}.pb-md-1{padding-bottom:.25rem!important}.pb-md-2{padding-bottom:.5rem!important}.pb-md-3{padding-bottom:1rem!important}.pb-md-4{padding-bottom:1.5rem!important}.pb-md-5{padding-bottom:3rem!important}.ps-md-0{padding-left:0!important}.ps-md-1{padding-left:.25rem!important}.ps-md-2{padding-left:.5rem!important}.ps-md-3{padding-left:1rem!important}.ps-md-4{padding-left:1.5rem!important}.ps-md-5{padding-left:3rem!important}.gap-md-0{gap:0!important}.gap-md-1{gap:.25rem!important}.gap-md-2{gap:.5rem!important}.gap-md-3{gap:1rem!important}.gap-md-4{gap:1.5rem!important}.gap-md-5{gap:3rem!important}.row-gap-md-0{row-gap:0!important}.row-gap-md-1{row-gap:.25rem!important}.row-gap-md-2{row-gap:.5rem!important}.row-gap-md-3{row-gap:1rem!important}.row-gap-md-4{row-gap:1.5rem!important}.row-gap-md-5{row-gap:3rem!important}.column-gap-md-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-md-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-md-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-md-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-md-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-md-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-md-start{text-align:left!important}.text-md-end{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.float-lg-start{float:left!important}.float-lg-end{float:right!important}.float-lg-none{float:none!important}.object-fit-lg-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-lg-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-lg-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-lg-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-lg-none{-o-object-fit:none!important;object-fit:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-grid{display:grid!important}.d-lg-inline-grid{display:inline-grid!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:flex!important}.d-lg-inline-flex{display:inline-flex!important}.d-lg-none{display:none!important}.flex-lg-fill{flex:1 1 auto!important}.flex-lg-row{flex-direction:row!important}.flex-lg-column{flex-direction:column!important}.flex-lg-row-reverse{flex-direction:row-reverse!important}.flex-lg-column-reverse{flex-direction:column-reverse!important}.flex-lg-grow-0{flex-grow:0!important}.flex-lg-grow-1{flex-grow:1!important}.flex-lg-shrink-0{flex-shrink:0!important}.flex-lg-shrink-1{flex-shrink:1!important}.flex-lg-wrap{flex-wrap:wrap!important}.flex-lg-nowrap{flex-wrap:nowrap!important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-lg-start{justify-content:flex-start!important}.justify-content-lg-end{justify-content:flex-end!important}.justify-content-lg-center{justify-content:center!important}.justify-content-lg-between{justify-content:space-between!important}.justify-content-lg-around{justify-content:space-around!important}.justify-content-lg-evenly{justify-content:space-evenly!important}.align-items-lg-start{align-items:flex-start!important}.align-items-lg-end{align-items:flex-end!important}.align-items-lg-center{align-items:center!important}.align-items-lg-baseline{align-items:baseline!important}.align-items-lg-stretch{align-items:stretch!important}.align-content-lg-start{align-content:flex-start!important}.align-content-lg-end{align-content:flex-end!important}.align-content-lg-center{align-content:center!important}.align-content-lg-between{align-content:space-between!important}.align-content-lg-around{align-content:space-around!important}.align-content-lg-stretch{align-content:stretch!important}.align-self-lg-auto{align-self:auto!important}.align-self-lg-start{align-self:flex-start!important}.align-self-lg-end{align-self:flex-end!important}.align-self-lg-center{align-self:center!important}.align-self-lg-baseline{align-self:baseline!important}.align-self-lg-stretch{align-self:stretch!important}.order-lg-first{order:-1!important}.order-lg-0{order:0!important}.order-lg-1{order:1!important}.order-lg-2{order:2!important}.order-lg-3{order:3!important}.order-lg-4{order:4!important}.order-lg-5{order:5!important}.order-lg-last{order:6!important}.m-lg-0{margin:0!important}.m-lg-1{margin:.25rem!important}.m-lg-2{margin:.5rem!important}.m-lg-3{margin:1rem!important}.m-lg-4{margin:1.5rem!important}.m-lg-5{margin:3rem!important}.m-lg-auto{margin:auto!important}.mx-lg-0{margin-right:0!important;margin-left:0!important}.mx-lg-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-lg-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-lg-3{margin-right:1rem!important;margin-left:1rem!important}.mx-lg-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-lg-5{margin-right:3rem!important;margin-left:3rem!important}.mx-lg-auto{margin-right:auto!important;margin-left:auto!important}.my-lg-0{margin-top:0!important;margin-bottom:0!important}.my-lg-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-lg-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-lg-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-lg-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-lg-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-lg-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-lg-0{margin-top:0!important}.mt-lg-1{margin-top:.25rem!important}.mt-lg-2{margin-top:.5rem!important}.mt-lg-3{margin-top:1rem!important}.mt-lg-4{margin-top:1.5rem!important}.mt-lg-5{margin-top:3rem!important}.mt-lg-auto{margin-top:auto!important}.me-lg-0{margin-right:0!important}.me-lg-1{margin-right:.25rem!important}.me-lg-2{margin-right:.5rem!important}.me-lg-3{margin-right:1rem!important}.me-lg-4{margin-right:1.5rem!important}.me-lg-5{margin-right:3rem!important}.me-lg-auto{margin-right:auto!important}.mb-lg-0{margin-bottom:0!important}.mb-lg-1{margin-bottom:.25rem!important}.mb-lg-2{margin-bottom:.5rem!important}.mb-lg-3{margin-bottom:1rem!important}.mb-lg-4{margin-bottom:1.5rem!important}.mb-lg-5{margin-bottom:3rem!important}.mb-lg-auto{margin-bottom:auto!important}.ms-lg-0{margin-left:0!important}.ms-lg-1{margin-left:.25rem!important}.ms-lg-2{margin-left:.5rem!important}.ms-lg-3{margin-left:1rem!important}.ms-lg-4{margin-left:1.5rem!important}.ms-lg-5{margin-left:3rem!important}.ms-lg-auto{margin-left:auto!important}.p-lg-0{padding:0!important}.p-lg-1{padding:.25rem!important}.p-lg-2{padding:.5rem!important}.p-lg-3{padding:1rem!important}.p-lg-4{padding:1.5rem!important}.p-lg-5{padding:3rem!important}.px-lg-0{padding-right:0!important;padding-left:0!important}.px-lg-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-lg-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-lg-3{padding-right:1rem!important;padding-left:1rem!important}.px-lg-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-lg-5{padding-right:3rem!important;padding-left:3rem!important}.py-lg-0{padding-top:0!important;padding-bottom:0!important}.py-lg-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-lg-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-lg-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-lg-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-lg-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-lg-0{padding-top:0!important}.pt-lg-1{padding-top:.25rem!important}.pt-lg-2{padding-top:.5rem!important}.pt-lg-3{padding-top:1rem!important}.pt-lg-4{padding-top:1.5rem!important}.pt-lg-5{padding-top:3rem!important}.pe-lg-0{padding-right:0!important}.pe-lg-1{padding-right:.25rem!important}.pe-lg-2{padding-right:.5rem!important}.pe-lg-3{padding-right:1rem!important}.pe-lg-4{padding-right:1.5rem!important}.pe-lg-5{padding-right:3rem!important}.pb-lg-0{padding-bottom:0!important}.pb-lg-1{padding-bottom:.25rem!important}.pb-lg-2{padding-bottom:.5rem!important}.pb-lg-3{padding-bottom:1rem!important}.pb-lg-4{padding-bottom:1.5rem!important}.pb-lg-5{padding-bottom:3rem!important}.ps-lg-0{padding-left:0!important}.ps-lg-1{padding-left:.25rem!important}.ps-lg-2{padding-left:.5rem!important}.ps-lg-3{padding-left:1rem!important}.ps-lg-4{padding-left:1.5rem!important}.ps-lg-5{padding-left:3rem!important}.gap-lg-0{gap:0!important}.gap-lg-1{gap:.25rem!important}.gap-lg-2{gap:.5rem!important}.gap-lg-3{gap:1rem!important}.gap-lg-4{gap:1.5rem!important}.gap-lg-5{gap:3rem!important}.row-gap-lg-0{row-gap:0!important}.row-gap-lg-1{row-gap:.25rem!important}.row-gap-lg-2{row-gap:.5rem!important}.row-gap-lg-3{row-gap:1rem!important}.row-gap-lg-4{row-gap:1.5rem!important}.row-gap-lg-5{row-gap:3rem!important}.column-gap-lg-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-lg-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-lg-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-lg-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-lg-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-lg-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-lg-start{text-align:left!important}.text-lg-end{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.float-xl-start{float:left!important}.float-xl-end{float:right!important}.float-xl-none{float:none!important}.object-fit-xl-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-xl-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-xl-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-xl-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-xl-none{-o-object-fit:none!important;object-fit:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-grid{display:grid!important}.d-xl-inline-grid{display:inline-grid!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:flex!important}.d-xl-inline-flex{display:inline-flex!important}.d-xl-none{display:none!important}.flex-xl-fill{flex:1 1 auto!important}.flex-xl-row{flex-direction:row!important}.flex-xl-column{flex-direction:column!important}.flex-xl-row-reverse{flex-direction:row-reverse!important}.flex-xl-column-reverse{flex-direction:column-reverse!important}.flex-xl-grow-0{flex-grow:0!important}.flex-xl-grow-1{flex-grow:1!important}.flex-xl-shrink-0{flex-shrink:0!important}.flex-xl-shrink-1{flex-shrink:1!important}.flex-xl-wrap{flex-wrap:wrap!important}.flex-xl-nowrap{flex-wrap:nowrap!important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-xl-start{justify-content:flex-start!important}.justify-content-xl-end{justify-content:flex-end!important}.justify-content-xl-center{justify-content:center!important}.justify-content-xl-between{justify-content:space-between!important}.justify-content-xl-around{justify-content:space-around!important}.justify-content-xl-evenly{justify-content:space-evenly!important}.align-items-xl-start{align-items:flex-start!important}.align-items-xl-end{align-items:flex-end!important}.align-items-xl-center{align-items:center!important}.align-items-xl-baseline{align-items:baseline!important}.align-items-xl-stretch{align-items:stretch!important}.align-content-xl-start{align-content:flex-start!important}.align-content-xl-end{align-content:flex-end!important}.align-content-xl-center{align-content:center!important}.align-content-xl-between{align-content:space-between!important}.align-content-xl-around{align-content:space-around!important}.align-content-xl-stretch{align-content:stretch!important}.align-self-xl-auto{align-self:auto!important}.align-self-xl-start{align-self:flex-start!important}.align-self-xl-end{align-self:flex-end!important}.align-self-xl-center{align-self:center!important}.align-self-xl-baseline{align-self:baseline!important}.align-self-xl-stretch{align-self:stretch!important}.order-xl-first{order:-1!important}.order-xl-0{order:0!important}.order-xl-1{order:1!important}.order-xl-2{order:2!important}.order-xl-3{order:3!important}.order-xl-4{order:4!important}.order-xl-5{order:5!important}.order-xl-last{order:6!important}.m-xl-0{margin:0!important}.m-xl-1{margin:.25rem!important}.m-xl-2{margin:.5rem!important}.m-xl-3{margin:1rem!important}.m-xl-4{margin:1.5rem!important}.m-xl-5{margin:3rem!important}.m-xl-auto{margin:auto!important}.mx-xl-0{margin-right:0!important;margin-left:0!important}.mx-xl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xl-auto{margin-right:auto!important;margin-left:auto!important}.my-xl-0{margin-top:0!important;margin-bottom:0!important}.my-xl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xl-0{margin-top:0!important}.mt-xl-1{margin-top:.25rem!important}.mt-xl-2{margin-top:.5rem!important}.mt-xl-3{margin-top:1rem!important}.mt-xl-4{margin-top:1.5rem!important}.mt-xl-5{margin-top:3rem!important}.mt-xl-auto{margin-top:auto!important}.me-xl-0{margin-right:0!important}.me-xl-1{margin-right:.25rem!important}.me-xl-2{margin-right:.5rem!important}.me-xl-3{margin-right:1rem!important}.me-xl-4{margin-right:1.5rem!important}.me-xl-5{margin-right:3rem!important}.me-xl-auto{margin-right:auto!important}.mb-xl-0{margin-bottom:0!important}.mb-xl-1{margin-bottom:.25rem!important}.mb-xl-2{margin-bottom:.5rem!important}.mb-xl-3{margin-bottom:1rem!important}.mb-xl-4{margin-bottom:1.5rem!important}.mb-xl-5{margin-bottom:3rem!important}.mb-xl-auto{margin-bottom:auto!important}.ms-xl-0{margin-left:0!important}.ms-xl-1{margin-left:.25rem!important}.ms-xl-2{margin-left:.5rem!important}.ms-xl-3{margin-left:1rem!important}.ms-xl-4{margin-left:1.5rem!important}.ms-xl-5{margin-left:3rem!important}.ms-xl-auto{margin-left:auto!important}.p-xl-0{padding:0!important}.p-xl-1{padding:.25rem!important}.p-xl-2{padding:.5rem!important}.p-xl-3{padding:1rem!important}.p-xl-4{padding:1.5rem!important}.p-xl-5{padding:3rem!important}.px-xl-0{padding-right:0!important;padding-left:0!important}.px-xl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xl-0{padding-top:0!important;padding-bottom:0!important}.py-xl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xl-0{padding-top:0!important}.pt-xl-1{padding-top:.25rem!important}.pt-xl-2{padding-top:.5rem!important}.pt-xl-3{padding-top:1rem!important}.pt-xl-4{padding-top:1.5rem!important}.pt-xl-5{padding-top:3rem!important}.pe-xl-0{padding-right:0!important}.pe-xl-1{padding-right:.25rem!important}.pe-xl-2{padding-right:.5rem!important}.pe-xl-3{padding-right:1rem!important}.pe-xl-4{padding-right:1.5rem!important}.pe-xl-5{padding-right:3rem!important}.pb-xl-0{padding-bottom:0!important}.pb-xl-1{padding-bottom:.25rem!important}.pb-xl-2{padding-bottom:.5rem!important}.pb-xl-3{padding-bottom:1rem!important}.pb-xl-4{padding-bottom:1.5rem!important}.pb-xl-5{padding-bottom:3rem!important}.ps-xl-0{padding-left:0!important}.ps-xl-1{padding-left:.25rem!important}.ps-xl-2{padding-left:.5rem!important}.ps-xl-3{padding-left:1rem!important}.ps-xl-4{padding-left:1.5rem!important}.ps-xl-5{padding-left:3rem!important}.gap-xl-0{gap:0!important}.gap-xl-1{gap:.25rem!important}.gap-xl-2{gap:.5rem!important}.gap-xl-3{gap:1rem!important}.gap-xl-4{gap:1.5rem!important}.gap-xl-5{gap:3rem!important}.row-gap-xl-0{row-gap:0!important}.row-gap-xl-1{row-gap:.25rem!important}.row-gap-xl-2{row-gap:.5rem!important}.row-gap-xl-3{row-gap:1rem!important}.row-gap-xl-4{row-gap:1.5rem!important}.row-gap-xl-5{row-gap:3rem!important}.column-gap-xl-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-xl-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-xl-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-xl-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-xl-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-xl-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-xl-start{text-align:left!important}.text-xl-end{text-align:right!important}.text-xl-center{text-align:center!important}}@media (min-width:1400px){.float-xxl-start{float:left!important}.float-xxl-end{float:right!important}.float-xxl-none{float:none!important}.object-fit-xxl-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-xxl-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-xxl-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-xxl-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-xxl-none{-o-object-fit:none!important;object-fit:none!important}.d-xxl-inline{display:inline!important}.d-xxl-inline-block{display:inline-block!important}.d-xxl-block{display:block!important}.d-xxl-grid{display:grid!important}.d-xxl-inline-grid{display:inline-grid!important}.d-xxl-table{display:table!important}.d-xxl-table-row{display:table-row!important}.d-xxl-table-cell{display:table-cell!important}.d-xxl-flex{display:flex!important}.d-xxl-inline-flex{display:inline-flex!important}.d-xxl-none{display:none!important}.flex-xxl-fill{flex:1 1 auto!important}.flex-xxl-row{flex-direction:row!important}.flex-xxl-column{flex-direction:column!important}.flex-xxl-row-reverse{flex-direction:row-reverse!important}.flex-xxl-column-reverse{flex-direction:column-reverse!important}.flex-xxl-grow-0{flex-grow:0!important}.flex-xxl-grow-1{flex-grow:1!important}.flex-xxl-shrink-0{flex-shrink:0!important}.flex-xxl-shrink-1{flex-shrink:1!important}.flex-xxl-wrap{flex-wrap:wrap!important}.flex-xxl-nowrap{flex-wrap:nowrap!important}.flex-xxl-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-xxl-start{justify-content:flex-start!important}.justify-content-xxl-end{justify-content:flex-end!important}.justify-content-xxl-center{justify-content:center!important}.justify-content-xxl-between{justify-content:space-between!important}.justify-content-xxl-around{justify-content:space-around!important}.justify-content-xxl-evenly{justify-content:space-evenly!important}.align-items-xxl-start{align-items:flex-start!important}.align-items-xxl-end{align-items:flex-end!important}.align-items-xxl-center{align-items:center!important}.align-items-xxl-baseline{align-items:baseline!important}.align-items-xxl-stretch{align-items:stretch!important}.align-content-xxl-start{align-content:flex-start!important}.align-content-xxl-end{align-content:flex-end!important}.align-content-xxl-center{align-content:center!important}.align-content-xxl-between{align-content:space-between!important}.align-content-xxl-around{align-content:space-around!important}.align-content-xxl-stretch{align-content:stretch!important}.align-self-xxl-auto{align-self:auto!important}.align-self-xxl-start{align-self:flex-start!important}.align-self-xxl-end{align-self:flex-end!important}.align-self-xxl-center{align-self:center!important}.align-self-xxl-baseline{align-self:baseline!important}.align-self-xxl-stretch{align-self:stretch!important}.order-xxl-first{order:-1!important}.order-xxl-0{order:0!important}.order-xxl-1{order:1!important}.order-xxl-2{order:2!important}.order-xxl-3{order:3!important}.order-xxl-4{order:4!important}.order-xxl-5{order:5!important}.order-xxl-last{order:6!important}.m-xxl-0{margin:0!important}.m-xxl-1{margin:.25rem!important}.m-xxl-2{margin:.5rem!important}.m-xxl-3{margin:1rem!important}.m-xxl-4{margin:1.5rem!important}.m-xxl-5{margin:3rem!important}.m-xxl-auto{margin:auto!important}.mx-xxl-0{margin-right:0!important;margin-left:0!important}.mx-xxl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xxl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xxl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xxl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xxl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xxl-auto{margin-right:auto!important;margin-left:auto!important}.my-xxl-0{margin-top:0!important;margin-bottom:0!important}.my-xxl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xxl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xxl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xxl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xxl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xxl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xxl-0{margin-top:0!important}.mt-xxl-1{margin-top:.25rem!important}.mt-xxl-2{margin-top:.5rem!important}.mt-xxl-3{margin-top:1rem!important}.mt-xxl-4{margin-top:1.5rem!important}.mt-xxl-5{margin-top:3rem!important}.mt-xxl-auto{margin-top:auto!important}.me-xxl-0{margin-right:0!important}.me-xxl-1{margin-right:.25rem!important}.me-xxl-2{margin-right:.5rem!important}.me-xxl-3{margin-right:1rem!important}.me-xxl-4{margin-right:1.5rem!important}.me-xxl-5{margin-right:3rem!important}.me-xxl-auto{margin-right:auto!important}.mb-xxl-0{margin-bottom:0!important}.mb-xxl-1{margin-bottom:.25rem!important}.mb-xxl-2{margin-bottom:.5rem!important}.mb-xxl-3{margin-bottom:1rem!important}.mb-xxl-4{margin-bottom:1.5rem!important}.mb-xxl-5{margin-bottom:3rem!important}.mb-xxl-auto{margin-bottom:auto!important}.ms-xxl-0{margin-left:0!important}.ms-xxl-1{margin-left:.25rem!important}.ms-xxl-2{margin-left:.5rem!important}.ms-xxl-3{margin-left:1rem!important}.ms-xxl-4{margin-left:1.5rem!important}.ms-xxl-5{margin-left:3rem!important}.ms-xxl-auto{margin-left:auto!important}.p-xxl-0{padding:0!important}.p-xxl-1{padding:.25rem!important}.p-xxl-2{padding:.5rem!important}.p-xxl-3{padding:1rem!important}.p-xxl-4{padding:1.5rem!important}.p-xxl-5{padding:3rem!important}.px-xxl-0{padding-right:0!important;padding-left:0!important}.px-xxl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xxl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xxl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xxl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xxl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xxl-0{padding-top:0!important;padding-bottom:0!important}.py-xxl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xxl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xxl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xxl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xxl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xxl-0{padding-top:0!important}.pt-xxl-1{padding-top:.25rem!important}.pt-xxl-2{padding-top:.5rem!important}.pt-xxl-3{padding-top:1rem!important}.pt-xxl-4{padding-top:1.5rem!important}.pt-xxl-5{padding-top:3rem!important}.pe-xxl-0{padding-right:0!important}.pe-xxl-1{padding-right:.25rem!important}.pe-xxl-2{padding-right:.5rem!important}.pe-xxl-3{padding-right:1rem!important}.pe-xxl-4{padding-right:1.5rem!important}.pe-xxl-5{padding-right:3rem!important}.pb-xxl-0{padding-bottom:0!important}.pb-xxl-1{padding-bottom:.25rem!important}.pb-xxl-2{padding-bottom:.5rem!important}.pb-xxl-3{padding-bottom:1rem!important}.pb-xxl-4{padding-bottom:1.5rem!important}.pb-xxl-5{padding-bottom:3rem!important}.ps-xxl-0{padding-left:0!important}.ps-xxl-1{padding-left:.25rem!important}.ps-xxl-2{padding-left:.5rem!important}.ps-xxl-3{padding-left:1rem!important}.ps-xxl-4{padding-left:1.5rem!important}.ps-xxl-5{padding-left:3rem!important}.gap-xxl-0{gap:0!important}.gap-xxl-1{gap:.25rem!important}.gap-xxl-2{gap:.5rem!important}.gap-xxl-3{gap:1rem!important}.gap-xxl-4{gap:1.5rem!important}.gap-xxl-5{gap:3rem!important}.row-gap-xxl-0{row-gap:0!important}.row-gap-xxl-1{row-gap:.25rem!important}.row-gap-xxl-2{row-gap:.5rem!important}.row-gap-xxl-3{row-gap:1rem!important}.row-gap-xxl-4{row-gap:1.5rem!important}.row-gap-xxl-5{row-gap:3rem!important}.column-gap-xxl-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-xxl-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-xxl-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-xxl-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-xxl-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-xxl-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-xxl-start{text-align:left!important}.text-xxl-end{text-align:right!important}.text-xxl-center{text-align:center!important}}@media (min-width:1200px){.fs-1{font-size:2.5rem!important}.fs-2{font-size:2rem!important}.fs-3{font-size:1.75rem!important}.fs-4{font-size:1.5rem!important}}@media print{.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-grid{display:grid!important}.d-print-inline-grid{display:inline-grid!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:flex!important}.d-print-inline-flex{display:inline-flex!important}.d-print-none{display:none!important}} +/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/packages/image/build/report/_css/custom.css b/packages/image/build/report/_css/custom.css new file mode 100644 index 000000000..e69de29bb diff --git a/packages/image/build/report/_css/octicons.css b/packages/image/build/report/_css/octicons.css new file mode 100644 index 000000000..31d97867a --- /dev/null +++ b/packages/image/build/report/_css/octicons.css @@ -0,0 +1,5 @@ +.octicon { + display: inline-block; + vertical-align: text-top; + fill: currentColor; +} diff --git a/packages/image/build/report/_css/style.css b/packages/image/build/report/_css/style.css new file mode 100644 index 000000000..4303bf844 --- /dev/null +++ b/packages/image/build/report/_css/style.css @@ -0,0 +1,274 @@ + +:root { + /* Implementing an auto-selection of dark/light theme via: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/light-dark */ + color-scheme: light dark; + + /* PHPUnit light/dark colors */ + --phpunit-breadcrumbs: light-dark(var(--bs-gray-200), var(--bs-gray-800)); + --phpunit-success-bar: light-dark(#28a745 ,#1f8135); + --phpunit-success-high: light-dark(#99cb84, #3d5c4e); + --phpunit-success-medium: light-dark(#c3e3b5,#3c6051); + --phpunit-success-low: light-dark(#dff0d8, #2d4431); + --phpunit-warning: light-dark(#fcf8e3, #3e3408); + --phpunit-warning-bar: light-dark(#ffc107 ,#c19406); + --phpunit-danger: light-dark(#f2dede, #42221e); + --phpunit-danger-bar: light-dark(#dc3545, #a62633); + + /* Bootstrap v5.3 default colors (light, dark) */ + --bs-body-bg-rgb: 255, 255, 255; + --bs-body-bg: light-dark(#fff, #212529); + --bs-body-color-rgb: light-dark(33, 37, 41, 222, 226, 230); + --bs-body-color: light-dark(#212529, #dee2e6); + --bs-border-color-translucent: light-dark(rgba(0, 0, 0, 0.175), rgba(255, 255, 255, 0.15)); + --bs-border-color: light-dark(#dee2e6, #495057); + --bs-code-color: light-dark(#d63384, #e685b5); + --bs-danger-bg-subtle: light-dark(#f8d7da, #2c0b0e); + --bs-danger-border-subtle: light-dark(#f1aeb5, #842029); + --bs-danger-text-emphasis: light-dark(#58151c, #ea868f); + --bs-dark-bg-subtle: light-dark(#ced4da, #1a1d20); + --bs-dark-border-subtle: light-dark(#adb5bd, #343a40); + --bs-dark-text-emphasis: light-dark(#495057, #dee2e6); + --bs-emphasis-color-rgb: 0, 0, 0; + --bs-emphasis-color: light-dark(#000, #fff); + --bs-form-invalid-border-color: light-dark(#dc3545, #ea868f); + --bs-form-invalid-color: light-dark(#dc3545, #ea868f); + --bs-form-valid-border-color: light-dark(#198754, #75b798); + --bs-form-valid-color: light-dark(#198754, #75b798); + --bs-highlight-bg: light-dark(#fff3cd, #664d03); + --bs-highlight-color: light-dark(#212529, #dee2e6); + --bs-info-bg-subtle: light-dark(#cff4fc, #032830); + --bs-info-border-subtle: light-dark(#9eeaf9, #087990); + --bs-info-text-emphasis: light-dark(#055160, #6edff6); + --bs-light-bg-subtle: light-dark(#fcfcfd, #343a40); + --bs-light-border-subtle: light-dark(#e9ecef, #495057); + --bs-light-text-emphasis: light-dark(#495057, #f8f9fa); + --bs-link-color-rgb: 13, 110, 253; + --bs-link-color: light-dark(#0d6efd, #6ea8fe); + --bs-link-hover-color-rgb: 10, 88, 202; + --bs-link-hover-color: light-dark(#0a58ca, #8bb9fe); + --bs-primary-bg-subtle: light-dark(#cfe2ff, #031633); + --bs-primary-border-subtle: light-dark(#9ec5fe, #084298); + --bs-primary-text-emphasis: light-dark(#052c65, #6ea8fe); + --bs-secondary-bg-rgb: 233, 236, 239; + --bs-secondary-bg-subtle: light-dark(#e2e3e5, #161719); + --bs-secondary-bg: light-dark(#e9ecef, #343a40); + --bs-secondary-border-subtle: light-dark(#c4c8cb, #41464b); + --bs-secondary-color-rgb: 33, 37, 41; + --bs-secondary-color: light-dark(rgba(33, 37, 41, 0.75), rgba(222, 226, 230, 0.75)); + --bs-secondary-text-emphasis: light-dark(#2b2f32, #a7acb1); + --bs-success-bg-subtle: light-dark(#d1e7dd, #051b11); + --bs-success-border-subtle: light-dark(#a3cfbb, #0f5132); + --bs-success-text-emphasis: light-dark(#0a3622, #75b798); + --bs-tertiary-bg-rgb: light-dark(248, 249, 250, 43, 48, 53); + --bs-tertiary-bg: light-dark(#f8f9fa, #2b3035); + --bs-tertiary-color-rgb: 33, 37, 41; + --bs-tertiary-color: light-dark(rgba(33, 37, 41, 0.5), rgba(222, 226, 230, 0.5)); + --bs-warning-bg-subtle: light-dark(#fff3cd, #332701); + --bs-warning-border-subtle: light-dark(#ffe69c, #997404); + --bs-warning-text-emphasis: light-dark(#664d03, #ffda6a); +} + +@media (prefers-color-scheme: dark) { + :root { + --bs-body-bg-rgb: 33, 37, 41; + --bs-emphasis-color-rgb: 255, 255, 255; + --bs-link-color-rgb: 110, 168, 254; + --bs-link-hover-color-rgb: 139, 185, 254; + --bs-secondary-bg-rgb: 52, 58, 64; + --bs-secondary-color-rgb: 222, 226, 230; + --bs-tertiary-color-rgb: 222, 226, 230; + } + + /* Invert icon's colors on dark mode to improve readability */ + img.octicon { filter: invert(1); } +} + +body { + font-family: sans-serif; + font-size: 1em; + font-kerning: normal; + text-rendering: optimizeLegibility; + padding-top: 10px; +} + +nav .breadcrumb { + border-radius: var(--bs-border-radius); + background-color: var(--phpunit-breadcrumbs); + padding: .75rem 1rem; +} + +.popover { + max-width: none; +} + +.popover-body { + max-height: 90vh; + overflow-y: auto; +} + +.octicon { + margin-right:.25em; + vertical-align: baseline; + width: 0.75em; +} + +.table-bordered>thead>tr>td { + border-bottom-width: 1px; +} + +.table tbody>tr>td, .table thead>tr>td { + padding-top: 3px; + padding-bottom: 3px; +} + +.table-condensed tbody>tr>td { + padding-top: 0; + padding-bottom: 0; +} + +.table .progress { + margin-bottom: inherit; +} + +.table-borderless th, .table-borderless td { + border: 0 !important; +} + +.table tbody tr.covered-by-large-tests, .table tbody tr.covered-by-large-tests td, li.covered-by-large-tests, tr.success, tr.success td, td.success, li.success, span.success { + background-color: var(--phpunit-success-low); +} + +.table tbody tr.covered-by-medium-tests, .table tbody tr.covered-by-medium-tests td, li.covered-by-medium-tests { + background-color: var(--phpunit-success-medium); +} + +.table tbody tr.covered-by-small-tests, .table tbody tr.covered-by-small-tests td, li.covered-by-small-tests { + background-color: var(--phpunit-success-high); +} + +.table tbody tr.warning, .table tbody tr.warning td, .table tbody td.warning, li.warning, span.warning { + background-color: var(--phpunit-warning); +} + +.table tbody tr.danger, .table tbody tr.danger td, .table tbody td.danger, li.danger, span.danger { + background-color: var(--phpunit-danger); +} + +.table tbody td.info { + background-color: rgb(from var(--bs-info) r g b / 0.25); +} + +td.big { + vertical-align: middle; + width: 117px; +} + +td.small { +} + +td.codeLine { + font-family: "Source Code Pro", var(--bs-font-monospace); + white-space: pre-wrap; +} + +td span.comment { + color: var(--bs-secondary-color); +} + +td span.default { + color: var(--bs-body-color); +} + +td span.html { + color: var(--bs-secondary-color); +} + +td span.keyword { + color: var(--bs-body-color); + font-weight: bold; +} + +pre span.string { + color: var(--bs-body-color); +} + +span.success, span.warning, span.danger { + margin-right: 2px; + padding-left: 10px; + padding-right: 10px; + text-align: center; +} + +#toplink { + position: fixed; + left: 5px; + bottom: 5px; + outline: 0; +} + +svg text { + font-family: var(--bs-font-sans-serif); + font-size: 11px; + color: var(--bs-gray); + fill: var(--bs-gray); +} + +.scrollbox { + height:245px; + overflow-x:scroll; + overflow-y:scroll; +} + +table + .structure-heading { + border-top: 1px solid var(--bs-gray-200); + padding-top: 0.5em; +} + +table#code td:first-of-type { + padding-left: .75em; + padding-right: .75em; +} + +table#code td:first-of-type a { + text-decoration: none; +} + +.legend { + font-weight: bold; + margin-right: 2px; + padding-left: 10px; + padding-right: 10px; + text-align: center; +} + +.covered-by-small-tests { + background-color: var(--phpunit-success-high); +} + +.covered-by-medium-tests { + background-color: var(--phpunit-success-medium); +} + +.covered-by-large-tests { + background-color: var(--phpunit-success-low); +} + +.not-covered { + background-color: var(--phpunit-danger); +} + +.not-coverable { + background-color: var(--phpunit-warning); +} + +.progress-bar.bg-success { + background-color: var(--phpunit-success-bar) !important; +} + +.progress-bar.bg-warning { + background-color: var(--phpunit-warning-bar) !important; +} + +.progress-bar.bg-danger { + background-color: var(--phpunit-danger-bar) !important; +} diff --git a/packages/image/build/report/_icons/file-code.svg b/packages/image/build/report/_icons/file-code.svg new file mode 100644 index 000000000..5b4b19953 --- /dev/null +++ b/packages/image/build/report/_icons/file-code.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/image/build/report/_icons/file-directory.svg b/packages/image/build/report/_icons/file-directory.svg new file mode 100644 index 000000000..4bf1f1caa --- /dev/null +++ b/packages/image/build/report/_icons/file-directory.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/image/build/report/_js/billboard.pkgd.min.js b/packages/image/build/report/_js/billboard.pkgd.min.js new file mode 100644 index 000000000..52128a864 --- /dev/null +++ b/packages/image/build/report/_js/billboard.pkgd.min.js @@ -0,0 +1,57 @@ +/*! + * Copyright (c) 2017 ~ present NAVER Corp. + * billboard.js project is licensed under the MIT license + * + * billboard.js, JavaScript chart library + * https://naver.github.io/billboard.js/ + * + * @version 3.15.1 + * + * All-in-one packaged file for ease use of 'billboard.js' with dependant d3.js modules & polyfills. + * - @types/d3-selection ^3.0.11 + * - @types/d3-transition ^3.0.9 + * - d3-axis ^3.0.0 + * - d3-brush ^3.0.0 + * - d3-drag ^3.0.0 + * - d3-dsv ^3.0.1 + * - d3-ease ^3.0.1 + * - d3-hierarchy ^3.1.2 + * - d3-interpolate ^3.0.1 + * - d3-scale ^4.0.2 + * - d3-selection ^3.0.0 + * - d3-shape ^3.2.0 + * - d3-time-format ^4.1.0 + * - d3-transition ^3.0.1 + * - d3-zoom ^3.0.0 + */(function(Xa,zn){if(typeof exports=="object"&&typeof module=="object")module.exports=zn();else if(typeof define=="function"&&define.amd)define([],zn);else{var Ha=zn();for(var x in Ha)(typeof exports=="object"?exports:Xa)[x]=Ha[x]}})(this,function(){return function(){"use strict";var Cs=[function(x,b,r){r(1),r(97),r(98),r(99),r(100),r(101),r(102),r(103),r(104),r(105),r(106),r(107),r(108),r(109),r(110),r(111),r(124),r(126),r(136),r(137),r(139),r(143),r(146),r(148),r(150),r(151),r(152),r(153),r(155),r(156),r(158),r(159),r(161),r(165),r(166),r(167),r(168),r(173),r(174),r(176),r(177),r(178),r(180),r(184),r(185),r(186),r(187),r(188),r(193),r(195),r(196),r(198),r(201),r(202),r(203),r(204),r(205),r(207),r(218),r(220),r(221),r(223),r(224),r(227),r(230),r(236),r(237),r(238),r(239),r(240),r(241),r(245),r(246),r(248),r(249),r(250),r(252),r(253),r(254),r(255),r(256),r(261),r(262),r(263),r(264),r(266),r(267),r(268),r(270),r(271),r(272),r(273),r(93),r(274),r(275),r(283),r(285),r(287),r(288),r(289),r(290),r(291),r(293),r(294),r(295),r(296),r(297),r(298),r(300),r(301),r(302),r(303),r(304),r(305),r(306),r(307),r(311),r(312),r(314),r(316),r(317),r(318),r(319),r(320),r(322),r(324),r(325),r(326),r(327),r(329),r(330),r(332),r(333),r(334),r(335),r(337),r(338),r(339),r(340),r(341),r(342),r(343),r(344),r(345),r(347),r(348),r(349),r(350),r(351),r(352),r(353),r(354),r(355),r(356),r(357),r(359),r(360),r(361),r(362),r(386),r(387),r(388),r(389),r(390),r(391),r(392),r(393),r(394),r(395),r(397),r(398),r(399),r(400),r(401),r(402),r(403),r(404),r(405),r(406),r(413),r(415),r(416),r(418),r(419),r(420),r(421),r(422),r(424),r(434),r(436),r(438),r(440),r(442),r(444),r(446),r(447),r(449),r(452),r(453),r(454),r(455),r(456),r(460),r(461),r(463),r(464),r(465),r(466),r(468),r(469),r(470),r(471),r(472),r(473),r(474),r(476),r(479),r(482),r(485),r(486),r(487),r(488),r(489),r(490),r(491),r(492),r(493),r(494),r(495),r(496),r(497),r(505),r(506),r(507),r(508),r(509),r(510),r(511),r(512),r(513),r(514),r(515),r(516),r(517),r(519),r(520),r(521),r(522),r(523),r(524),r(525),r(526),r(527),r(528),r(529),r(530),r(531),r(532),r(533),r(534),r(535),r(536),r(537),r(538),r(539),r(540),r(541),r(542),r(543),r(544),r(545),r(546),r(549),r(551),r(553),r(554),r(557),r(558),r(560),r(561),r(562),r(566),r(567),r(568),r(569),r(572),r(577),r(578),r(579),r(580),r(581),r(582),r(583),r(80)},function(x,b,r){r(2),r(90),r(92),r(93),r(96)},function(x,b,r){var u=r(3),d=r(4),h=r(8),p=r(14),y=r(36),T=r(6),$=r(26),A=r(7),E=r(38),R=r(24),I=r(46),O=r(12),C=r(18),D=r(68),M=r(11),F=r(71),z=r(73),U=r(57),j=r(75),G=r(66),B=r(5),V=r(44),Y=r(72),Z=r(10),J=r(47),q=r(77),nt=r(34),rt=r(53),_=r(54),tt=r(40),et=r(33),lt=r(78),mt=r(79),gt=r(81),xt=r(82),yt=r(51),Ut=r(83).forEach,Dt=rt("hidden"),Xt="Symbol",Qt="prototype",kt=yt.set,me=yt.getterFor(Xt),ge=Object[Qt],ae=d.Symbol,Mt=ae&&ae[Qt],Ht=d.RangeError,re=d.TypeError,se=d.QObject,ee=B.f,fe=V.f,Pe=j.f,Me=Z.f,$e=p([].push),ce=nt("symbols"),Ae=nt("op-symbols"),Te=nt("wks"),de=!se||!se[Qt]||!se[Qt].findChild,bt=function(It,Pt,Ct){var Nt=ee(ge,Pt);Nt&&delete ge[Pt],fe(It,Pt,Ct),Nt&&It!==ge&&fe(ge,Pt,Nt)},Ft=T&&A(function(){return F(fe({},"a",{get:function(){return fe(this,"a",{value:7}).a}})).a!==7})?bt:fe,Tt=function(It,Pt){var Ct=ce[It]=F(Mt);return kt(Ct,{type:Xt,tag:It,description:Pt}),T||(Ct.description=Pt),Ct},qt=function(Pt,Ct,Nt){Pt===ge&&qt(Ae,Ct,Nt),I(Pt);var Et=C(Ct);return I(Nt),E(ce,Et)?(Nt.enumerable?(E(Pt,Dt)&&Pt[Dt][Et]&&(Pt[Dt][Et]=!1),Nt=F(Nt,{enumerable:M(0,!1)})):(E(Pt,Dt)||fe(Pt,Dt,M(1,F(null))),Pt[Dt][Et]=!0),Ft(Pt,Et,Nt)):fe(Pt,Et,Nt)},te=function(Pt,Ct){I(Pt);var Nt=O(Ct),Et=z(Nt).concat(ut(Nt));return Ut(Et,function(ie){(!T||h(Yt,Nt,ie))&&qt(Pt,ie,Nt[ie])}),Pt},Zt=function(Pt,Ct){return Ct===void 0?F(Pt):te(F(Pt),Ct)},Yt=function(Pt){var Ct=C(Pt),Nt=h(Me,this,Ct);return this===ge&&E(ce,Ct)&&!E(Ae,Ct)?!1:Nt||!E(this,Ct)||!E(ce,Ct)||E(this,Dt)&&this[Dt][Ct]?Nt:!0},Ye=function(Pt,Ct){var Nt=O(Pt),Et=C(Ct);if(!(Nt===ge&&E(ce,Et)&&!E(Ae,Et))){var ie=ee(Nt,Et);return ie&&E(ce,Et)&&!(E(Nt,Dt)&&Nt[Dt][Et])&&(ie.enumerable=!0),ie}},Ze=function(Pt){var Ct=Pe(O(Pt)),Nt=[];return Ut(Ct,function(Et){!E(ce,Et)&&!E(_,Et)&&$e(Nt,Et)}),Nt},ut=function(It){var Pt=It===ge,Ct=Pe(Pt?Ae:O(It)),Nt=[];return Ut(Ct,function(Et){E(ce,Et)&&(!Pt||E(ge,Et))&&$e(Nt,ce[Et])}),Nt};$||(ae=function(){if(R(Mt,this))throw new re("Symbol is not a constructor");var Pt=!arguments.length||arguments[0]===void 0?void 0:D(arguments[0]),Ct=tt(Pt),Nt=function(Et){var ie=this===void 0?d:this;ie===ge&&h(Nt,Ae,Et),E(ie,Dt)&&E(ie[Dt],Ct)&&(ie[Dt][Ct]=!1);var we=M(1,Et);try{Ft(ie,Ct,we)}catch(Rt){if(!(Rt instanceof Ht))throw Rt;bt(ie,Ct,we)}};return T&&de&&Ft(ge,Ct,{configurable:!0,set:Nt}),Tt(Ct,Pt)},Mt=ae[Qt],J(Mt,"toString",function(){return me(this).tag}),J(ae,"withoutSetter",function(It){return Tt(tt(It),It)}),Z.f=Yt,V.f=qt,Y.f=te,B.f=Ye,U.f=j.f=Ze,G.f=ut,lt.f=function(It){return Tt(et(It),It)},T&&(q(Mt,"description",{configurable:!0,get:function(){return me(this).description}}),y||J(ge,"propertyIsEnumerable",Yt,{unsafe:!0}))),u({global:!0,constructor:!0,wrap:!0,forced:!$,sham:!$},{Symbol:ae}),Ut(z(Te),function(It){mt(It)}),u({target:Xt,stat:!0,forced:!$},{useSetter:function(){de=!0},useSimple:function(){de=!1}}),u({target:"Object",stat:!0,forced:!$,sham:!T},{create:Zt,defineProperty:qt,defineProperties:te,getOwnPropertyDescriptor:Ye}),u({target:"Object",stat:!0,forced:!$},{getOwnPropertyNames:Ze}),gt(),xt(ae,Xt),_[Dt]=!0},function(x,b,r){var u=r(4),d=r(5).f,h=r(43),p=r(47),y=r(37),T=r(55),$=r(67);x.exports=function(A,E){var R=A.target,I=A.global,O=A.stat,C,D,M,F,z,U;if(I?D=u:O?D=u[R]||y(R,{}):D=u[R]&&u[R].prototype,D)for(M in E){if(z=E[M],A.dontCallGetSet?(U=d(D,M),F=U&&U.value):F=D[M],C=$(I?M:R+(O?".":"#")+M,A.forced),!C&&F!==void 0){if(typeof z==typeof F)continue;T(z,F)}(A.sham||F&&F.sham)&&h(z,"sham",!0),p(D,M,z,A)}}},function(x){var b=function(r){return r&&r.Math===Math&&r};x.exports=b(typeof globalThis=="object"&&globalThis)||b(typeof window=="object"&&window)||b(typeof self=="object"&&self)||b(typeof global=="object"&&global)||b(typeof this=="object"&&this)||function(){return this}()||Function("return this")()},function(x,b,r){var u=r(6),d=r(8),h=r(10),p=r(11),y=r(12),T=r(18),$=r(38),A=r(41),E=Object.getOwnPropertyDescriptor;b.f=u?E:function(I,O){if(I=y(I),O=T(O),A)try{return E(I,O)}catch(C){}if($(I,O))return p(!d(h.f,I,O),I[O])}},function(x,b,r){var u=r(7);x.exports=!u(function(){return Object.defineProperty({},1,{get:function(){return 7}})[1]!==7})},function(x){x.exports=function(b){try{return!!b()}catch(r){return!0}}},function(x,b,r){var u=r(9),d=Function.prototype.call;x.exports=u?d.bind(d):function(){return d.apply(d,arguments)}},function(x,b,r){var u=r(7);x.exports=!u(function(){var d=function(){}.bind();return typeof d!="function"||d.hasOwnProperty("prototype")})},function(x,b){var r={}.propertyIsEnumerable,u=Object.getOwnPropertyDescriptor,d=u&&!r.call({1:2},1);b.f=d?function(p){var y=u(this,p);return!!y&&y.enumerable}:r},function(x){x.exports=function(b,r){return{enumerable:!(b&1),configurable:!(b&2),writable:!(b&4),value:r}}},function(x,b,r){var u=r(13),d=r(16);x.exports=function(h){return u(d(h))}},function(x,b,r){var u=r(14),d=r(7),h=r(15),p=Object,y=u("".split);x.exports=d(function(){return!p("z").propertyIsEnumerable(0)})?function(T){return h(T)==="String"?y(T,""):p(T)}:p},function(x,b,r){var u=r(9),d=Function.prototype,h=d.call,p=u&&d.bind.bind(h,h);x.exports=u?p:function(y){return function(){return h.apply(y,arguments)}}},function(x,b,r){var u=r(14),d=u({}.toString),h=u("".slice);x.exports=function(p){return h(d(p),8,-1)}},function(x,b,r){var u=r(17),d=TypeError;x.exports=function(h){if(u(h))throw new d("Can't call method on "+h);return h}},function(x){x.exports=function(b){return b==null}},function(x,b,r){var u=r(19),d=r(22);x.exports=function(h){var p=u(h,"string");return d(p)?p:p+""}},function(x,b,r){var u=r(8),d=r(20),h=r(22),p=r(29),y=r(32),T=r(33),$=TypeError,A=T("toPrimitive");x.exports=function(E,R){if(!d(E)||h(E))return E;var I=p(E,A),O;if(I){if(R===void 0&&(R="default"),O=u(I,E,R),!d(O)||h(O))return O;throw new $("Can't convert object to primitive value")}return R===void 0&&(R="number"),y(E,R)}},function(x,b,r){var u=r(21);x.exports=function(d){return typeof d=="object"?d!==null:u(d)}},function(x){var b=typeof document=="object"&&document.all;x.exports=typeof b=="undefined"&&b!==void 0?function(r){return typeof r=="function"||r===b}:function(r){return typeof r=="function"}},function(x,b,r){var u=r(23),d=r(21),h=r(24),p=r(25),y=Object;x.exports=p?function(T){return typeof T=="symbol"}:function(T){var $=u("Symbol");return d($)&&h($.prototype,y(T))}},function(x,b,r){var u=r(4),d=r(21),h=function(p){return d(p)?p:void 0};x.exports=function(p,y){return arguments.length<2?h(u[p]):u[p]&&u[p][y]}},function(x,b,r){var u=r(14);x.exports=u({}.isPrototypeOf)},function(x,b,r){var u=r(26);x.exports=u&&!Symbol.sham&&typeof Symbol.iterator=="symbol"},function(x,b,r){var u=r(27),d=r(7),h=r(4),p=h.String;x.exports=!!Object.getOwnPropertySymbols&&!d(function(){var y=Symbol("symbol detection");return!p(y)||!(Object(y)instanceof Symbol)||!Symbol.sham&&u&&u<41})},function(x,b,r){var u=r(4),d=r(28),h=u.process,p=u.Deno,y=h&&h.versions||p&&p.version,T=y&&y.v8,$,A;T&&($=T.split("."),A=$[0]>0&&$[0]<4?1:+($[0]+$[1])),!A&&d&&($=d.match(/Edge\/(\d+)/),(!$||$[1]>=74)&&($=d.match(/Chrome\/(\d+)/),$&&(A=+$[1]))),x.exports=A},function(x,b,r){var u=r(4),d=u.navigator,h=d&&d.userAgent;x.exports=h?String(h):""},function(x,b,r){var u=r(30),d=r(17);x.exports=function(h,p){var y=h[p];return d(y)?void 0:u(y)}},function(x,b,r){var u=r(21),d=r(31),h=TypeError;x.exports=function(p){if(u(p))return p;throw new h(d(p)+" is not a function")}},function(x){var b=String;x.exports=function(r){try{return b(r)}catch(u){return"Object"}}},function(x,b,r){var u=r(8),d=r(21),h=r(20),p=TypeError;x.exports=function(y,T){var $,A;if(T==="string"&&d($=y.toString)&&!h(A=u($,y))||d($=y.valueOf)&&!h(A=u($,y))||T!=="string"&&d($=y.toString)&&!h(A=u($,y)))return A;throw new p("Can't convert object to primitive value")}},function(x,b,r){var u=r(4),d=r(34),h=r(38),p=r(40),y=r(26),T=r(25),$=u.Symbol,A=d("wks"),E=T?$.for||$:$&&$.withoutSetter||p;x.exports=function(R){return h(A,R)||(A[R]=y&&h($,R)?$[R]:E("Symbol."+R)),A[R]}},function(x,b,r){var u=r(35);x.exports=function(d,h){return u[d]||(u[d]=h||{})}},function(x,b,r){var u=r(36),d=r(4),h=r(37),p="__core-js_shared__",y=x.exports=d[p]||h(p,{});(y.versions||(y.versions=[])).push({version:"3.41.0",mode:u?"pure":"global",copyright:"\xA9 2014-2025 Denis Pushkarev (zloirock.ru)",license:"https://github.com/zloirock/core-js/blob/v3.41.0/LICENSE",source:"https://github.com/zloirock/core-js"})},function(x){x.exports=!1},function(x,b,r){var u=r(4),d=Object.defineProperty;x.exports=function(h,p){try{d(u,h,{value:p,configurable:!0,writable:!0})}catch(y){u[h]=p}return p}},function(x,b,r){var u=r(14),d=r(39),h=u({}.hasOwnProperty);x.exports=Object.hasOwn||function(y,T){return h(d(y),T)}},function(x,b,r){var u=r(16),d=Object;x.exports=function(h){return d(u(h))}},function(x,b,r){var u=r(14),d=0,h=Math.random(),p=u(1 .toString);x.exports=function(y){return"Symbol("+(y===void 0?"":y)+")_"+p(++d+h,36)}},function(x,b,r){var u=r(6),d=r(7),h=r(42);x.exports=!u&&!d(function(){return Object.defineProperty(h("div"),"a",{get:function(){return 7}}).a!==7})},function(x,b,r){var u=r(4),d=r(20),h=u.document,p=d(h)&&d(h.createElement);x.exports=function(y){return p?h.createElement(y):{}}},function(x,b,r){var u=r(6),d=r(44),h=r(11);x.exports=u?function(p,y,T){return d.f(p,y,h(1,T))}:function(p,y,T){return p[y]=T,p}},function(x,b,r){var u=r(6),d=r(41),h=r(45),p=r(46),y=r(18),T=TypeError,$=Object.defineProperty,A=Object.getOwnPropertyDescriptor,E="enumerable",R="configurable",I="writable";b.f=u?h?function(C,D,M){if(p(C),D=y(D),p(M),typeof C=="function"&&D==="prototype"&&"value"in M&&I in M&&!M[I]){var F=A(C,D);F&&F[I]&&(C[D]=M.value,M={configurable:R in M?M[R]:F[R],enumerable:E in M?M[E]:F[E],writable:!1})}return $(C,D,M)}:$:function(C,D,M){if(p(C),D=y(D),p(M),d)try{return $(C,D,M)}catch(F){}if("get"in M||"set"in M)throw new T("Accessors not supported");return"value"in M&&(C[D]=M.value),C}},function(x,b,r){var u=r(6),d=r(7);x.exports=u&&d(function(){return Object.defineProperty(function(){},"prototype",{value:42,writable:!1}).prototype!==42})},function(x,b,r){var u=r(20),d=String,h=TypeError;x.exports=function(p){if(u(p))return p;throw new h(d(p)+" is not an object")}},function(x,b,r){var u=r(21),d=r(44),h=r(48),p=r(37);x.exports=function(y,T,$,A){A||(A={});var E=A.enumerable,R=A.name!==void 0?A.name:T;if(u($)&&h($,R,A),A.global)E?y[T]=$:p(T,$);else{try{A.unsafe?y[T]&&(E=!0):delete y[T]}catch(I){}E?y[T]=$:d.f(y,T,{value:$,enumerable:!1,configurable:!A.nonConfigurable,writable:!A.nonWritable})}return y}},function(x,b,r){var u=r(14),d=r(7),h=r(21),p=r(38),y=r(6),T=r(49).CONFIGURABLE,$=r(50),A=r(51),E=A.enforce,R=A.get,I=String,O=Object.defineProperty,C=u("".slice),D=u("".replace),M=u([].join),F=y&&!d(function(){return O(function(){},"length",{value:8}).length!==8}),z=String(String).split("String"),U=x.exports=function(j,G,B){C(I(G),0,7)==="Symbol("&&(G="["+D(I(G),/^Symbol\(([^)]*)\).*$/,"$1")+"]"),B&&B.getter&&(G="get "+G),B&&B.setter&&(G="set "+G),(!p(j,"name")||T&&j.name!==G)&&(y?O(j,"name",{value:G,configurable:!0}):j.name=G),F&&B&&p(B,"arity")&&j.length!==B.arity&&O(j,"length",{value:B.arity});try{B&&p(B,"constructor")&&B.constructor?y&&O(j,"prototype",{writable:!1}):j.prototype&&(j.prototype=void 0)}catch(Y){}var V=E(j);return p(V,"source")||(V.source=M(z,typeof G=="string"?G:"")),j};Function.prototype.toString=U(function(){return h(this)&&R(this).source||$(this)},"toString")},function(x,b,r){var u=r(6),d=r(38),h=Function.prototype,p=u&&Object.getOwnPropertyDescriptor,y=d(h,"name"),T=y&&function(){}.name==="something",$=y&&(!u||u&&p(h,"name").configurable);x.exports={EXISTS:y,PROPER:T,CONFIGURABLE:$}},function(x,b,r){var u=r(14),d=r(21),h=r(35),p=u(Function.toString);d(h.inspectSource)||(h.inspectSource=function(y){return p(y)}),x.exports=h.inspectSource},function(x,b,r){var u=r(52),d=r(4),h=r(20),p=r(43),y=r(38),T=r(35),$=r(53),A=r(54),E="Object already initialized",R=d.TypeError,I=d.WeakMap,O,C,D,M=function(j){return D(j)?C(j):O(j,{})},F=function(j){return function(G){var B;if(!h(G)||(B=C(G)).type!==j)throw new R("Incompatible receiver, "+j+" required");return B}};if(u||T.state){var z=T.state||(T.state=new I);z.get=z.get,z.has=z.has,z.set=z.set,O=function(j,G){if(z.has(j))throw new R(E);return G.facade=j,z.set(j,G),G},C=function(j){return z.get(j)||{}},D=function(j){return z.has(j)}}else{var U=$("state");A[U]=!0,O=function(j,G){if(y(j,U))throw new R(E);return G.facade=j,p(j,U,G),G},C=function(j){return y(j,U)?j[U]:{}},D=function(j){return y(j,U)}}x.exports={set:O,get:C,has:D,enforce:M,getterFor:F}},function(x,b,r){var u=r(4),d=r(21),h=u.WeakMap;x.exports=d(h)&&/native code/.test(String(h))},function(x,b,r){var u=r(34),d=r(40),h=u("keys");x.exports=function(p){return h[p]||(h[p]=d(p))}},function(x){x.exports={}},function(x,b,r){var u=r(38),d=r(56),h=r(5),p=r(44);x.exports=function(y,T,$){for(var A=d(T),E=p.f,R=h.f,I=0;IR;)d(E,O=A[R++])&&(~p(I,O)||T(I,O));return I}},function(x,b,r){var u=r(12),d=r(60),h=r(63),p=function(y){return function(T,$,A){var E=u(T),R=h(E);if(R===0)return!y&&-1;var I=d(A,R),O;if(y&&$!==$){for(;R>I;)if(O=E[I++],O!==O)return!0}else for(;R>I;I++)if((y||I in E)&&E[I]===$)return y||I||0;return!y&&-1}};x.exports={includes:p(!0),indexOf:p(!1)}},function(x,b,r){var u=r(61),d=Math.max,h=Math.min;x.exports=function(p,y){var T=u(p);return T<0?d(T+y,0):h(T,y)}},function(x,b,r){var u=r(62);x.exports=function(d){var h=+d;return h!==h||h===0?0:u(h)}},function(x){var b=Math.ceil,r=Math.floor;x.exports=Math.trunc||function(d){var h=+d;return(h>0?r:b)(h)}},function(x,b,r){var u=r(64);x.exports=function(d){return u(d.length)}},function(x,b,r){var u=r(61),d=Math.min;x.exports=function(h){var p=u(h);return p>0?d(p,9007199254740991):0}},function(x){x.exports=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"]},function(x,b){b.f=Object.getOwnPropertySymbols},function(x,b,r){var u=r(7),d=r(21),h=/#|\.prototype\./,p=function(E,R){var I=T[y(E)];return I===A?!0:I===$?!1:d(R)?u(R):!!R},y=p.normalize=function(E){return String(E).replace(h,".").toLowerCase()},T=p.data={},$=p.NATIVE="N",A=p.POLYFILL="P";x.exports=p},function(x,b,r){var u=r(69),d=String;x.exports=function(h){if(u(h)==="Symbol")throw new TypeError("Cannot convert a Symbol value to a string");return d(h)}},function(x,b,r){var u=r(70),d=r(21),h=r(15),p=r(33),y=p("toStringTag"),T=Object,$=h(function(){return arguments}())==="Arguments",A=function(E,R){try{return E[R]}catch(I){}};x.exports=u?h:function(E){var R,I,O;return E===void 0?"Undefined":E===null?"Null":typeof(I=A(R=T(E),y))=="string"?I:$?h(R):(O=h(R))==="Object"&&d(R.callee)?"Arguments":O}},function(x,b,r){var u=r(33),d=u("toStringTag"),h={};h[d]="z",x.exports=String(h)==="[object z]"},function(x,b,r){var u=r(46),d=r(72),h=r(65),p=r(54),y=r(74),T=r(42),$=r(53),A=">",E="<",R="prototype",I="script",O=$("IE_PROTO"),C=function(){},D=function(j){return E+I+A+j+E+"/"+I+A},M=function(j){j.write(D("")),j.close();var G=j.parentWindow.Object;return j=null,G},F=function(){var j=T("iframe"),G="java"+I+":",B;return j.style.display="none",y.appendChild(j),j.src=String(G),B=j.contentWindow.document,B.open(),B.write(D("document.F=Object")),B.close(),B.F},z,U=function(){try{z=new ActiveXObject("htmlfile")}catch(G){}U=typeof document!="undefined"?document.domain&&z?M(z):F():M(z);for(var j=h.length;j--;)delete U[R][h[j]];return U()};p[O]=!0,x.exports=Object.create||function(G,B){var V;return G!==null?(C[R]=u(G),V=new C,C[R]=null,V[O]=G):V=U(),B===void 0?V:d.f(V,B)}},function(x,b,r){var u=r(6),d=r(45),h=r(44),p=r(46),y=r(12),T=r(73);b.f=u&&!d?Object.defineProperties:function(A,E){p(A);for(var R=y(E),I=T(E),O=I.length,C=0,D;O>C;)h.f(A,D=I[C++],R[D]);return A}},function(x,b,r){var u=r(58),d=r(65);x.exports=Object.keys||function(p){return u(p,d)}},function(x,b,r){var u=r(23);x.exports=u("document","documentElement")},function(x,b,r){var u=r(15),d=r(12),h=r(57).f,p=r(76),y=typeof window=="object"&&window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[],T=function($){try{return h($)}catch(A){return p(y)}};x.exports.f=function(A){return y&&u(A)==="Window"?T(A):h(d(A))}},function(x,b,r){var u=r(14);x.exports=u([].slice)},function(x,b,r){var u=r(48),d=r(44);x.exports=function(h,p,y){return y.get&&u(y.get,p,{getter:!0}),y.set&&u(y.set,p,{setter:!0}),d.f(h,p,y)}},function(x,b,r){var u=r(33);b.f=u},function(x,b,r){var u=r(80),d=r(38),h=r(78),p=r(44).f;x.exports=function(y){var T=u.Symbol||(u.Symbol={});d(T,y)||p(T,y,{value:h.f(y)})}},function(x,b,r){var u=r(4);x.exports=u},function(x,b,r){var u=r(8),d=r(23),h=r(33),p=r(47);x.exports=function(){var y=d("Symbol"),T=y&&y.prototype,$=T&&T.valueOf,A=h("toPrimitive");T&&!T[A]&&p(T,A,function(E){return u($,this)},{arity:1})}},function(x,b,r){var u=r(44).f,d=r(38),h=r(33),p=h("toStringTag");x.exports=function(y,T,$){y&&!$&&(y=y.prototype),y&&!d(y,p)&&u(y,p,{configurable:!0,value:T})}},function(x,b,r){var u=r(84),d=r(14),h=r(13),p=r(39),y=r(63),T=r(86),$=d([].push),A=function(E){var R=E===1,I=E===2,O=E===3,C=E===4,D=E===6,M=E===7,F=E===5||D;return function(z,U,j,G){for(var B=p(z),V=h(B),Y=y(V),Z=u(U,j),J=0,q=G||T,nt=R?q(z,Y):I||M?q(z,0):void 0,rt,_;Y>J;J++)if((F||J in V)&&(rt=V[J],_=Z(rt,J,B),E))if(R)nt[J]=_;else if(_)switch(E){case 3:return!0;case 5:return rt;case 6:return J;case 2:$(nt,rt)}else switch(E){case 4:return!1;case 7:$(nt,rt)}return D?-1:O||C?C:nt}};x.exports={forEach:A(0),map:A(1),filter:A(2),some:A(3),every:A(4),find:A(5),findIndex:A(6),filterReject:A(7)}},function(x,b,r){var u=r(85),d=r(30),h=r(9),p=u(u.bind);x.exports=function(y,T){return d(y),T===void 0?y:h?p(y,T):function(){return y.apply(T,arguments)}}},function(x,b,r){var u=r(15),d=r(14);x.exports=function(h){if(u(h)==="Function")return d(h)}},function(x,b,r){var u=r(87);x.exports=function(d,h){return new(u(d))(h===0?0:h)}},function(x,b,r){var u=r(88),d=r(89),h=r(20),p=r(33),y=p("species"),T=Array;x.exports=function($){var A;return u($)&&(A=$.constructor,d(A)&&(A===T||u(A.prototype))?A=void 0:h(A)&&(A=A[y],A===null&&(A=void 0))),A===void 0?T:A}},function(x,b,r){var u=r(15);x.exports=Array.isArray||function(h){return u(h)==="Array"}},function(x,b,r){var u=r(14),d=r(7),h=r(21),p=r(69),y=r(23),T=r(50),$=function(){},A=y("Reflect","construct"),E=/^\s*(?:class|function)\b/,R=u(E.exec),I=!E.test($),O=function(M){if(!h(M))return!1;try{return A($,[],M),!0}catch(F){return!1}},C=function(M){if(!h(M))return!1;switch(p(M)){case"AsyncFunction":case"GeneratorFunction":case"AsyncGeneratorFunction":return!1}try{return I||!!R(E,T(M))}catch(F){return!0}};C.sham=!0,x.exports=!A||d(function(){var D;return O(O.call)||!O(Object)||!O(function(){D=!0})||D})?C:O},function(x,b,r){var u=r(3),d=r(23),h=r(38),p=r(68),y=r(34),T=r(91),$=y("string-to-symbol-registry"),A=y("symbol-to-string-registry");u({target:"Symbol",stat:!0,forced:!T},{for:function(E){var R=p(E);if(h($,R))return $[R];var I=d("Symbol")(R);return $[R]=I,A[I]=R,I}})},function(x,b,r){var u=r(26);x.exports=u&&!!Symbol.for&&!!Symbol.keyFor},function(x,b,r){var u=r(3),d=r(38),h=r(22),p=r(31),y=r(34),T=r(91),$=y("symbol-to-string-registry");u({target:"Symbol",stat:!0,forced:!T},{keyFor:function(E){if(!h(E))throw new TypeError(p(E)+" is not a symbol");if(d($,E))return $[E]}})},function(x,b,r){var u=r(3),d=r(23),h=r(94),p=r(8),y=r(14),T=r(7),$=r(21),A=r(22),E=r(76),R=r(95),I=r(26),O=String,C=d("JSON","stringify"),D=y(/./.exec),M=y("".charAt),F=y("".charCodeAt),z=y("".replace),U=y(1 .toString),j=/[\uD800-\uDFFF]/g,G=/^[\uD800-\uDBFF]$/,B=/^[\uDC00-\uDFFF]$/,V=!I||T(function(){var q=d("Symbol")("stringify detection");return C([q])!=="[null]"||C({a:q})!=="{}"||C(Object(q))!=="{}"}),Y=T(function(){return C("\uDF06\uD834")!=='"\\udf06\\ud834"'||C("\uDEAD")!=='"\\udead"'}),Z=function(q,nt){var rt=E(arguments),_=R(nt);if(!(!$(_)&&(q===void 0||A(q))))return rt[1]=function(tt,et){if($(_)&&(et=p(_,this,O(tt),et)),!A(et))return et},h(C,null,rt)},J=function(q,nt,rt){var _=M(rt,nt-1),tt=M(rt,nt+1);return D(G,q)&&!D(B,tt)||D(B,q)&&!D(G,_)?"\\u"+U(F(q,0),16):q};C&&u({target:"JSON",stat:!0,arity:3,forced:V||Y},{stringify:function(nt,rt,_){var tt=E(arguments),et=h(V?Z:C,null,tt);return Y&&typeof et=="string"?z(et,j,J):et}})},function(x,b,r){var u=r(9),d=Function.prototype,h=d.apply,p=d.call;x.exports=typeof Reflect=="object"&&Reflect.apply||(u?p.bind(h):function(){return p.apply(h,arguments)})},function(x,b,r){var u=r(14),d=r(88),h=r(21),p=r(15),y=r(68),T=u([].push);x.exports=function($){if(h($))return $;if(d($)){for(var A=$.length,E=[],R=0;Rj&&R(_,arguments[j]),_});if(J.prototype=Y,B!=="Error"?y?y(J,Z):T(J,Z,{name:!0}):O&&U in V&&($(J,V,U),$(J,V,"prepareStackTrace")),T(J,V),!C)try{Y.name!==B&&h(Y,"name",B),Y.constructor=J}catch(q){}return J}}},function(x,b,r){var u=r(114),d=r(20),h=r(16),p=r(115);x.exports=Object.setPrototypeOf||("__proto__"in{}?function(){var y=!1,T={},$;try{$=u(Object.prototype,"__proto__","set"),$(T,[]),y=T instanceof Array}catch(A){}return function(E,R){return h(E),p(R),d(E)&&(y?$(E,R):E.__proto__=R),E}}():void 0)},function(x,b,r){var u=r(14),d=r(30);x.exports=function(h,p,y){try{return u(d(Object.getOwnPropertyDescriptor(h,p)[y]))}catch(T){}}},function(x,b,r){var u=r(116),d=String,h=TypeError;x.exports=function(p){if(u(p))return p;throw new h("Can't set "+d(p)+" as a prototype")}},function(x,b,r){var u=r(20);x.exports=function(d){return u(d)||d===null}},function(x,b,r){var u=r(44).f;x.exports=function(d,h,p){p in d||u(d,p,{configurable:!0,get:function(){return h[p]},set:function(y){h[p]=y}})}},function(x,b,r){var u=r(21),d=r(20),h=r(113);x.exports=function(p,y,T){var $,A;return h&&u($=y.constructor)&&$!==T&&d(A=$.prototype)&&A!==T.prototype&&h(p,A),p}},function(x,b,r){var u=r(68);x.exports=function(d,h){return d===void 0?arguments.length<2?"":h:u(d)}},function(x,b,r){var u=r(20),d=r(43);x.exports=function(h,p){u(p)&&"cause"in p&&d(h,"cause",p.cause)}},function(x,b,r){var u=r(43),d=r(122),h=r(123),p=Error.captureStackTrace;x.exports=function(y,T,$,A){h&&(p?p(y,T):u(y,"stack",d($,A)))}},function(x,b,r){var u=r(14),d=Error,h=u("".replace),p=function($){return String(new d($).stack)}("zxcasd"),y=/\n\s*at [^:]*:[^\n]*/,T=y.test(p);x.exports=function($,A){if(T&&typeof $=="string"&&!d.prepareStackTrace)for(;A--;)$=h($,y,"");return $}},function(x,b,r){var u=r(7),d=r(11);x.exports=!u(function(){var h=new Error("a");return"stack"in h?(Object.defineProperty(h,"stack",d(1,7)),h.stack!==7):!0})},function(x,b,r){var u=r(47),d=r(125),h=Error.prototype;h.toString!==d&&u(h,"toString",d)},function(x,b,r){var u=r(6),d=r(7),h=r(46),p=r(119),y=Error.prototype.toString,T=d(function(){if(u){var $=Object.create(Object.defineProperty({},"name",{get:function(){return this===$}}));if(y.call($)!=="true")return!0}return y.call({message:1,name:2})!=="2: 1"||y.call({})!=="Error"});x.exports=T?function(){var A=h(this),E=p(A.name,"Error"),R=p(A.message);return E?R?E+": "+R:E:R}:y},function(x,b,r){r(127)},function(x,b,r){var u=r(3),d=r(24),h=r(128),p=r(113),y=r(55),T=r(71),$=r(43),A=r(11),E=r(120),R=r(121),I=r(130),O=r(119),C=r(33),D=C("toStringTag"),M=Error,F=[].push,z=function(G,B){var V=d(U,this),Y;p?Y=p(new M,V?h(this):U):(Y=V?this:T(U),$(Y,D,"Error")),B!==void 0&&$(Y,"message",O(B)),R(Y,z,Y.stack,1),arguments.length>2&&E(Y,arguments[2]);var Z=[];return I(G,F,{that:Z}),$(Y,"errors",Z),Y};p?p(z,M):y(z,M,{name:!0});var U=z.prototype=T(M.prototype,{constructor:A(1,z),message:A(1,""),name:A(1,"AggregateError")});u({global:!0,constructor:!0,arity:2},{AggregateError:z})},function(x,b,r){var u=r(38),d=r(21),h=r(39),p=r(53),y=r(129),T=p("IE_PROTO"),$=Object,A=$.prototype;x.exports=y?$.getPrototypeOf:function(E){var R=h(E);if(u(R,T))return R[T];var I=R.constructor;return d(I)&&R instanceof I?I.prototype:R instanceof $?A:null}},function(x,b,r){var u=r(7);x.exports=!u(function(){function d(){}return d.prototype.constructor=null,Object.getPrototypeOf(new d)!==d.prototype})},function(x,b,r){var u=r(84),d=r(8),h=r(46),p=r(31),y=r(131),T=r(63),$=r(24),A=r(133),E=r(134),R=r(135),I=TypeError,O=function(D,M){this.stopped=D,this.result=M},C=O.prototype;x.exports=function(D,M,F){var z=F&&F.that,U=!!(F&&F.AS_ENTRIES),j=!!(F&&F.IS_RECORD),G=!!(F&&F.IS_ITERATOR),B=!!(F&&F.INTERRUPTED),V=u(M,z),Y,Z,J,q,nt,rt,_,tt=function(lt){return Y&&R(Y,"normal",lt),new O(!0,lt)},et=function(lt){return U?(h(lt),B?V(lt[0],lt[1],tt):V(lt[0],lt[1])):B?V(lt,tt):V(lt)};if(j)Y=D.iterator;else if(G)Y=D;else{if(Z=E(D),!Z)throw new I(p(D)+" is not iterable");if(y(Z)){for(J=0,q=T(D);q>J;J++)if(nt=et(D[J]),nt&&$(C,nt))return nt;return new O(!1)}Y=A(D,Z)}for(rt=j?D.next:Y.next;!(_=d(rt,Y)).done;){try{nt=et(_.value)}catch(lt){R(Y,"throw",lt)}if(typeof nt=="object"&&nt&&$(C,nt))return nt}return new O(!1)}},function(x,b,r){var u=r(33),d=r(132),h=u("iterator"),p=Array.prototype;x.exports=function(y){return y!==void 0&&(d.Array===y||p[h]===y)}},function(x){x.exports={}},function(x,b,r){var u=r(8),d=r(30),h=r(46),p=r(31),y=r(134),T=TypeError;x.exports=function($,A){var E=arguments.length<2?y($):A;if(d(E))return h(u(E,$));throw new T(p($)+" is not iterable")}},function(x,b,r){var u=r(69),d=r(29),h=r(17),p=r(132),y=r(33),T=y("iterator");x.exports=function($){if(!h($))return d($,T)||d($,"@@iterator")||p[u($)]}},function(x,b,r){var u=r(8),d=r(46),h=r(29);x.exports=function(p,y,T){var $,A;d(p);try{if($=h(p,"return"),!$){if(y==="throw")throw T;return T}$=u($,p)}catch(E){A=!0,$=E}if(y==="throw")throw T;if(A)throw $;return d($),T}},function(x,b,r){var u=r(3),d=r(23),h=r(94),p=r(7),y=r(112),T="AggregateError",$=d(T),A=!p(function(){return $([1]).errors[0]!==1})&&p(function(){return $([1],T,{cause:7}).cause!==7});u({global:!0,constructor:!0,arity:2,forced:A},{AggregateError:y(T,function(E){return function(I,O){return h(E,this,arguments)}},A,!0)})},function(x,b,r){var u=r(3),d=r(39),h=r(63),p=r(61),y=r(138);u({target:"Array",proto:!0},{at:function($){var A=d(this),E=h(A),R=p($),I=R>=0?R:E+R;return I<0||I>=E?void 0:A[I]}}),y("at")},function(x,b,r){var u=r(33),d=r(71),h=r(44).f,p=u("unscopables"),y=Array.prototype;y[p]===void 0&&h(y,p,{configurable:!0,value:d(null)}),x.exports=function(T){y[p][T]=!0}},function(x,b,r){var u=r(3),d=r(7),h=r(88),p=r(20),y=r(39),T=r(63),$=r(140),A=r(141),E=r(86),R=r(142),I=r(33),O=r(27),C=I("isConcatSpreadable"),D=O>=51||!d(function(){var z=[];return z[C]=!1,z.concat()[0]!==z}),M=function(z){if(!p(z))return!1;var U=z[C];return U!==void 0?!!U:h(z)},F=!D||!R("concat");u({target:"Array",proto:!0,arity:1,forced:F},{concat:function(U){var j=y(this),G=E(j,0),B=0,V,Y,Z,J,q;for(V=-1,Z=arguments.length;Vr)throw b("Maximum allowed index exceeded");return u}},function(x,b,r){var u=r(6),d=r(44),h=r(11);x.exports=function(p,y,T){u?d.f(p,y,h(0,T)):p[y]=T}},function(x,b,r){var u=r(7),d=r(33),h=r(27),p=d("species");x.exports=function(y){return h>=51||!u(function(){var T=[],$=T.constructor={};return $[p]=function(){return{foo:1}},T[y](Boolean).foo!==1})}},function(x,b,r){var u=r(3),d=r(144),h=r(138);u({target:"Array",proto:!0},{copyWithin:d}),h("copyWithin")},function(x,b,r){var u=r(39),d=r(60),h=r(63),p=r(145),y=Math.min;x.exports=[].copyWithin||function($,A){var E=u(this),R=h(E),I=d($,R),O=d(A,R),C=arguments.length>2?arguments[2]:void 0,D=y((C===void 0?R:d(C,R))-O,R-I),M=1;for(O0;)O in E?E[I]=E[O]:p(E,I),I+=M,O+=M;return E}},function(x,b,r){var u=r(31),d=TypeError;x.exports=function(h,p){if(!delete h[p])throw new d("Cannot delete property "+u(p)+" of "+u(h))}},function(x,b,r){var u=r(3),d=r(83).every,h=r(147),p=h("every");u({target:"Array",proto:!0,forced:!p},{every:function(T){return d(this,T,arguments.length>1?arguments[1]:void 0)}})},function(x,b,r){var u=r(7);x.exports=function(d,h){var p=[][d];return!!p&&u(function(){p.call(null,h||function(){return 1},1)})}},function(x,b,r){var u=r(3),d=r(149),h=r(138);u({target:"Array",proto:!0},{fill:d}),h("fill")},function(x,b,r){var u=r(39),d=r(60),h=r(63);x.exports=function(y){for(var T=u(this),$=h(T),A=arguments.length,E=d(A>1?arguments[1]:void 0,$),R=A>2?arguments[2]:void 0,I=R===void 0?$:d(R,$);I>E;)T[E++]=y;return T}},function(x,b,r){var u=r(3),d=r(83).filter,h=r(142),p=h("filter");u({target:"Array",proto:!0,forced:!p},{filter:function(T){return d(this,T,arguments.length>1?arguments[1]:void 0)}})},function(x,b,r){var u=r(3),d=r(83).find,h=r(138),p="find",y=!0;p in[]&&Array(1)[p](function(){y=!1}),u({target:"Array",proto:!0,forced:y},{find:function($){return d(this,$,arguments.length>1?arguments[1]:void 0)}}),h(p)},function(x,b,r){var u=r(3),d=r(83).findIndex,h=r(138),p="findIndex",y=!0;p in[]&&Array(1)[p](function(){y=!1}),u({target:"Array",proto:!0,forced:y},{findIndex:function($){return d(this,$,arguments.length>1?arguments[1]:void 0)}}),h(p)},function(x,b,r){var u=r(3),d=r(154).findLast,h=r(138);u({target:"Array",proto:!0},{findLast:function(y){return d(this,y,arguments.length>1?arguments[1]:void 0)}}),h("findLast")},function(x,b,r){var u=r(84),d=r(13),h=r(39),p=r(63),y=function(T){var $=T===1;return function(A,E,R){for(var I=h(A),O=d(I),C=p(O),D=u(E,R),M,F;C-- >0;)if(M=O[C],F=D(M,C,I),F)switch(T){case 0:return M;case 1:return C}return $?-1:void 0}};x.exports={findLast:y(0),findLastIndex:y(1)}},function(x,b,r){var u=r(3),d=r(154).findLastIndex,h=r(138);u({target:"Array",proto:!0},{findLastIndex:function(y){return d(this,y,arguments.length>1?arguments[1]:void 0)}}),h("findLastIndex")},function(x,b,r){var u=r(3),d=r(157),h=r(39),p=r(63),y=r(61),T=r(86);u({target:"Array",proto:!0},{flat:function(){var A=arguments.length?arguments[0]:void 0,E=h(this),R=p(E),I=T(E,0);return I.length=d(I,E,E,R,0,A===void 0?1:y(A)),I}})},function(x,b,r){var u=r(88),d=r(63),h=r(140),p=r(84),y=function(T,$,A,E,R,I,O,C){for(var D=R,M=0,F=O?p(O,C):!1,z,U;M0&&u(z)?(U=d(z),D=y(T,$,z,U,D,I-1)-1):(h(D+1),T[D]=z),D++),M++;return D};x.exports=y},function(x,b,r){var u=r(3),d=r(157),h=r(30),p=r(39),y=r(63),T=r(86);u({target:"Array",proto:!0},{flatMap:function(A){var E=p(this),R=y(E),I;return h(A),I=T(E,0),I.length=d(I,E,E,R,0,1,A,arguments.length>1?arguments[1]:void 0),I}})},function(x,b,r){var u=r(3),d=r(160);u({target:"Array",proto:!0,forced:[].forEach!==d},{forEach:d})},function(x,b,r){var u=r(83).forEach,d=r(147),h=d("forEach");x.exports=h?[].forEach:function(y){return u(this,y,arguments.length>1?arguments[1]:void 0)}},function(x,b,r){var u=r(3),d=r(162),h=r(164),p=!h(function(y){Array.from(y)});u({target:"Array",stat:!0,forced:p},{from:d})},function(x,b,r){var u=r(84),d=r(8),h=r(39),p=r(163),y=r(131),T=r(89),$=r(63),A=r(141),E=r(133),R=r(134),I=Array;x.exports=function(C){var D=h(C),M=T(this),F=arguments.length,z=F>1?arguments[1]:void 0,U=z!==void 0;U&&(z=u(z,F>2?arguments[2]:void 0));var j=R(D),G=0,B,V,Y,Z,J,q;if(j&&!(this===I&&y(j)))for(V=M?new this:[],Z=E(D,j),J=Z.next;!(Y=d(J,Z)).done;G++)q=U?p(Z,z,[Y.value,G],!0):Y.value,A(V,G,q);else for(B=$(D),V=M?new this(B):I(B);B>G;G++)q=U?z(D[G],G):D[G],A(V,G,q);return V.length=G,V}},function(x,b,r){var u=r(46),d=r(135);x.exports=function(h,p,y,T){try{return T?p(u(y)[0],y[1]):p(y)}catch($){d(h,"throw",$)}}},function(x,b,r){var u=r(33),d=u("iterator"),h=!1;try{var p=0,y={next:function(){return{done:!!p++}},return:function(){h=!0}};y[d]=function(){return this},Array.from(y,function(){throw 2})}catch(T){}x.exports=function(T,$){try{if(!$&&!h)return!1}catch(R){return!1}var A=!1;try{var E={};E[d]=function(){return{next:function(){return{done:A=!0}}}},T(E)}catch(R){}return A}},function(x,b,r){var u=r(3),d=r(59).includes,h=r(7),p=r(138),y=h(function(){return!Array(1).includes()});u({target:"Array",proto:!0,forced:y},{includes:function($){return d(this,$,arguments.length>1?arguments[1]:void 0)}}),p("includes")},function(x,b,r){var u=r(3),d=r(85),h=r(59).indexOf,p=r(147),y=d([].indexOf),T=!!y&&1/y([1],1,-0)<0,$=T||!p("indexOf");u({target:"Array",proto:!0,forced:$},{indexOf:function(E){var R=arguments.length>1?arguments[1]:void 0;return T?y(this,E,R)||0:h(this,E,R)}})},function(x,b,r){var u=r(3),d=r(88);u({target:"Array",stat:!0},{isArray:d})},function(x,b,r){var u=r(12),d=r(138),h=r(132),p=r(51),y=r(44).f,T=r(169),$=r(172),A=r(36),E=r(6),R="Array Iterator",I=p.set,O=p.getterFor(R);x.exports=T(Array,"Array",function(D,M){I(this,{type:R,target:u(D),index:0,kind:M})},function(){var D=O(this),M=D.target,F=D.index++;if(!M||F>=M.length)return D.target=null,$(void 0,!0);switch(D.kind){case"keys":return $(F,!1);case"values":return $(M[F],!1)}return $([F,M[F]],!1)},"values");var C=h.Arguments=h.Array;if(d("keys"),d("values"),d("entries"),!A&&E&&C.name!=="values")try{y(C,"name",{value:"values"})}catch(D){}},function(x,b,r){var u=r(3),d=r(8),h=r(36),p=r(49),y=r(21),T=r(170),$=r(128),A=r(113),E=r(82),R=r(43),I=r(47),O=r(33),C=r(132),D=r(171),M=p.PROPER,F=p.CONFIGURABLE,z=D.IteratorPrototype,U=D.BUGGY_SAFARI_ITERATORS,j=O("iterator"),G="keys",B="values",V="entries",Y=function(){return this};x.exports=function(Z,J,q,nt,rt,_,tt){T(q,J,nt);var et=function(kt){if(kt===rt&&yt)return yt;if(!U&&kt&&kt in gt)return gt[kt];switch(kt){case G:return function(){return new q(this,kt)};case B:return function(){return new q(this,kt)};case V:return function(){return new q(this,kt)}}return function(){return new q(this)}},lt=J+" Iterator",mt=!1,gt=Z.prototype,xt=gt[j]||gt["@@iterator"]||rt&>[rt],yt=!U&&xt||et(rt),Ut=J==="Array"&>.entries||xt,Dt,Xt,Qt;if(Ut&&(Dt=$(Ut.call(new Z)),Dt!==Object.prototype&&Dt.next&&(!h&&$(Dt)!==z&&(A?A(Dt,z):y(Dt[j])||I(Dt,j,Y)),E(Dt,lt,!0,!0),h&&(C[lt]=Y))),M&&rt===B&&xt&&xt.name!==B&&(!h&&F?R(gt,"name",B):(mt=!0,yt=function(){return d(xt,this)})),rt)if(Xt={values:et(B),keys:_?yt:et(G),entries:et(V)},tt)for(Qt in Xt)(U||mt||!(Qt in gt))&&I(gt,Qt,Xt[Qt]);else u({target:J,proto:!0,forced:U||mt},Xt);return(!h||tt)&>[j]!==yt&&I(gt,j,yt,{name:rt}),C[J]=yt,Xt}},function(x,b,r){var u=r(171).IteratorPrototype,d=r(71),h=r(11),p=r(82),y=r(132),T=function(){return this};x.exports=function($,A,E,R){var I=A+" Iterator";return $.prototype=d(u,{next:h(+!R,E)}),p($,I,!1,!0),y[I]=T,$}},function(x,b,r){var u=r(7),d=r(21),h=r(20),p=r(71),y=r(128),T=r(47),$=r(33),A=r(36),E=$("iterator"),R=!1,I,O,C;[].keys&&(C=[].keys(),"next"in C?(O=y(y(C)),O!==Object.prototype&&(I=O)):R=!0);var D=!h(I)||u(function(){var M={};return I[E].call(M)!==M});D?I={}:A&&(I=p(I)),d(I[E])||T(I,E,function(){return this}),x.exports={IteratorPrototype:I,BUGGY_SAFARI_ITERATORS:R}},function(x){x.exports=function(b,r){return{value:b,done:r}}},function(x,b,r){var u=r(3),d=r(14),h=r(13),p=r(12),y=r(147),T=d([].join),$=h!==Object,A=$||!y("join",",");u({target:"Array",proto:!0,forced:A},{join:function(R){return T(p(this),R===void 0?",":R)}})},function(x,b,r){var u=r(3),d=r(175);u({target:"Array",proto:!0,forced:d!==[].lastIndexOf},{lastIndexOf:d})},function(x,b,r){var u=r(94),d=r(12),h=r(61),p=r(63),y=r(147),T=Math.min,$=[].lastIndexOf,A=!!$&&1/[1].lastIndexOf(1,-0)<0,E=y("lastIndexOf"),R=A||!E;x.exports=R?function(O){if(A)return u($,this,arguments)||0;var C=d(this),D=p(C);if(D===0)return-1;var M=D-1;for(arguments.length>1&&(M=T(M,h(arguments[1]))),M<0&&(M=D+M);M>=0;M--)if(M in C&&C[M]===O)return M||0;return-1}:$},function(x,b,r){var u=r(3),d=r(83).map,h=r(142),p=h("map");u({target:"Array",proto:!0,forced:!p},{map:function(T){return d(this,T,arguments.length>1?arguments[1]:void 0)}})},function(x,b,r){var u=r(3),d=r(7),h=r(89),p=r(141),y=Array,T=d(function(){function $(){}return!(y.of.call($)instanceof $)});u({target:"Array",stat:!0,forced:T},{of:function(){for(var A=0,E=arguments.length,R=new(h(this)?this:y)(E);E>A;)p(R,A,arguments[A++]);return R.length=E,R}})},function(x,b,r){var u=r(3),d=r(39),h=r(63),p=r(179),y=r(140),T=r(7),$=T(function(){return[].push.call({length:4294967296},1)!==4294967297}),A=function(){try{Object.defineProperty([],"length",{writable:!1}).push()}catch(R){return R instanceof TypeError}},E=$||!A();u({target:"Array",proto:!0,arity:1,forced:E},{push:function(I){var O=d(this),C=h(O),D=arguments.length;y(C+D);for(var M=0;M79&&p<83,$=T||!h("reduce");u({target:"Array",proto:!0,forced:$},{reduce:function(E){var R=arguments.length;return d(this,E,R,R>1?arguments[1]:void 0)}})},function(x,b,r){var u=r(30),d=r(39),h=r(13),p=r(63),y=TypeError,T="Reduce of empty array with no initial value",$=function(A){return function(E,R,I,O){var C=d(E),D=h(C),M=p(C);if(u(R),M===0&&I<2)throw new y(T);var F=A?M-1:0,z=A?-1:1;if(I<2)for(;;){if(F in D){O=D[F],F+=z;break}if(F+=z,A?F<0:M<=F)throw new y(T)}for(;A?F>=0:M>F;F+=z)F in D&&(O=R(O,D[F],F,C));return O}};x.exports={left:$(!1),right:$(!0)}},function(x,b,r){var u=r(183);x.exports=u==="NODE"},function(x,b,r){var u=r(4),d=r(28),h=r(15),p=function(y){return d.slice(0,y.length)===y};x.exports=function(){return p("Bun/")?"BUN":p("Cloudflare-Workers")?"CLOUDFLARE":p("Deno/")?"DENO":p("Node.js/")?"NODE":u.Bun&&typeof Bun.version=="string"?"BUN":u.Deno&&typeof Deno.version=="object"?"DENO":h(u.process)==="process"?"NODE":u.window&&u.document?"BROWSER":"REST"}()},function(x,b,r){var u=r(3),d=r(181).right,h=r(147),p=r(27),y=r(182),T=!y&&p>79&&p<83,$=T||!h("reduceRight");u({target:"Array",proto:!0,forced:$},{reduceRight:function(E){return d(this,E,arguments.length,arguments.length>1?arguments[1]:void 0)}})},function(x,b,r){var u=r(3),d=r(14),h=r(88),p=d([].reverse),y=[1,2];u({target:"Array",proto:!0,forced:String(y)===String(y.reverse())},{reverse:function(){return h(this)&&(this.length=this.length),p(this)}})},function(x,b,r){var u=r(3),d=r(88),h=r(89),p=r(20),y=r(60),T=r(63),$=r(12),A=r(141),E=r(33),R=r(142),I=r(76),O=R("slice"),C=E("species"),D=Array,M=Math.max;u({target:"Array",proto:!0,forced:!O},{slice:function(z,U){var j=$(this),G=T(j),B=y(z,G),V=y(U===void 0?G:U,G),Y,Z,J;if(d(j)&&(Y=j.constructor,h(Y)&&(Y===D||d(Y.prototype))?Y=void 0:p(Y)&&(Y=Y[C],Y===null&&(Y=void 0)),Y===D||Y===void 0))return I(j,B,V);for(Z=new(Y===void 0?D:Y)(M(V-B,0)),J=0;B1?arguments[1]:void 0)}})},function(x,b,r){var u=r(3),d=r(14),h=r(30),p=r(39),y=r(63),T=r(145),$=r(68),A=r(7),E=r(189),R=r(147),I=r(190),O=r(191),C=r(27),D=r(192),M=[],F=d(M.sort),z=d(M.push),U=A(function(){M.sort(void 0)}),j=A(function(){M.sort(null)}),G=R("sort"),B=!A(function(){if(C)return C<70;if(!(I&&I>3)){if(O)return!0;if(D)return D<603;var Z="",J,q,nt,rt;for(J=65;J<76;J++){switch(q=String.fromCharCode(J),J){case 66:case 69:case 70:case 72:nt=3;break;case 68:case 71:nt=4;break;default:nt=2}for(rt=0;rt<47;rt++)M.push({k:q+rt,v:nt})}for(M.sort(function(_,tt){return tt.v-_.v}),rt=0;rt$(q)?1:-1}};u({target:"Array",proto:!0,forced:V},{sort:function(J){J!==void 0&&h(J);var q=p(this);if(B)return J===void 0?F(q):F(q,J);var nt=[],rt=y(q),_,tt;for(tt=0;tt0;)p[E]=p[--E];E!==$++&&(p[E]=A)}else for(var R=d(T/2),I=h(u(p,0,R),y),O=h(u(p,R),y),C=I.length,D=O.length,M=0,F=0;Mj-Y+V;J--)R(U,J-1)}else if(V>Y)for(J=j-Y;J>G;J--)q=J+Y-1,nt=J+V-1,q in U?U[nt]=U[q]:R(U,nt);for(J=0;J2?p:u(h),$=new d(T);T>y;)$[y]=h[y++];return $}},function(x,b,r){var u=r(4);x.exports=function(d,h){var p=u[d],y=p&&p.prototype;return y&&y[h]}},function(x,b,r){var u=r(3),d=r(138),h=r(140),p=r(63),y=r(60),T=r(12),$=r(61),A=Array,E=Math.max,R=Math.min;u({target:"Array",proto:!0},{toSpliced:function(O,C){var D=T(this),M=p(D),F=y(O,M),z=arguments.length,U=0,j,G,B,V;for(z===0?j=G=0:z===1?(j=0,G=M-F):(j=z-2,G=R(E($(C),0),M-F)),B=h(M+j-G),V=A(B);U=A||R<0)throw new h("Incorrect index");for(var I=new y(A),O=0;O>8&255]},se=function(bt){return[bt&255,bt>>8&255,bt>>16&255,bt>>24&255]},ee=function(bt){return bt[3]<<24|bt[2]<<16|bt[1]<<8|bt[0]},fe=function(bt){return ae(D(bt),23,4)},Pe=function(bt){return ae(bt,52,8)},Me=function(bt,Ft,Tt){$(bt[rt],Ft,{configurable:!0,get:function(){return Tt(this)[Ft]}})},$e=function(bt,Ft,Tt,qt){var te=lt(bt),Zt=C(Tt),Yt=!!qt;if(Zt+Ft>te.byteLength)throw new kt(tt);var Ye=te.bytes,Ze=Zt+te.byteOffset,ut=j(Ye,Ze,Ze+Ft);return Yt?ut:ge(ut)},ce=function(bt,Ft,Tt,qt,te,Zt){var Yt=lt(bt),Ye=C(Tt),Ze=qt(+te),ut=!!Zt;if(Ye+Ft>Yt.byteLength)throw new kt(tt);for(var It=Yt.bytes,Pt=Ye+Yt.byteOffset,Ct=0;CtZt)throw new kt("Wrong offset");if(qt=qt===void 0?Zt-Yt:O(qt),Yt+qt>Zt)throw new kt(_);mt(this,{type:nt,buffer:Ft,byteLength:qt,byteOffset:Yt,bytes:te.bytes}),h||(this.buffer=Ft,this.byteLength=qt,this.byteOffset=Yt)},Dt=Ut[rt],h&&(Me(xt,"byteLength",et),Me(Ut,"buffer",lt),Me(Ut,"byteLength",lt),Me(Ut,"byteOffset",lt)),A(Dt,{getInt8:function(Ft){return $e(this,1,Ft)[0]<<24>>24},getUint8:function(Ft){return $e(this,1,Ft)[0]},getInt16:function(Ft){var Tt=$e(this,2,Ft,arguments.length>1?arguments[1]:!1);return(Tt[1]<<8|Tt[0])<<16>>16},getUint16:function(Ft){var Tt=$e(this,2,Ft,arguments.length>1?arguments[1]:!1);return Tt[1]<<8|Tt[0]},getInt32:function(Ft){return ee($e(this,4,Ft,arguments.length>1?arguments[1]:!1))},getUint32:function(Ft){return ee($e(this,4,Ft,arguments.length>1?arguments[1]:!1))>>>0},getFloat32:function(Ft){return Mt($e(this,4,Ft,arguments.length>1?arguments[1]:!1),23)},getFloat64:function(Ft){return Mt($e(this,8,Ft,arguments.length>1?arguments[1]:!1),52)},setInt8:function(Ft,Tt){ce(this,1,Ft,Ht,Tt)},setUint8:function(Ft,Tt){ce(this,1,Ft,Ht,Tt)},setInt16:function(Ft,Tt){ce(this,2,Ft,re,Tt,arguments.length>2?arguments[2]:!1)},setUint16:function(Ft,Tt){ce(this,2,Ft,re,Tt,arguments.length>2?arguments[2]:!1)},setInt32:function(Ft,Tt){ce(this,4,Ft,se,Tt,arguments.length>2?arguments[2]:!1)},setUint32:function(Ft,Tt){ce(this,4,Ft,se,Tt,arguments.length>2?arguments[2]:!1)},setFloat32:function(Ft,Tt){ce(this,4,Ft,fe,Tt,arguments.length>2?arguments[2]:!1)},setFloat64:function(Ft,Tt){ce(this,8,Ft,Pe,Tt,arguments.length>2?arguments[2]:!1)}});else{var Ae=Z&>.name!==q;!E(function(){gt(1)})||!E(function(){new gt(-1)})||E(function(){return new gt,new gt(1.5),new gt(NaN),gt.length!==1||Ae&&!J})?(xt=function(Ft){return R(this,yt),G(new gt(C(Ft)),this,xt)},xt[rt]=yt,yt.constructor=xt,B(xt,gt)):Ae&&J&&T(gt,"name",q),z&&F(Dt)!==Xt&&z(Dt,Xt);var Te=new Ut(new xt(2)),de=d(Dt.setInt8);Te.setInt8(0,2147483648),Te.setInt8(1,2147483649),(Te.getInt8(0)||!Te.getInt8(1))&&A(Dt,{setInt8:function(Ft,Tt){de(this,Ft,Tt<<24>>24)},setUint8:function(Ft,Tt){de(this,Ft,Tt<<24>>24)}},{unsafe:!0})}V(xt,q),V(Ut,nt),x.exports={ArrayBuffer:xt,DataView:Ut}},function(x){x.exports=typeof ArrayBuffer!="undefined"&&typeof DataView!="undefined"},function(x,b,r){var u=r(47);x.exports=function(d,h,p){for(var y in h)u(d,y,h[y],p);return d}},function(x,b,r){var u=r(24),d=TypeError;x.exports=function(h,p){if(u(p,h))return h;throw new d("Incorrect invocation")}},function(x,b,r){var u=r(61),d=r(64),h=RangeError;x.exports=function(p){if(p===void 0)return 0;var y=u(p),T=d(y);if(y!==T)throw new h("Wrong length or index");return T}},function(x,b,r){var u=r(214),d=11920928955078125e-23,h=34028234663852886e22,p=11754943508222875e-54;x.exports=Math.fround||function(T){return u(T,d,h,p)}},function(x,b,r){var u=r(215),d=r(216),h=Math.abs,p=2220446049250313e-31;x.exports=function(y,T,$,A){var E=+y,R=h(E),I=u(E);if(R$||C!==C?I*(1/0):I*C}},function(x){x.exports=Math.sign||function(r){var u=+r;return u===0||u!==u?u:u<0?-1:1}},function(x){var b=2220446049250313e-31,r=1/b;x.exports=function(u){return u+r-r}},function(x){var b=Array,r=Math.abs,u=Math.pow,d=Math.floor,h=Math.log,p=Math.LN2,y=function($,A,E){var R=b(E),I=E*8-A-1,O=(1<>1,D=A===23?u(2,-24)-u(2,-77):0,M=$<0||$===0&&1/$<0?1:0,F=0,z,U,j;for($=r($),$!==$||$===1/0?(U=$!==$?1:0,z=O):(z=d(h($)/p),j=u(2,-z),$*j<1&&(z--,j*=2),z+C>=1?$+=D/j:$+=D*u(2,1-C),$*j>=2&&(z++,j/=2),z+C>=O?(U=0,z=O):z+C>=1?(U=($*j-1)*u(2,A),z+=C):(U=$*u(2,C-1)*u(2,A),z=0));A>=8;)R[F++]=U&255,U/=256,A-=8;for(z=z<0;)R[F++]=z&255,z/=256,I-=8;return R[F-1]|=M*128,R},T=function($,A){var E=$.length,R=E*8-A-1,I=(1<>1,C=R-7,D=E-1,M=$[D--],F=M&127,z;for(M>>=7;C>0;)F=F*256+$[D--],C-=8;for(z=F&(1<<-C)-1,F>>=-C,C+=A;C>0;)z=z*256+$[D--],C-=8;if(F===0)F=1-O;else{if(F===I)return z?NaN:M?-1/0:1/0;z+=u(2,A),F-=O}return(M?-1:1)*z*u(2,F-A)};x.exports={pack:y,unpack:T}},function(x,b,r){var u=r(3),d=r(219),h=d.NATIVE_ARRAY_BUFFER_VIEWS;u({target:"ArrayBuffer",stat:!0,forced:!h},{isView:d.isView})},function(x,b,r){var u=r(209),d=r(6),h=r(4),p=r(21),y=r(20),T=r(38),$=r(69),A=r(31),E=r(43),R=r(47),I=r(77),O=r(24),C=r(128),D=r(113),M=r(33),F=r(40),z=r(51),U=z.enforce,j=z.get,G=h.Int8Array,B=G&&G.prototype,V=h.Uint8ClampedArray,Y=V&&V.prototype,Z=G&&C(G),J=B&&C(B),q=Object.prototype,nt=h.TypeError,rt=M("toStringTag"),_=F("TYPED_ARRAY_TAG"),tt="TypedArrayConstructor",et=u&&!!D&&$(h.opera)!=="Opera",lt=!1,mt,gt,xt,yt={Int8Array:1,Uint8Array:1,Uint8ClampedArray:1,Int16Array:2,Uint16Array:2,Int32Array:4,Uint32Array:4,Float32Array:4,Float64Array:8},Ut={BigInt64Array:8,BigUint64Array:8},Dt=function(Ht){if(!y(Ht))return!1;var re=$(Ht);return re==="DataView"||T(yt,re)||T(Ut,re)},Xt=function(Mt){var Ht=C(Mt);if(y(Ht)){var re=j(Ht);return re&&T(re,tt)?re[tt]:Xt(Ht)}},Qt=function(Mt){if(!y(Mt))return!1;var Ht=$(Mt);return T(yt,Ht)||T(Ut,Ht)},kt=function(Mt){if(Qt(Mt))return Mt;throw new nt("Target is not a typed array")},me=function(Mt){if(p(Mt)&&(!D||O(Z,Mt)))return Mt;throw new nt(A(Mt)+" is not a typed array constructor")},ge=function(Mt,Ht,re,se){if(d){if(re)for(var ee in yt){var fe=h[ee];if(fe&&T(fe.prototype,Mt))try{delete fe.prototype[Mt]}catch(Pe){try{fe.prototype[Mt]=Ht}catch(Me){}}}(!J[Mt]||re)&&R(J,Mt,re?Ht:et&&B[Mt]||Ht,se)}},ae=function(Mt,Ht,re){var se,ee;if(d){if(D){if(re){for(se in yt)if(ee=h[se],ee&&T(ee,Mt))try{delete ee[Mt]}catch(fe){}}if(!Z[Mt]||re)try{return R(Z,Mt,re?Ht:et&&Z[Mt]||Ht)}catch(fe){}else return}for(se in yt)ee=h[se],ee&&(!ee[Mt]||re)&&R(ee,Mt,Ht)}};for(mt in yt)gt=h[mt],xt=gt&>.prototype,xt?U(xt)[tt]=gt:et=!1;for(mt in Ut)gt=h[mt],xt=gt&>.prototype,xt&&(U(xt)[tt]=gt);if((!et||!p(Z)||Z===Function.prototype)&&(Z=function(){throw new nt("Incorrect invocation")},et))for(mt in yt)h[mt]&&D(h[mt],Z);if((!et||!J||J===q)&&(J=Z.prototype,et))for(mt in yt)h[mt]&&D(h[mt].prototype,J);if(et&&C(Y)!==J&&D(Y,J),d&&!T(J,rt)){lt=!0,I(J,rt,{configurable:!0,get:function(){return y(this)?this[_]:void 0}});for(mt in yt)h[mt]&&E(h[mt],_,mt)}x.exports={NATIVE_ARRAY_BUFFER_VIEWS:et,TYPED_ARRAY_TAG:lt&&_,aTypedArray:kt,aTypedArrayConstructor:me,exportTypedArrayMethod:ge,exportTypedArrayStaticMethod:ae,getTypedArrayConstructor:Xt,isView:Dt,isTypedArray:Qt,TypedArray:Z,TypedArrayPrototype:J}},function(x,b,r){var u=r(3),d=r(85),h=r(7),p=r(208),y=r(46),T=r(60),$=r(64),A=p.ArrayBuffer,E=p.DataView,R=E.prototype,I=d(A.prototype.slice),O=d(R.getUint8),C=d(R.setUint8),D=h(function(){return!new A(2).slice(1,void 0).byteLength});u({target:"ArrayBuffer",proto:!0,unsafe:!0,forced:D},{slice:function(F,z){if(I&&z===void 0)return I(y(this),F);for(var U=y(this).byteLength,j=T(F,U),G=T(z===void 0?U:z,U),B=new A($(G-j)),V=new E(this),Y=new E(B),Z=0;j>>15,O=R>>>10&p,C=R&y;return O===p?C===0?I===0?1/0:-1/0:NaN:O===0?C*(I===0?T:-T):h(2,O-15)*(I===0?1+C*$:-1-C*$)},E=d(DataView.prototype.getUint16);u({target:"DataView",proto:!0},{getFloat16:function(I){var O=E(this,I,arguments.length>1?arguments[1]:!1);return A(O)}})},function(x,b,r){var u=r(3),d=r(14),h=r(225),p=r(212),y=r(226),T=r(216),$=Math.pow,A=65520,E=61005353927612305e-21,R=16777216,I=1024,O=function(D){if(D!==D)return 32256;if(D===0)return(1/D===-1/0)<<15;var M=D<0;if(M&&(D=-D),D>=A)return M<<15|31744;if(D2?arguments[2]:!1)}})},function(x,b,r){var u=r(69),d=TypeError;x.exports=function(h){if(u(h)==="DataView")return h;throw new d("Argument is not a DataView")}},function(x){var b=Math.log,r=Math.LN2;x.exports=Math.log2||function(d){return b(d)/r}},function(x,b,r){var u=r(6),d=r(77),h=r(228),p=ArrayBuffer.prototype;u&&!("detached"in p)&&d(p,"detached",{configurable:!0,get:function(){return h(this)}})},function(x,b,r){var u=r(4),d=r(209),h=r(229),p=u.DataView;x.exports=function(y){if(!d||h(y)!==0)return!1;try{return new p(y),!1}catch(T){return!0}}},function(x,b,r){var u=r(4),d=r(114),h=r(15),p=u.ArrayBuffer,y=u.TypeError;x.exports=p&&d(p.prototype,"byteLength","get")||function(T){if(h(T)!=="ArrayBuffer")throw new y("ArrayBuffer expected");return T.byteLength}},function(x,b,r){var u=r(3),d=r(231);d&&u({target:"ArrayBuffer",proto:!0},{transfer:function(){return d(this,arguments.length?arguments[0]:void 0,!0)}})},function(x,b,r){var u=r(4),d=r(14),h=r(114),p=r(212),y=r(232),T=r(229),$=r(233),A=r(235),E=u.structuredClone,R=u.ArrayBuffer,I=u.DataView,O=Math.min,C=R.prototype,D=I.prototype,M=d(C.slice),F=h(C,"resizable","get"),z=h(C,"maxByteLength","get"),U=d(D.getInt8),j=d(D.setInt8);x.exports=(A||$)&&function(G,B,V){var Y=T(G),Z=B===void 0?Y:p(B),J=!F||!F(G),q;if(y(G),A&&(G=E(G,{transfer:[G]}),Y===Z&&(V||J)))return G;if(Y>=Z&&(!V||J))q=M(G,0,Z);else{var nt=V&&!J&&z?{maxByteLength:z(G)}:void 0;q=new R(Z,nt);for(var rt=new I(G),_=new I(q),tt=O(Z,Y),et=0;et92||p==="NODE"&&h>94||p==="BROWSER"&&h>97)return!1;var T=new ArrayBuffer(8),$=y(T,{transfer:[T]});return T.byteLength!==0||$.byteLength!==8})},function(x,b,r){var u=r(3),d=r(231);d&&u({target:"ArrayBuffer",proto:!0},{transferToFixedLength:function(){return d(this,arguments.length?arguments[0]:void 0,!1)}})},function(x,b,r){var u=r(3),d=r(14),h=r(7),p=h(function(){return new Date(16e11).getYear()!==120}),y=d(Date.prototype.getFullYear);u({target:"Date",proto:!0,forced:p},{getYear:function(){return y(this)-1900}})},function(x,b,r){var u=r(3),d=r(14),h=Date,p=d(h.prototype.getTime);u({target:"Date",stat:!0},{now:function(){return p(new h)}})},function(x,b,r){var u=r(3),d=r(14),h=r(61),p=Date.prototype,y=d(p.getTime),T=d(p.setFullYear);u({target:"Date",proto:!0},{setYear:function(A){y(this);var E=h(A),R=E>=0&&E<=99?E+1900:E;return T(this,R)}})},function(x,b,r){var u=r(3);u({target:"Date",proto:!0},{toGMTString:Date.prototype.toUTCString})},function(x,b,r){var u=r(3),d=r(242);u({target:"Date",proto:!0,forced:Date.prototype.toISOString!==d},{toISOString:d})},function(x,b,r){var u=r(14),d=r(7),h=r(243).start,p=RangeError,y=isFinite,T=Math.abs,$=Date.prototype,A=$.toISOString,E=u($.getTime),R=u($.getUTCDate),I=u($.getUTCFullYear),O=u($.getUTCHours),C=u($.getUTCMilliseconds),D=u($.getUTCMinutes),M=u($.getUTCMonth),F=u($.getUTCSeconds);x.exports=d(function(){return A.call(new Date(-50000000000001))!=="0385-07-25T07:06:39.999Z"})||!d(function(){A.call(new Date(NaN))})?function(){if(!y(E(this)))throw new p("Invalid time value");var U=this,j=I(U),G=C(U),B=j<0?"-":j>9999?"+":"";return B+h(T(j),B?6:4,0)+"-"+h(M(U)+1,2,0)+"-"+h(R(U),2,0)+"T"+h(O(U),2,0)+":"+h(D(U),2,0)+":"+h(F(U),2,0)+"."+h(G,3,0)+"Z"}:A},function(x,b,r){var u=r(14),d=r(64),h=r(68),p=r(244),y=r(16),T=u(p),$=u("".slice),A=Math.ceil,E=function(R){return function(I,O,C){var D=h(y(I)),M=d(O),F=D.length,z=C===void 0?" ":h(C),U,j;return M<=F||z===""?D:(U=M-F,j=T(z,A(U/z.length)),j.length>U&&(j=$(j,0,U)),R?D+j:j+D)}};x.exports={start:E(!1),end:E(!0)}},function(x,b,r){var u=r(61),d=r(68),h=r(16),p=RangeError;x.exports=function(T){var $=d(h(this)),A="",E=u(T);if(E<0||E===1/0)throw new p("Wrong number of repetitions");for(;E>0;(E>>>=1)&&($+=$))E&1&&(A+=$);return A}},function(x,b,r){var u=r(3),d=r(7),h=r(39),p=r(19),y=d(function(){return new Date(NaN).toJSON()!==null||Date.prototype.toJSON.call({toISOString:function(){return 1}})!==1});u({target:"Date",proto:!0,arity:1,forced:y},{toJSON:function($){var A=h(this),E=p(A,"number");return typeof E=="number"&&!isFinite(E)?null:A.toISOString()}})},function(x,b,r){var u=r(38),d=r(47),h=r(247),p=r(33),y=p("toPrimitive"),T=Date.prototype;u(T,y)||d(T,y,h)},function(x,b,r){var u=r(46),d=r(32),h=TypeError;x.exports=function(p){if(u(this),p==="string"||p==="default")p="string";else if(p!=="number")throw new h("Incorrect hint");return d(this,p)}},function(x,b,r){var u=r(14),d=r(47),h=Date.prototype,p="Invalid Date",y="toString",T=u(h[y]),$=u(h.getTime);String(new Date(NaN))!==p&&d(h,y,function(){var E=$(this);return E===E?T(this):p})},function(x,b,r){var u=r(3),d=r(14),h=r(68),p=d("".charAt),y=d("".charCodeAt),T=d(/./.exec),$=d(1 .toString),A=d("".toUpperCase),E=/[\w*+\-./@]/,R=function(I,O){for(var C=$(I,16);C.length1?arguments[1]:void 0),_;_=_?_.next:nt.first;)for(rt(_.value,_.key,this);_&&_.removed;)_=_.previous},has:function(q){return!!Z(this,q)}}),h(B,U?{get:function(q){var nt=Z(this,q);return nt&&nt.value},set:function(q,nt){return Y(this,q===0?0:q,nt)}}:{add:function(q){return Y(this,q=q===0?0:q,q)}}),I&&d(B,"size",{configurable:!0,get:function(){return V(this).size}}),G},setStrong:function(F,z,U){var j=z+" Iterator",G=M(z),B=M(j);A(F,z,function(V,Y){D(this,{type:j,target:V,state:G(V),kind:Y,last:null})},function(){for(var V=B(this),Y=V.kind,Z=V.last;Z&&Z.removed;)Z=Z.previous;return!V.target||!(V.last=Z=Z?Z.next:V.state.first)?(V.target=null,E(void 0,!0)):E(Y==="keys"?Z.key:Y==="values"?Z.value:[Z.key,Z.value],!1)},U?"entries":"values",!U,!0),R(z)}}},function(x,b,r){var u=r(3),d=r(14),h=r(30),p=r(16),y=r(130),T=r(284),$=r(36),A=r(7),E=T.Map,R=T.has,I=T.get,O=T.set,C=d([].push),D=$||A(function(){return E.groupBy("ab",function(M){return M}).get("a").length!==1});u({target:"Map",stat:!0,forced:$||D},{groupBy:function(F,z){p(F),h(z);var U=new E,j=0;return y(F,function(G){var B=z(G,j++);R(U,B)?C(I(U,B),G):O(U,B,[G])}),U}})},function(x,b,r){var u=r(14),d=Map.prototype;x.exports={Map,set:u(d.set),get:u(d.get),has:u(d.has),remove:u(d.delete),proto:d}},function(x,b,r){var u=r(3),d=r(286),h=Math.acosh,p=Math.log,y=Math.sqrt,T=Math.LN2,$=!h||Math.floor(h(Number.MAX_VALUE))!==710||h(1/0)!==1/0;u({target:"Math",stat:!0,forced:$},{acosh:function(E){var R=+E;return R<1?NaN:R>9490626562425156e-8?p(R)+T:d(R-1+y(R-1)*y(R+1))}})},function(x){var b=Math.log;x.exports=Math.log1p||function(u){var d=+u;return d>-1e-8&&d<1e-8?d-d*d/2:b(1+d)}},function(x,b,r){var u=r(3),d=Math.asinh,h=Math.log,p=Math.sqrt;function y($){var A=+$;return!isFinite(A)||A===0?A:A<0?-y(-A):h(A+p(A*A+1))}var T=!(d&&1/d(0)>0);u({target:"Math",stat:!0,forced:T},{asinh:y})},function(x,b,r){var u=r(3),d=Math.atanh,h=Math.log,p=!(d&&1/d(-0)<0);u({target:"Math",stat:!0,forced:p},{atanh:function(T){var $=+T;return $===0?$:h((1+$)/(1-$))/2}})},function(x,b,r){var u=r(3),d=r(215),h=Math.abs,p=Math.pow;u({target:"Math",stat:!0},{cbrt:function(T){var $=+T;return d($)*p(h($),.3333333333333333)}})},function(x,b,r){var u=r(3),d=Math.floor,h=Math.log,p=Math.LOG2E;u({target:"Math",stat:!0},{clz32:function(T){var $=T>>>0;return $?31-d(h($+.5)*p):32}})},function(x,b,r){var u=r(3),d=r(292),h=Math.cosh,p=Math.abs,y=Math.E,T=!h||h(710)===1/0;u({target:"Math",stat:!0,forced:T},{cosh:function(A){var E=d(p(A)-1)+1;return(E+1/(E*y*y))*(y/2)}})},function(x){var b=Math.expm1,r=Math.exp;x.exports=!b||b(10)>22025.465794806718||b(10)<22025.465794806718||b(-2e-17)!==-2e-17?function(d){var h=+d;return h===0?h:h>-1e-6&&h<1e-6?h+h*h/2:r(h)-1}:b},function(x,b,r){var u=r(3),d=r(292);u({target:"Math",stat:!0,forced:d!==Math.expm1},{expm1:d})},function(x,b,r){var u=r(3),d=r(213);u({target:"Math",stat:!0},{fround:d})},function(x,b,r){var u=r(3),d=r(214),h=.0009765625,p=65504,y=6103515625e-14;u({target:"Math",stat:!0},{f16round:function($){return d($,h,p,y)}})},function(x,b,r){var u=r(3),d=Math.hypot,h=Math.abs,p=Math.sqrt,y=!!d&&d(1/0,NaN)!==1/0;u({target:"Math",stat:!0,arity:2,forced:y},{hypot:function($,A){for(var E=0,R=0,I=arguments.length,O=0,C,D;R0?(D=C/O,E+=D*D):E+=C;return O===1/0?1/0:O*p(E)}})},function(x,b,r){var u=r(3),d=r(7),h=Math.imul,p=d(function(){return h(4294967295,5)!==-5||h.length!==2});u({target:"Math",stat:!0,forced:p},{imul:function(T,$){var A=65535,E=+T,R=+$,I=A&E,O=A&R;return 0|I*O+((A&E>>>16)*O+I*(A&R>>>16)<<16>>>0)}})},function(x,b,r){var u=r(3),d=r(299);u({target:"Math",stat:!0},{log10:d})},function(x){var b=Math.log,r=Math.LOG10E;x.exports=Math.log10||function(d){return b(d)*r}},function(x,b,r){var u=r(3),d=r(286);u({target:"Math",stat:!0},{log1p:d})},function(x,b,r){var u=r(3),d=r(226);u({target:"Math",stat:!0},{log2:d})},function(x,b,r){var u=r(3),d=r(215);u({target:"Math",stat:!0},{sign:d})},function(x,b,r){var u=r(3),d=r(7),h=r(292),p=Math.abs,y=Math.exp,T=Math.E,$=d(function(){return Math.sinh(-2e-17)!==-2e-17});u({target:"Math",stat:!0,forced:$},{sinh:function(E){var R=+E;return p(R)<1?(h(R)-h(-R))/2:(y(R-1)-y(-R-1))*(T/2)}})},function(x,b,r){var u=r(3),d=r(292),h=Math.exp;u({target:"Math",stat:!0},{tanh:function(y){var T=+y,$=d(T),A=d(-T);return $===1/0?1:A===1/0?-1:($-A)/(h(T)+h(-T))}})},function(x,b,r){var u=r(82);u(Math,"Math",!0)},function(x,b,r){var u=r(3),d=r(62);u({target:"Math",stat:!0},{trunc:d})},function(x,b,r){var u=r(3),d=r(36),h=r(6),p=r(4),y=r(80),T=r(14),$=r(67),A=r(38),E=r(118),R=r(24),I=r(22),O=r(19),C=r(7),D=r(57).f,M=r(5).f,F=r(44).f,z=r(308),U=r(309).trim,j="Number",G=p[j],B=y[j],V=G.prototype,Y=p.TypeError,Z=T("".slice),J=T("".charCodeAt),q=function(lt){var mt=O(lt,"number");return typeof mt=="bigint"?mt:nt(mt)},nt=function(lt){var mt=O(lt,"number"),gt,xt,yt,Ut,Dt,Xt,Qt,kt;if(I(mt))throw new Y("Cannot convert a Symbol value to a number");if(typeof mt=="string"&&mt.length>2){if(mt=U(mt),gt=J(mt,0),gt===43||gt===45){if(xt=J(mt,2),xt===88||xt===120)return NaN}else if(gt===48){switch(J(mt,1)){case 66:case 98:yt=2,Ut=49;break;case 79:case 111:yt=8,Ut=55;break;default:return+mt}for(Dt=Z(mt,2),Xt=Dt.length,Qt=0;QtUt)return NaN;return parseInt(Dt,yt)}}return+mt},rt=$(j,!G(" 0o1")||!G("0b1")||G("+0x1")),_=function(lt){return R(V,lt)&&C(function(){z(lt)})},tt=function(mt){var gt=arguments.length<1?0:G(q(mt));return _(this)?E(Object(gt),this,tt):gt};tt.prototype=V,rt&&!d&&(V.constructor=tt),u({global:!0,constructor:!0,wrap:!0,forced:rt},{Number:tt});var et=function(lt,mt){for(var gt=h?D(mt):"MAX_VALUE,MIN_VALUE,NaN,NEGATIVE_INFINITY,POSITIVE_INFINITY,EPSILON,MAX_SAFE_INTEGER,MIN_SAFE_INTEGER,isFinite,isInteger,isNaN,isSafeInteger,parseFloat,parseInt,fromString,range".split(","),xt=0,yt;gt.length>xt;xt++)A(mt,yt=gt[xt])&&!A(lt,yt)&&F(lt,yt,M(mt,yt))};d&&B&&et(y[j],B),(rt||d)&&et(y[j],G)},function(x,b,r){var u=r(14);x.exports=u(1 .valueOf)},function(x,b,r){var u=r(14),d=r(16),h=r(68),p=r(310),y=u("".replace),T=RegExp("^["+p+"]+"),$=RegExp("(^|[^"+p+"])["+p+"]+$"),A=function(E){return function(R){var I=h(d(R));return E&1&&(I=y(I,T,"")),E&2&&(I=y(I,$,"$1")),I}};x.exports={start:A(1),end:A(2),trim:A(3)}},function(x){x.exports=` +\v\f\r \xA0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF`},function(x,b,r){var u=r(3);u({target:"Number",stat:!0,nonConfigurable:!0,nonWritable:!0},{EPSILON:Math.pow(2,-52)})},function(x,b,r){var u=r(3),d=r(313);u({target:"Number",stat:!0},{isFinite:d})},function(x,b,r){var u=r(4),d=u.isFinite;x.exports=Number.isFinite||function(p){return typeof p=="number"&&d(p)}},function(x,b,r){var u=r(3),d=r(315);u({target:"Number",stat:!0},{isInteger:d})},function(x,b,r){var u=r(20),d=Math.floor;x.exports=Number.isInteger||function(p){return!u(p)&&isFinite(p)&&d(p)===p}},function(x,b,r){var u=r(3);u({target:"Number",stat:!0},{isNaN:function(h){return h!==h}})},function(x,b,r){var u=r(3),d=r(315),h=Math.abs;u({target:"Number",stat:!0},{isSafeInteger:function(y){return d(y)&&h(y)<=9007199254740991}})},function(x,b,r){var u=r(3);u({target:"Number",stat:!0,nonConfigurable:!0,nonWritable:!0},{MAX_SAFE_INTEGER:9007199254740991})},function(x,b,r){var u=r(3);u({target:"Number",stat:!0,nonConfigurable:!0,nonWritable:!0},{MIN_SAFE_INTEGER:-9007199254740991})},function(x,b,r){var u=r(3),d=r(321);u({target:"Number",stat:!0,forced:Number.parseFloat!==d},{parseFloat:d})},function(x,b,r){var u=r(4),d=r(7),h=r(14),p=r(68),y=r(309).trim,T=r(310),$=h("".charAt),A=u.parseFloat,E=u.Symbol,R=E&&E.iterator,I=1/A(T+"-0")!==-1/0||R&&!d(function(){A(Object(R))});x.exports=I?function(C){var D=y(p(C)),M=A(D);return M===0&&$(D,0)==="-"?-0:M}:A},function(x,b,r){var u=r(3),d=r(323);u({target:"Number",stat:!0,forced:Number.parseInt!==d},{parseInt:d})},function(x,b,r){var u=r(4),d=r(7),h=r(14),p=r(68),y=r(309).trim,T=r(310),$=u.parseInt,A=u.Symbol,E=A&&A.iterator,R=/^[+-]?0x/i,I=h(R.exec),O=$(T+"08")!==8||$(T+"0x16")!==22||E&&!d(function(){$(Object(E))});x.exports=O?function(D,M){var F=y(p(D));return $(F,M>>>0||(I(R,F)?16:10))}:$},function(x,b,r){var u=r(3),d=r(14),h=r(61),p=r(308),y=r(244),T=r(299),$=r(7),A=RangeError,E=String,R=isFinite,I=Math.abs,O=Math.floor,C=Math.pow,D=Math.round,M=d(1 .toExponential),F=d(y),z=d("".slice),U=M(-69e-12,4)==="-6.9000e-11"&&M(1.255,2)==="1.25e+0"&&M(12345,3)==="1.235e+4"&&M(25,0)==="3e+1",j=function(){return $(function(){M(1,1/0)})&&$(function(){M(1,-1/0)})},G=function(){return!$(function(){M(1/0,1/0),M(NaN,1/0)})},B=!U||!j()||!G();u({target:"Number",proto:!0,forced:B},{toExponential:function(Y){var Z=p(this);if(Y===void 0)return M(Z);var J=h(Y);if(!R(Z))return String(Z);if(J<0||J>20)throw new A("Incorrect fraction digits");if(U)return M(Z,J);var q="",nt,rt,_,tt;if(Z<0&&(q="-",Z=-Z),Z===0)rt=0,nt=F("0",J+1);else{var et=T(Z);rt=O(et);var lt=C(10,rt-J),mt=D(Z/lt);2*Z>=(2*mt+1)*lt&&(mt+=1),mt>=C(10,J+1)&&(mt/=10,rt+=1),nt=E(mt)}return J!==0&&(nt=z(nt,0,1)+"."+z(nt,1)),rt===0?(_="+",tt="0"):(_=rt>0?"+":"-",tt=E(I(rt))),nt+="e"+_+tt,q+nt}})},function(x,b,r){var u=r(3),d=r(14),h=r(61),p=r(308),y=r(244),T=r(7),$=RangeError,A=String,E=Math.floor,R=d(y),I=d("".slice),O=d(1 .toFixed),C=function(j,G,B){return G===0?B:G%2===1?C(j,G-1,B*j):C(j*j,G/2,B)},D=function(j){for(var G=0,B=j;B>=4096;)G+=12,B/=4096;for(;B>=2;)G+=1,B/=2;return G},M=function(j,G,B){for(var V=-1,Y=B;++V<6;)Y+=G*j[V],j[V]=Y%1e7,Y=E(Y/1e7)},F=function(j,G){for(var B=6,V=0;--B>=0;)V+=j[B],j[B]=E(V/G),V=V%G*1e7},z=function(j){for(var G=6,B="";--G>=0;)if(B!==""||G===0||j[G]!==0){var V=A(j[G]);B=B===""?V:B+R("0",7-V.length)+V}return B},U=T(function(){return O(8e-5,3)!=="0.000"||O(.9,0)!=="1"||O(1.255,2)!=="1.25"||O(0xde0b6b3a7640080,0)!=="1000000000000000128"})||!T(function(){O({})});u({target:"Number",proto:!0,forced:U},{toFixed:function(G){var B=p(this),V=h(G),Y=[0,0,0,0,0,0],Z="",J="0",q,nt,rt,_;if(V<0||V>20)throw new $("Incorrect fraction digits");if(B!==B)return"NaN";if(B<=-1e21||B>=1e21)return A(B);if(B<0&&(Z="-",B=-B),B>1e-21)if(q=D(B*C(2,69,1))-69,nt=q<0?B*C(2,-q,1):B/C(2,q,1),nt*=4503599627370496,q=52-q,q>0){for(M(Y,0,nt),rt=V;rt>=7;)M(Y,1e7,0),rt-=7;for(M(Y,C(10,rt,1),0),rt=q-1;rt>=23;)F(Y,8388608),rt-=23;F(Y,1<0?(_=J.length,J=Z+(_<=V?"0."+R("0",V-_)+J:I(J,0,_-V)+"."+I(J,_-V))):J=Z+J,J}})},function(x,b,r){var u=r(3),d=r(14),h=r(7),p=r(308),y=d(1 .toPrecision),T=h(function(){return y(1,void 0)!=="1"})||!h(function(){y({})});u({target:"Number",proto:!0,forced:T},{toPrecision:function(A){return A===void 0?y(p(this)):y(p(this),A)}})},function(x,b,r){var u=r(3),d=r(328);u({target:"Object",stat:!0,arity:2,forced:Object.assign!==d},{assign:d})},function(x,b,r){var u=r(6),d=r(14),h=r(8),p=r(7),y=r(73),T=r(66),$=r(10),A=r(39),E=r(13),R=Object.assign,I=Object.defineProperty,O=d([].concat);x.exports=!R||p(function(){if(u&&R({b:1},R(I({},"a",{enumerable:!0,get:function(){I(this,"b",{value:3,enumerable:!1})}}),{b:2})).b!==1)return!0;var C={},D={},M=Symbol("assign detection"),F="abcdefghijklmnopqrst";return C[M]=7,F.split("").forEach(function(z){D[z]=z}),R({},C)[M]!==7||y(R({},D)).join("")!==F})?function(D,M){for(var F=A(D),z=arguments.length,U=1,j=T.f,G=$.f;z>U;)for(var B=E(arguments[U++]),V=j?O(y(B),j(B)):y(B),Y=V.length,Z=0,J;Y>Z;)J=V[Z++],(!u||h(G,B,J))&&(F[J]=B[J]);return F}:R},function(x,b,r){var u=r(3),d=r(6),h=r(71);u({target:"Object",stat:!0,sham:!d},{create:h})},function(x,b,r){var u=r(3),d=r(6),h=r(331),p=r(30),y=r(39),T=r(44);d&&u({target:"Object",proto:!0,forced:h},{__defineGetter__:function(A,E){T.f(y(this),A,{get:p(E),enumerable:!0,configurable:!0})}})},function(x,b,r){var u=r(36),d=r(4),h=r(7),p=r(192);x.exports=u||!h(function(){if(!(p&&p<535)){var y=Math.random();__defineSetter__.call(null,y,function(){}),delete d[y]}})},function(x,b,r){var u=r(3),d=r(6),h=r(72).f;u({target:"Object",stat:!0,forced:Object.defineProperties!==h,sham:!d},{defineProperties:h})},function(x,b,r){var u=r(3),d=r(6),h=r(44).f;u({target:"Object",stat:!0,forced:Object.defineProperty!==h,sham:!d},{defineProperty:h})},function(x,b,r){var u=r(3),d=r(6),h=r(331),p=r(30),y=r(39),T=r(44);d&&u({target:"Object",proto:!0,forced:h},{__defineSetter__:function(A,E){T.f(y(this),A,{set:p(E),enumerable:!0,configurable:!0})}})},function(x,b,r){var u=r(3),d=r(336).entries;u({target:"Object",stat:!0},{entries:function(p){return d(p)}})},function(x,b,r){var u=r(6),d=r(7),h=r(14),p=r(128),y=r(73),T=r(12),$=r(10).f,A=h($),E=h([].push),R=u&&d(function(){var O=Object.create(null);return O[2]=2,!A(O,2)}),I=function(O){return function(C){for(var D=T(C),M=y(D),F=R&&p(D)===null,z=M.length,U=0,j=[],G;z>U;)G=M[U++],(!u||(F?G in D:A(D,G)))&&E(j,O?[G,D[G]]:D[G]);return j}};x.exports={entries:I(!0),values:I(!1)}},function(x,b,r){var u=r(3),d=r(281),h=r(7),p=r(20),y=r(278).onFreeze,T=Object.freeze,$=h(function(){T(1)});u({target:"Object",stat:!0,forced:$,sham:!d},{freeze:function(E){return T&&p(E)?T(y(E)):E}})},function(x,b,r){var u=r(3),d=r(130),h=r(141);u({target:"Object",stat:!0},{fromEntries:function(y){var T={};return d(y,function($,A){h(T,$,A)},{AS_ENTRIES:!0}),T}})},function(x,b,r){var u=r(3),d=r(7),h=r(12),p=r(5).f,y=r(6),T=!y||d(function(){p(1)});u({target:"Object",stat:!0,forced:T,sham:!y},{getOwnPropertyDescriptor:function(A,E){return p(h(A),E)}})},function(x,b,r){var u=r(3),d=r(6),h=r(56),p=r(12),y=r(5),T=r(141);u({target:"Object",stat:!0,sham:!d},{getOwnPropertyDescriptors:function(A){for(var E=p(A),R=y.f,I=h(E),O={},C=0,D,M;I.length>C;)M=R(E,D=I[C++]),M!==void 0&&T(O,D,M);return O}})},function(x,b,r){var u=r(3),d=r(7),h=r(75).f,p=d(function(){return!Object.getOwnPropertyNames(1)});u({target:"Object",stat:!0,forced:p},{getOwnPropertyNames:h})},function(x,b,r){var u=r(3),d=r(7),h=r(39),p=r(128),y=r(129),T=d(function(){p(1)});u({target:"Object",stat:!0,forced:T,sham:!y},{getPrototypeOf:function(A){return p(h(A))}})},function(x,b,r){var u=r(3),d=r(23),h=r(14),p=r(30),y=r(16),T=r(18),$=r(130),A=r(7),E=Object.groupBy,R=d("Object","create"),I=h([].push),O=!E||A(function(){return E("ab",function(C){return C}).a.length!==1});u({target:"Object",stat:!0,forced:O},{groupBy:function(D,M){y(D),p(M);var F=R(null),z=0;return $(D,function(U){var j=T(M(U,z++));j in F?I(F[j],U):F[j]=[U]}),F}})},function(x,b,r){var u=r(3),d=r(38);u({target:"Object",stat:!0},{hasOwn:d})},function(x,b,r){var u=r(3),d=r(346);u({target:"Object",stat:!0},{is:d})},function(x){x.exports=Object.is||function(r,u){return r===u?r!==0||1/r===1/u:r!==r&&u!==u}},function(x,b,r){var u=r(3),d=r(279);u({target:"Object",stat:!0,forced:Object.isExtensible!==d},{isExtensible:d})},function(x,b,r){var u=r(3),d=r(7),h=r(20),p=r(15),y=r(280),T=Object.isFrozen,$=y||d(function(){T(1)});u({target:"Object",stat:!0,forced:$},{isFrozen:function(E){return!h(E)||y&&p(E)==="ArrayBuffer"?!0:T?T(E):!1}})},function(x,b,r){var u=r(3),d=r(7),h=r(20),p=r(15),y=r(280),T=Object.isSealed,$=y||d(function(){T(1)});u({target:"Object",stat:!0,forced:$},{isSealed:function(E){return!h(E)||y&&p(E)==="ArrayBuffer"?!0:T?T(E):!1}})},function(x,b,r){var u=r(3),d=r(39),h=r(73),p=r(7),y=p(function(){h(1)});u({target:"Object",stat:!0,forced:y},{keys:function($){return h(d($))}})},function(x,b,r){var u=r(3),d=r(6),h=r(331),p=r(39),y=r(18),T=r(128),$=r(5).f;d&&u({target:"Object",proto:!0,forced:h},{__lookupGetter__:function(E){var R=p(this),I=y(E),O;do if(O=$(R,I))return O.get;while(R=T(R))}})},function(x,b,r){var u=r(3),d=r(6),h=r(331),p=r(39),y=r(18),T=r(128),$=r(5).f;d&&u({target:"Object",proto:!0,forced:h},{__lookupSetter__:function(E){var R=p(this),I=y(E),O;do if(O=$(R,I))return O.set;while(R=T(R))}})},function(x,b,r){var u=r(3),d=r(20),h=r(278).onFreeze,p=r(281),y=r(7),T=Object.preventExtensions,$=y(function(){T(1)});u({target:"Object",stat:!0,forced:$,sham:!p},{preventExtensions:function(E){return T&&d(E)?T(h(E)):E}})},function(x,b,r){var u=r(6),d=r(77),h=r(20),p=r(116),y=r(39),T=r(16),$=Object.getPrototypeOf,A=Object.setPrototypeOf,E=Object.prototype,R="__proto__";if(u&&$&&A&&!(R in E))try{d(E,R,{configurable:!0,get:function(){return $(y(this))},set:function(O){var C=T(this);p(O)&&h(C)&&A(C,O)}})}catch(I){}},function(x,b,r){var u=r(3),d=r(20),h=r(278).onFreeze,p=r(281),y=r(7),T=Object.seal,$=y(function(){T(1)});u({target:"Object",stat:!0,forced:$,sham:!p},{seal:function(E){return T&&d(E)?T(h(E)):E}})},function(x,b,r){var u=r(3),d=r(113);u({target:"Object",stat:!0},{setPrototypeOf:d})},function(x,b,r){var u=r(70),d=r(47),h=r(358);u||d(Object.prototype,"toString",h,{unsafe:!0})},function(x,b,r){var u=r(70),d=r(69);x.exports=u?{}.toString:function(){return"[object "+d(this)+"]"}},function(x,b,r){var u=r(3),d=r(336).values;u({target:"Object",stat:!0},{values:function(p){return d(p)}})},function(x,b,r){var u=r(3),d=r(321);u({global:!0,forced:parseFloat!==d},{parseFloat:d})},function(x,b,r){var u=r(3),d=r(323);u({global:!0,forced:parseInt!==d},{parseInt:d})},function(x,b,r){r(363),r(379),r(381),r(382),r(383),r(384)},function(x,b,r){var u=r(3),d=r(36),h=r(182),p=r(4),y=r(8),T=r(47),$=r(113),A=r(82),E=r(194),R=r(30),I=r(21),O=r(20),C=r(211),D=r(364),M=r(366).set,F=r(369),z=r(374),U=r(375),j=r(371),G=r(51),B=r(376),V=r(377),Y=r(378),Z="Promise",J=V.CONSTRUCTOR,q=V.REJECTION_EVENT,nt=V.SUBCLASSING,rt=G.getterFor(Z),_=G.set,tt=B&&B.prototype,et=B,lt=tt,mt=p.TypeError,gt=p.document,xt=p.process,yt=Y.f,Ut=yt,Dt=!!(gt&>.createEvent&&p.dispatchEvent),Xt="unhandledrejection",Qt="rejectionhandled",kt=0,me=1,ge=2,ae=1,Mt=2,Ht,re,se,ee,fe=function(Tt){var qt;return O(Tt)&&I(qt=Tt.then)?qt:!1},Pe=function(Tt,qt){var te=qt.value,Zt=qt.state===me,Yt=Zt?Tt.ok:Tt.fail,Ye=Tt.resolve,Ze=Tt.reject,ut=Tt.domain,It,Pt,Ct;try{Yt?(Zt||(qt.rejection===Mt&&Te(qt),qt.rejection=ae),Yt===!0?It=te:(ut&&ut.enter(),It=Yt(te),ut&&(ut.exit(),Ct=!0)),It===Tt.promise?Ze(new mt("Promise-chain cycle")):(Pt=fe(It))?y(Pt,It,Ye,Ze):Ye(It)):Ze(te)}catch(Nt){ut&&!Ct&&ut.exit(),Ze(Nt)}},Me=function(Tt,qt){Tt.notified||(Tt.notified=!0,F(function(){for(var te=Tt.reactions,Zt;Zt=te.get();)Pe(Zt,Tt);Tt.notified=!1,qt&&!Tt.rejection&&ce(Tt)}))},$e=function(Tt,qt,te){var Zt,Yt;Dt?(Zt=gt.createEvent("Event"),Zt.promise=qt,Zt.reason=te,Zt.initEvent(Tt,!1,!0),p.dispatchEvent(Zt)):Zt={promise:qt,reason:te},!q&&(Yt=p["on"+Tt])?Yt(Zt):Tt===Xt&&z("Unhandled promise rejection",te)},ce=function(Tt){y(M,p,function(){var qt=Tt.facade,te=Tt.value,Zt=Ae(Tt),Yt;if(Zt&&(Yt=U(function(){h?xt.emit("unhandledRejection",te,qt):$e(Xt,qt,te)}),Tt.rejection=h||Ae(Tt)?Mt:ae,Yt.error))throw Yt.value})},Ae=function(Tt){return Tt.rejection!==ae&&!Tt.parent},Te=function(Tt){y(M,p,function(){var qt=Tt.facade;h?xt.emit("rejectionHandled",qt):$e(Qt,qt,Tt.value)})},de=function(Tt,qt,te){return function(Zt){Tt(qt,Zt,te)}},bt=function(Tt,qt,te){Tt.done||(Tt.done=!0,te&&(Tt=te),Tt.value=qt,Tt.state=ge,Me(Tt,!0))},Ft=function(Tt,qt,te){if(!Tt.done){Tt.done=!0,te&&(Tt=te);try{if(Tt.facade===qt)throw new mt("Promise can't be resolved itself");var Zt=fe(qt);Zt?F(function(){var Yt={done:!1};try{y(Zt,qt,de(Ft,Yt,Tt),de(bt,Yt,Tt))}catch(Ye){bt(Yt,Ye,Tt)}}):(Tt.value=qt,Tt.state=me,Me(Tt,!1))}catch(Yt){bt({done:!1},Yt,Tt)}}};if(J&&(et=function(qt){C(this,lt),R(qt),y(Ht,this);var te=rt(this);try{qt(de(Ft,te),de(bt,te))}catch(Zt){bt(te,Zt)}},lt=et.prototype,Ht=function(qt){_(this,{type:Z,done:!1,notified:!1,parent:!1,reactions:new j,rejection:!1,state:kt,value:null})},Ht.prototype=T(lt,"then",function(qt,te){var Zt=rt(this),Yt=yt(D(this,et));return Zt.parent=!0,Yt.ok=I(qt)?qt:!0,Yt.fail=I(te)&&te,Yt.domain=h?xt.domain:void 0,Zt.state===kt?Zt.reactions.add(Yt):F(function(){Pe(Yt,Zt)}),Yt.promise}),re=function(){var Tt=new Ht,qt=rt(Tt);this.promise=Tt,this.resolve=de(Ft,qt),this.reject=de(bt,qt)},Y.f=yt=function(Tt){return Tt===et||Tt===se?new re(Tt):Ut(Tt)},!d&&I(B)&&tt!==Object.prototype)){ee=tt.then,nt||T(tt,"then",function(qt,te){var Zt=this;return new et(function(Yt,Ye){y(ee,Zt,Yt,Ye)}).then(qt,te)},{unsafe:!0});try{delete tt.constructor}catch(Tt){}$&&$(tt,lt)}u({global:!0,constructor:!0,wrap:!0,forced:J},{Promise:et}),A(et,Z,!1,!0),E(Z)},function(x,b,r){var u=r(46),d=r(365),h=r(17),p=r(33),y=p("species");x.exports=function(T,$){var A=u(T).constructor,E;return A===void 0||h(E=u(A)[y])?$:d(E)}},function(x,b,r){var u=r(89),d=r(31),h=TypeError;x.exports=function(p){if(u(p))return p;throw new h(d(p)+" is not a constructor")}},function(x,b,r){var u=r(4),d=r(94),h=r(84),p=r(21),y=r(38),T=r(7),$=r(74),A=r(76),E=r(42),R=r(367),I=r(368),O=r(182),C=u.setImmediate,D=u.clearImmediate,M=u.process,F=u.Dispatch,z=u.Function,U=u.MessageChannel,j=u.String,G=0,B={},V="onreadystatechange",Y,Z,J,q;T(function(){Y=u.location});var nt=function(et){if(y(B,et)){var lt=B[et];delete B[et],lt()}},rt=function(et){return function(){nt(et)}},_=function(et){nt(et.data)},tt=function(et){u.postMessage(j(et),Y.protocol+"//"+Y.host)};(!C||!D)&&(C=function(lt){R(arguments.length,1);var mt=p(lt)?lt:z(lt),gt=A(arguments,1);return B[++G]=function(){d(mt,void 0,gt)},Z(G),G},D=function(lt){delete B[lt]},O?Z=function(et){M.nextTick(rt(et))}:F&&F.now?Z=function(et){F.now(rt(et))}:U&&!I?(J=new U,q=J.port2,J.port1.onmessage=_,Z=h(q.postMessage,q)):u.addEventListener&&p(u.postMessage)&&!u.importScripts&&Y&&Y.protocol!=="file:"&&!T(tt)?(Z=tt,u.addEventListener("message",_,!1)):V in E("script")?Z=function(et){$.appendChild(E("script"))[V]=function(){$.removeChild(this),nt(et)}}:Z=function(et){setTimeout(rt(et),0)}),x.exports={set:C,clear:D}},function(x){var b=TypeError;x.exports=function(r,u){if(r1?p(arguments,1):[],C=y.f(this),D=$(function(){return h(T(I),void 0,O)});return(D.error?C.reject:C.resolve)(D.value),C.promise}})},function(x,b,r){var u=r(3),d=r(378);u({target:"Promise",stat:!0},{withResolvers:function(){var p=d.f(this);return{promise:p.promise,resolve:p.resolve,reject:p.reject}}})},function(x,b,r){var u=r(3),d=r(94),h=r(30),p=r(46),y=r(7),T=!y(function(){Reflect.apply(function(){})});u({target:"Reflect",stat:!0,forced:T},{apply:function(A,E,R){return d(h(A),E,p(R))}})},function(x,b,r){var u=r(3),d=r(23),h=r(94),p=r(251),y=r(365),T=r(46),$=r(20),A=r(71),E=r(7),R=d("Reflect","construct"),I=Object.prototype,O=[].push,C=E(function(){function F(){}return!(R(function(){},[],F)instanceof F)}),D=!E(function(){R(function(){})}),M=C||D;u({target:"Reflect",stat:!0,forced:M,sham:M},{construct:function(z,U){y(z),T(U);var j=arguments.length<3?z:y(arguments[2]);if(D&&!C)return R(z,U,j);if(z===j){switch(U.length){case 0:return new z;case 1:return new z(U[0]);case 2:return new z(U[0],U[1]);case 3:return new z(U[0],U[1],U[2]);case 4:return new z(U[0],U[1],U[2],U[3])}var G=[null];return h(O,G,U),new(h(p,z,G))}var B=j.prototype,V=A($(B)?B:I),Y=h(z,V,U);return $(Y)?Y:V}})},function(x,b,r){var u=r(3),d=r(6),h=r(46),p=r(18),y=r(44),T=r(7),$=T(function(){Reflect.defineProperty(y.f({},1,{value:1}),1,{value:2})});u({target:"Reflect",stat:!0,forced:$,sham:!d},{defineProperty:function(E,R,I){h(E);var O=p(R);h(I);try{return y.f(E,O,I),!0}catch(C){return!1}}})},function(x,b,r){var u=r(3),d=r(46),h=r(5).f;u({target:"Reflect",stat:!0},{deleteProperty:function(y,T){var $=h(d(y),T);return $&&!$.configurable?!1:delete y[T]}})},function(x,b,r){var u=r(3),d=r(8),h=r(20),p=r(46),y=r(396),T=r(5),$=r(128);function A(E,R){var I=arguments.length<3?E:arguments[2],O,C;if(p(E)===I)return E[R];if(O=T.f(E,R),O)return y(O)?O.value:O.get===void 0?void 0:d(O.get,I);if(h(C=$(E)))return A(C,R,I)}u({target:"Reflect",stat:!0},{get:A})},function(x,b,r){var u=r(38);x.exports=function(d){return d!==void 0&&(u(d,"value")||u(d,"writable"))}},function(x,b,r){var u=r(3),d=r(6),h=r(46),p=r(5);u({target:"Reflect",stat:!0,sham:!d},{getOwnPropertyDescriptor:function(T,$){return p.f(h(T),$)}})},function(x,b,r){var u=r(3),d=r(46),h=r(128),p=r(129);u({target:"Reflect",stat:!0,sham:!p},{getPrototypeOf:function(T){return h(d(T))}})},function(x,b,r){var u=r(3);u({target:"Reflect",stat:!0},{has:function(h,p){return p in h}})},function(x,b,r){var u=r(3),d=r(46),h=r(279);u({target:"Reflect",stat:!0},{isExtensible:function(y){return d(y),h(y)}})},function(x,b,r){var u=r(3),d=r(56);u({target:"Reflect",stat:!0},{ownKeys:d})},function(x,b,r){var u=r(3),d=r(23),h=r(46),p=r(281);u({target:"Reflect",stat:!0,sham:!p},{preventExtensions:function(T){h(T);try{var $=d("Object","preventExtensions");return $&&$(T),!0}catch(A){return!1}}})},function(x,b,r){var u=r(3),d=r(8),h=r(46),p=r(20),y=r(396),T=r(7),$=r(44),A=r(5),E=r(128),R=r(11);function I(C,D,M){var F=arguments.length<4?C:arguments[3],z=A.f(h(C),D),U,j,G;if(!z){if(p(j=E(C)))return I(j,D,M,F);z=R(0)}if(y(z)){if(z.writable===!1||!p(F))return!1;if(U=A.f(F,D)){if(U.get||U.set||U.writable===!1)return!1;U.value=M,$.f(F,D,U)}else $.f(F,D,R(0,M))}else{if(G=z.set,G===void 0)return!1;d(G,F,M)}return!0}var O=T(function(){var C=function(){},D=$.f(new C,"a",{configurable:!0});return Reflect.set(C.prototype,"a",1,D)!==!1});u({target:"Reflect",stat:!0,forced:O},{set:I})},function(x,b,r){var u=r(3),d=r(46),h=r(115),p=r(113);p&&u({target:"Reflect",stat:!0},{setPrototypeOf:function(T,$){d(T),h($);try{return p(T,$),!0}catch(A){return!1}}})},function(x,b,r){var u=r(3),d=r(4),h=r(82);u({global:!0},{Reflect:{}}),h(d.Reflect,"Reflect",!0)},function(x,b,r){var u=r(6),d=r(4),h=r(14),p=r(67),y=r(118),T=r(43),$=r(71),A=r(57).f,E=r(24),R=r(407),I=r(68),O=r(408),C=r(410),D=r(117),M=r(47),F=r(7),z=r(38),U=r(51).enforce,j=r(194),G=r(33),B=r(411),V=r(412),Y=G("match"),Z=d.RegExp,J=Z.prototype,q=d.SyntaxError,nt=h(J.exec),rt=h("".charAt),_=h("".replace),tt=h("".indexOf),et=h("".slice),lt=/^\?<[^\s\d!#%&*+<=>@^][^\s!#%&*+<=>@^]*>/,mt=/a/g,gt=/a/g,xt=new Z(mt)!==mt,yt=C.MISSED_STICKY,Ut=C.UNSUPPORTED_Y,Dt=u&&(!xt||yt||B||V||F(function(){return gt[Y]=!1,Z(mt)!==mt||Z(gt)===gt||String(Z(mt,"i"))!=="/a/i"})),Xt=function(ae){for(var Mt=ae.length,Ht=0,re="",se=!1,ee;Ht<=Mt;Ht++){if(ee=rt(ae,Ht),ee==="\\"){re+=ee+rt(ae,++Ht);continue}!se&&ee==="."?re+="[\\s\\S]":(ee==="["?se=!0:ee==="]"&&(se=!1),re+=ee)}return re},Qt=function(ae){for(var Mt=ae.length,Ht=0,re="",se=[],ee=$(null),fe=!1,Pe=!1,Me=0,$e="",ce;Ht<=Mt;Ht++){if(ce=rt(ae,Ht),ce==="\\")ce+=rt(ae,++Ht);else if(ce==="]")fe=!1;else if(!fe)switch(!0){case ce==="[":fe=!0;break;case ce==="(":if(re+=ce,et(ae,Ht+1,Ht+3)==="?:")continue;nt(lt,et(ae,Ht+1))&&(Ht+=2,Pe=!0),Me++;continue;case(ce===">"&&Pe):if($e===""||z(ee,$e))throw new q("Invalid capture group name");ee[$e]=!0,se[se.length]=[$e,Me],Pe=!1,$e="";continue}Pe?$e+=ce:re+=ce}return[re,se]};if(p("RegExp",Dt)){for(var kt=function(Mt,Ht){var re=E(J,this),se=R(Mt),ee=Ht===void 0,fe=[],Pe=Mt,Me,$e,ce,Ae,Te,de;if(!re&&se&&ee&&Mt.constructor===kt)return Mt;if((se||E(J,Mt))&&(Mt=Mt.source,ee&&(Ht=O(Pe))),Mt=Mt===void 0?"":I(Mt),Ht=Ht===void 0?"":I(Ht),Pe=Mt,B&&"dotAll"in mt&&($e=!!Ht&&tt(Ht,"s")>-1,$e&&(Ht=_(Ht,/s/g,""))),Me=Ht,yt&&"sticky"in mt&&(ce=!!Ht&&tt(Ht,"y")>-1,ce&&Ut&&(Ht=_(Ht,/y/g,""))),V&&(Ae=Qt(Mt),Mt=Ae[0],fe=Ae[1]),Te=y(Z(Mt,Ht),re?this:J,kt),($e||ce||fe.length)&&(de=U(Te),$e&&(de.dotAll=!0,de.raw=kt(Xt(Mt),Me)),ce&&(de.sticky=!0),fe.length&&(de.groups=fe)),Mt!==Pe)try{T(Te,"source",Pe===""?"(?:)":Pe)}catch(bt){}return Te},me=A(Z),ge=0;me.length>ge;)D(kt,Z,me[ge++]);J.constructor=kt,kt.prototype=J,M(d,"RegExp",kt,{constructor:!0})}j("RegExp")},function(x,b,r){var u=r(20),d=r(15),h=r(33),p=h("match");x.exports=function(y){var T;return u(y)&&((T=y[p])!==void 0?!!T:d(y)==="RegExp")}},function(x,b,r){var u=r(8),d=r(38),h=r(24),p=r(409),y=RegExp.prototype;x.exports=function(T){var $=T.flags;return $===void 0&&!("flags"in y)&&!d(T,"flags")&&h(y,T)?u(p,T):$}},function(x,b,r){var u=r(46);x.exports=function(){var d=u(this),h="";return d.hasIndices&&(h+="d"),d.global&&(h+="g"),d.ignoreCase&&(h+="i"),d.multiline&&(h+="m"),d.dotAll&&(h+="s"),d.unicode&&(h+="u"),d.unicodeSets&&(h+="v"),d.sticky&&(h+="y"),h}},function(x,b,r){var u=r(7),d=r(4),h=d.RegExp,p=u(function(){var $=h("a","y");return $.lastIndex=2,$.exec("abcd")!==null}),y=p||u(function(){return!h("a","y").sticky}),T=p||u(function(){var $=h("^r","gy");return $.lastIndex=2,$.exec("str")!==null});x.exports={BROKEN_CARET:T,MISSED_STICKY:y,UNSUPPORTED_Y:p}},function(x,b,r){var u=r(7),d=r(4),h=d.RegExp;x.exports=u(function(){var p=h(".","s");return!(p.dotAll&&p.test(` +`)&&p.flags==="s")})},function(x,b,r){var u=r(7),d=r(4),h=d.RegExp;x.exports=u(function(){var p=h("(?b)","g");return p.exec("b").groups.a!=="b"||"b".replace(p,"$c")!=="bc"})},function(x,b,r){var u=r(3),d=r(14),h=r(414),p=r(38),y=r(243).start,T=r(310),$=Array,A=RegExp.escape,E=d("".charAt),R=d("".charCodeAt),I=d(1.1.toString),O=d([].join),C=/^[0-9a-z]/i,D=/^[$()*+./?[\\\]^{|}]/,M=RegExp("^[!\"#%&',\\-:;<=>@`~"+T+"]"),F=d(C.exec),z={" ":"t","\n":"n","\v":"v","\f":"f","\r":"r"},U=function(G){var B=I(R(G,0),16);return B.length<3?"\\x"+y(B,2,"0"):"\\u"+y(B,4,"0")},j=!A||A("ab")!=="\\x61b";u({target:"RegExp",stat:!0,forced:j},{escape:function(B){h(B);for(var V=B.length,Y=$(V),Z=0;Z=56320||Z+1>=V||(R(B,Z+1)&64512)!==56320?Y[Z]=U(J):(Y[Z]=J,Y[++Z]=E(B,Z))}}return O(Y,"")}})},function(x){var b=TypeError;x.exports=function(r){if(typeof r=="string")return r;throw new b("Argument is not a string")}},function(x,b,r){var u=r(6),d=r(411),h=r(15),p=r(77),y=r(51).get,T=RegExp.prototype,$=TypeError;u&&d&&p(T,"dotAll",{configurable:!0,get:function(){if(this!==T){if(h(this)==="RegExp")return!!y(this).dotAll;throw new $("Incompatible receiver, RegExp required")}}})},function(x,b,r){var u=r(3),d=r(417);u({target:"RegExp",proto:!0,forced:/./.exec!==d},{exec:d})},function(x,b,r){var u=r(8),d=r(14),h=r(68),p=r(409),y=r(410),T=r(34),$=r(71),A=r(51).get,E=r(411),R=r(412),I=T("native-string-replace",String.prototype.replace),O=RegExp.prototype.exec,C=O,D=d("".charAt),M=d("".indexOf),F=d("".replace),z=d("".slice),U=function(){var V=/a/,Y=/b*/g;return u(O,V,"a"),u(O,Y,"a"),V.lastIndex!==0||Y.lastIndex!==0}(),j=y.BROKEN_CARET,G=/()??/.exec("")[1]!==void 0,B=U||G||j||E||R;B&&(C=function(Y){var Z=this,J=A(Z),q=h(Y),nt=J.raw,rt,_,tt,et,lt,mt,gt;if(nt)return nt.lastIndex=Z.lastIndex,rt=u(C,nt,q),Z.lastIndex=nt.lastIndex,rt;var xt=J.groups,yt=j&&Z.sticky,Ut=u(p,Z),Dt=Z.source,Xt=0,Qt=q;if(yt&&(Ut=F(Ut,"y",""),M(Ut,"g")===-1&&(Ut+="g"),Qt=z(q,Z.lastIndex),Z.lastIndex>0&&(!Z.multiline||Z.multiline&&D(q,Z.lastIndex-1)!==` +`)&&(Dt="(?: "+Dt+")",Qt=" "+Qt,Xt++),_=new RegExp("^(?:"+Dt+")",Ut)),G&&(_=new RegExp("^"+Dt+"$(?!\\s)",Ut)),U&&(tt=Z.lastIndex),et=u(O,yt?_:Z,Qt),yt?et?(et.input=z(et.input,Xt),et[0]=z(et[0],Xt),et.index=Z.lastIndex,Z.lastIndex+=et[0].length):Z.lastIndex=0:U&&et&&(Z.lastIndex=Z.global?et.index+et[0].length:tt),G&&et&&et.length>1&&u(I,et[0],_,function(){for(lt=1;ltC.size?T(C.getIterator(),function(M){E(O,M)&&A(D,M)}):y(O,function(M){C.includes(M)&&A(D,M)}),D}},function(x,b,r){var u=r(3),d=r(437),h=r(433),p=!h("isDisjointFrom",function(y){return!y});u({target:"Set",proto:!0,real:!0,forced:p},{isDisjointFrom:d})},function(x,b,r){var u=r(426),d=r(427).has,h=r(431),p=r(432),y=r(429),T=r(430),$=r(135);x.exports=function(E){var R=u(this),I=p(E);if(h(R)<=I.size)return y(R,function(C){if(I.includes(C))return!1},!0)!==!1;var O=I.getIterator();return T(O,function(C){if(d(R,C))return $(O,"normal",!1)})!==!1}},function(x,b,r){var u=r(3),d=r(439),h=r(433),p=!h("isSubsetOf",function(y){return y});u({target:"Set",proto:!0,real:!0,forced:p},{isSubsetOf:d})},function(x,b,r){var u=r(426),d=r(431),h=r(429),p=r(432);x.exports=function(T){var $=u(this),A=p(T);return d($)>A.size?!1:h($,function(E){if(!A.includes(E))return!1},!0)!==!1}},function(x,b,r){var u=r(3),d=r(441),h=r(433),p=!h("isSupersetOf",function(y){return!y});u({target:"Set",proto:!0,real:!0,forced:p},{isSupersetOf:d})},function(x,b,r){var u=r(426),d=r(427).has,h=r(431),p=r(432),y=r(430),T=r(135);x.exports=function(A){var E=u(this),R=p(A);if(h(E)=0?C:O+C;return D<0||D>=O?void 0:$(I,D)}})},function(x,b,r){var u=r(3),d=r(448).codeAt;u({target:"String",proto:!0},{codePointAt:function(p){return d(this,p)}})},function(x,b,r){var u=r(14),d=r(61),h=r(68),p=r(16),y=u("".charAt),T=u("".charCodeAt),$=u("".slice),A=function(E){return function(R,I){var O=h(p(R)),C=d(I),D=O.length,M,F;return C<0||C>=D?E?"":void 0:(M=T(O,C),M<55296||M>56319||C+1===D||(F=T(O,C+1))<56320||F>57343?E?y(O,C):M:E?$(O,C,C+2):(M-55296<<10)+(F-56320)+65536)}};x.exports={codeAt:A(!1),charAt:A(!0)}},function(x,b,r){var u=r(3),d=r(85),h=r(5).f,p=r(64),y=r(68),T=r(450),$=r(16),A=r(451),E=r(36),R=d("".slice),I=Math.min,O=A("endsWith"),C=!E&&!O&&!!function(){var D=h(String.prototype,"endsWith");return D&&!D.writable}();u({target:"String",proto:!0,forced:!C&&!O},{endsWith:function(M){var F=y($(this));T(M);var z=arguments.length>1?arguments[1]:void 0,U=F.length,j=z===void 0?U:I(p(z),U),G=y(M);return R(F,j-G.length,j)===G}})},function(x,b,r){var u=r(407),d=TypeError;x.exports=function(h){if(u(h))throw new d("The method doesn't accept regular expressions");return h}},function(x,b,r){var u=r(33),d=u("match");x.exports=function(h){var p=/./;try{"/./"[h](p)}catch(y){try{return p[d]=!1,"/./"[h](p)}catch(T){}}return!1}},function(x,b,r){var u=r(3),d=r(14),h=r(60),p=RangeError,y=String.fromCharCode,T=String.fromCodePoint,$=d([].join),A=!!T&&T.length!==1;u({target:"String",stat:!0,arity:1,forced:A},{fromCodePoint:function(R){for(var I=[],O=arguments.length,C=0,D;O>C;){if(D=+arguments[C++],h(D,1114111)!==D)throw new p(D+" is not a valid code point");I[C]=D<65536?y(D):y(((D-=65536)>>10)+55296,D%1024+56320)}return $(I,"")}})},function(x,b,r){var u=r(3),d=r(14),h=r(450),p=r(16),y=r(68),T=r(451),$=d("".indexOf);u({target:"String",proto:!0,forced:!T("includes")},{includes:function(E){return!!~$(y(p(this)),y(h(E)),arguments.length>1?arguments[1]:void 0)}})},function(x,b,r){var u=r(3),d=r(14),h=r(16),p=r(68),y=d("".charCodeAt);u({target:"String",proto:!0},{isWellFormed:function(){for(var $=p(h(this)),A=$.length,E=0;E=56320||++E>=A||(y($,E)&64512)!==56320))return!1}return!0}})},function(x,b,r){var u=r(448).charAt,d=r(68),h=r(51),p=r(169),y=r(172),T="String Iterator",$=h.set,A=h.getterFor(T);p(String,"String",function(E){$(this,{type:T,string:d(E),index:0})},function(){var R=A(this),I=R.string,O=R.index,C;return O>=I.length?y(void 0,!0):(C=u(I,O),R.index+=C.length,y(C,!1))})},function(x,b,r){var u=r(8),d=r(457),h=r(46),p=r(17),y=r(64),T=r(68),$=r(16),A=r(29),E=r(458),R=r(459);d("match",function(I,O,C){return[function(M){var F=$(this),z=p(M)?void 0:A(M,I);return z?u(z,M,F):new RegExp(M)[I](T(F))},function(D){var M=h(this),F=T(D),z=C(O,M,F);if(z.done)return z.value;if(!M.global)return R(M,F);var U=M.unicode;M.lastIndex=0;for(var j=[],G=0,B;(B=R(M,F))!==null;){var V=T(B[0]);j[G]=V,V===""&&(M.lastIndex=E(F,y(M.lastIndex),U)),G++}return G===0?null:j}]})},function(x,b,r){r(416);var u=r(8),d=r(47),h=r(417),p=r(7),y=r(33),T=r(43),$=y("species"),A=RegExp.prototype;x.exports=function(E,R,I,O){var C=y(E),D=!p(function(){var U={};return U[C]=function(){return 7},""[E](U)!==7}),M=D&&!p(function(){var U=!1,j=/a/;return E==="split"&&(j={},j.constructor={},j.constructor[$]=function(){return j},j.flags="",j[C]=/./[C]),j.exec=function(){return U=!0,null},j[C](""),!U});if(!D||!M||I){var F=/./[C],z=R(C,""[E],function(U,j,G,B,V){var Y=j.exec;return Y===h||Y===A.exec?D&&!V?{done:!0,value:u(F,j,G,B)}:{done:!0,value:u(U,G,j,B)}:{done:!1}});d(String.prototype,E,z[0]),d(A,C,z[1])}O&&T(A[C],"sham",!0)}},function(x,b,r){var u=r(448).charAt;x.exports=function(d,h,p){return h+(p?u(d,h).length:1)}},function(x,b,r){var u=r(8),d=r(46),h=r(21),p=r(15),y=r(417),T=TypeError;x.exports=function($,A){var E=$.exec;if(h(E)){var R=u(E,$,A);return R!==null&&d(R),R}if(p($)==="RegExp")return u(y,$,A);throw new T("RegExp#exec called on incompatible receiver")}},function(x,b,r){var u=r(3),d=r(8),h=r(85),p=r(170),y=r(172),T=r(16),$=r(64),A=r(68),E=r(46),R=r(17),I=r(15),O=r(407),C=r(408),D=r(29),M=r(47),F=r(7),z=r(33),U=r(364),j=r(458),G=r(459),B=r(51),V=r(36),Y=z("matchAll"),Z="RegExp String",J=Z+" Iterator",q=B.set,nt=B.getterFor(J),rt=RegExp.prototype,_=TypeError,tt=h("".indexOf),et=h("".matchAll),lt=!!et&&!F(function(){et("a",/./)}),mt=p(function(yt,Ut,Dt,Xt){q(this,{type:J,regexp:yt,string:Ut,global:Dt,unicode:Xt,done:!1})},Z,function(){var yt=nt(this);if(yt.done)return y(void 0,!0);var Ut=yt.regexp,Dt=yt.string,Xt=G(Ut,Dt);return Xt===null?(yt.done=!0,y(void 0,!0)):yt.global?(A(Xt[0])===""&&(Ut.lastIndex=j(Dt,$(Ut.lastIndex),yt.unicode)),y(Xt,!1)):(yt.done=!0,y(Xt,!1))}),gt=function(xt){var yt=E(this),Ut=A(xt),Dt=U(yt,RegExp),Xt=A(C(yt)),Qt,kt,me;return Qt=new Dt(Dt===RegExp?yt.source:yt,Xt),kt=!!~tt(Xt,"g"),me=!!~tt(Xt,"u"),Qt.lastIndex=$(yt.lastIndex),new mt(Qt,Ut,kt,me)};u({target:"String",proto:!0,forced:lt},{matchAll:function(yt){var Ut=T(this),Dt,Xt,Qt,kt;if(R(yt)){if(lt)return et(Ut,yt)}else{if(O(yt)&&(Dt=A(T(C(yt))),!~tt(Dt,"g")))throw new _("`.matchAll` does not allow non-global regexes");if(lt)return et(Ut,yt);if(Qt=D(yt,Y),Qt===void 0&&V&&I(yt)==="RegExp"&&(Qt=gt),Qt)return d(Qt,yt,Ut)}return Xt=A(Ut),kt=new RegExp(yt,"g"),V?d(gt,kt,Xt):kt[Y](Xt)}}),V||Y in rt||M(rt,Y,gt)},function(x,b,r){var u=r(3),d=r(243).end,h=r(462);u({target:"String",proto:!0,forced:h},{padEnd:function(y){return d(this,y,arguments.length>1?arguments[1]:void 0)}})},function(x,b,r){var u=r(28);x.exports=/Version\/10(?:\.\d+){1,2}(?: [\w./]+)?(?: Mobile\/\w+)? Safari\//.test(u)},function(x,b,r){var u=r(3),d=r(243).start,h=r(462);u({target:"String",proto:!0,forced:h},{padStart:function(y){return d(this,y,arguments.length>1?arguments[1]:void 0)}})},function(x,b,r){var u=r(3),d=r(14),h=r(12),p=r(39),y=r(68),T=r(63),$=d([].push),A=d([].join);u({target:"String",stat:!0},{raw:function(R){var I=h(p(R).raw),O=T(I);if(!O)return"";for(var C=arguments.length,D=[],M=0;;){if($(D,y(I[M++])),M===O)return A(D,"");M")!=="7"});p("replace",function(_,tt,et){var lt=nt?"$":"$0";return[function(gt,xt){var yt=O(this),Ut=A(gt)?void 0:D(gt,U);return Ut?d(Ut,gt,yt,xt):d(tt,I(yt),gt,xt)},function(mt,gt){var xt=T(this),yt=I(mt);if(typeof gt=="string"&&Y(gt,lt)===-1&&Y(gt,"$<")===-1){var Ut=et(tt,xt,yt,gt);if(Ut.done)return Ut.value}var Dt=$(gt);Dt||(gt=I(gt));var Xt=xt.global,Qt;Xt&&(Qt=xt.unicode,xt.lastIndex=0);for(var kt=[],me;me=F(xt,yt),!(me===null||(V(kt,me),!Xt));){var ge=I(me[0]);ge===""&&(xt.lastIndex=C(yt,R(xt.lastIndex),Qt))}for(var ae="",Mt=0,Ht=0;Ht=Mt&&(ae+=Z(yt,Mt,se)+fe,Mt=se+re.length)}return ae+Z(yt,Mt)}]},!rt||!q||nt)},function(x,b,r){var u=r(14),d=r(39),h=Math.floor,p=u("".charAt),y=u("".replace),T=u("".slice),$=/\$([$&'`]|\d{1,2}|<[^>]*>)/g,A=/\$([$&'`]|\d{1,2})/g;x.exports=function(E,R,I,O,C,D){var M=I+E.length,F=O.length,z=A;return C!==void 0&&(C=d(C),z=$),y(D,z,function(U,j){var G;switch(p(j,0)){case"$":return"$";case"&":return E;case"`":return T(R,0,I);case"'":return T(R,M);case"<":G=C[T(j,1,-1)];break;default:var B=+j;if(B===0)return U;if(B>F){var V=h(B/10);return V===0?U:V<=F?O[V-1]===void 0?p(j,1):O[V-1]+p(j,1):U}G=O[B-1]}return G===void 0?"":G})}},function(x,b,r){var u=r(3),d=r(8),h=r(14),p=r(16),y=r(21),T=r(17),$=r(407),A=r(68),E=r(29),R=r(408),I=r(467),O=r(33),C=r(36),D=O("replace"),M=TypeError,F=h("".indexOf),z=h("".replace),U=h("".slice),j=Math.max;u({target:"String",proto:!0},{replaceAll:function(B,V){var Y=p(this),Z,J,q,nt,rt,_,tt,et,lt,mt,gt=0,xt="";if(!T(B)){if(Z=$(B),Z&&(J=A(p(R(B))),!~F(J,"g")))throw new M("`.replaceAll` does not allow non-global regexes");if(q=E(B,D),q)return d(q,B,Y,V);if(C&&Z)return z(A(Y),B,V)}for(nt=A(Y),rt=A(B),_=y(V),_||(V=A(V)),tt=rt.length,et=j(1,tt),lt=F(nt,rt);lt!==-1;)mt=_?A(V(rt,lt,nt)):I(rt,nt,lt,[],void 0,V),xt+=U(nt,gt,lt)+mt,gt=lt+tt,lt=lt+et>nt.length?-1:F(nt,rt,lt+et);return gt1||"".split(/.?/).length;h("split",function(V,Y,Z){var J="0".split(void 0,0).length?function(q,nt){return q===void 0&&nt===0?[]:u(Y,this,q,nt)}:Y;return[function(nt,rt){var _=T(this),tt=y(nt)?void 0:I(nt,V);return tt?u(tt,nt,_,rt):u(J,R(_),nt,rt)},function(q,nt){var rt=p(this),_=R(q);if(!B){var tt=Z(J,rt,_,nt,J!==Y);if(tt.done)return tt.value}var et=$(rt,RegExp),lt=rt.unicode,mt=(rt.ignoreCase?"i":"")+(rt.multiline?"m":"")+(rt.unicode?"u":"")+(M?"g":"y"),gt=new et(M?"^(?:"+rt.source+")":rt,mt),xt=nt===void 0?F:nt>>>0;if(xt===0)return[];if(_.length===0)return O(gt,_)===null?[_]:[];for(var yt=0,Ut=0,Dt=[];Ut<_.length;){gt.lastIndex=M?0:Ut;var Xt=O(gt,M?j(_,Ut):_),Qt;if(Xt===null||(Qt=z(E(gt.lastIndex+(M?Ut:0)),_.length))===yt)Ut=A(_,Ut,lt);else{if(U(Dt,j(_,yt,Ut)),Dt.length===xt)return Dt;for(var kt=1;kt<=Xt.length-1;kt++)if(U(Dt,Xt[kt]),Dt.length===xt)return Dt;Ut=yt=Qt}}return U(Dt,j(_,yt)),Dt}]},B||!G,M)},function(x,b,r){var u=r(3),d=r(85),h=r(5).f,p=r(64),y=r(68),T=r(450),$=r(16),A=r(451),E=r(36),R=d("".slice),I=Math.min,O=A("startsWith"),C=!E&&!O&&!!function(){var D=h(String.prototype,"startsWith");return D&&!D.writable}();u({target:"String",proto:!0,forced:!C&&!O},{startsWith:function(M){var F=y($(this));T(M);var z=p(I(arguments.length>1?arguments[1]:void 0,F.length)),U=y(M);return R(F,z,z+U.length)===U}})},function(x,b,r){var u=r(3),d=r(14),h=r(16),p=r(61),y=r(68),T=d("".slice),$=Math.max,A=Math.min,E=!"".substr||"ab".substr(-1)!=="b";u({target:"String",proto:!0,forced:E},{substr:function(I,O){var C=y(h(this)),D=C.length,M=p(I),F,z;return M===1/0&&(M=0),M<0&&(M=$(D+M,0)),F=O===void 0?D:p(O),F<=0||F===1/0?"":(z=A(M+F,D),M>=z?"":T(C,M,z))}})},function(x,b,r){var u=r(3),d=r(8),h=r(14),p=r(16),y=r(68),T=r(7),$=Array,A=h("".charAt),E=h("".charCodeAt),R=h([].join),I="".toWellFormed,O="\uFFFD",C=I&&T(function(){return d(I,1)!=="1"});u({target:"String",proto:!0,forced:C},{toWellFormed:function(){var M=y(p(this));if(C)return d(I,M);for(var F=M.length,z=$(F),U=0;U=56320||U+1>=F||(E(M,U+1)&64512)!==56320?z[U]=O:(z[U]=A(M,U),z[++U]=A(M,U))}return R(z,"")}})},function(x,b,r){var u=r(3),d=r(309).trim,h=r(475);u({target:"String",proto:!0,forced:h("trim")},{trim:function(){return d(this)}})},function(x,b,r){var u=r(49).PROPER,d=r(7),h=r(310),p="\u200B\x85\u180E";x.exports=function(y){return d(function(){return!!h[y]()||p[y]()!==p||u&&h[y].name!==y})}},function(x,b,r){r(477);var u=r(3),d=r(478);u({target:"String",proto:!0,name:"trimEnd",forced:"".trimEnd!==d},{trimEnd:d})},function(x,b,r){var u=r(3),d=r(478);u({target:"String",proto:!0,name:"trimEnd",forced:"".trimRight!==d},{trimRight:d})},function(x,b,r){var u=r(309).end,d=r(475);x.exports=d("trimEnd")?function(){return u(this)}:"".trimEnd},function(x,b,r){r(480);var u=r(3),d=r(481);u({target:"String",proto:!0,name:"trimStart",forced:"".trimStart!==d},{trimStart:d})},function(x,b,r){var u=r(3),d=r(481);u({target:"String",proto:!0,name:"trimStart",forced:"".trimLeft!==d},{trimLeft:d})},function(x,b,r){var u=r(309).start,d=r(475);x.exports=d("trimStart")?function(){return u(this)}:"".trimStart},function(x,b,r){var u=r(3),d=r(483),h=r(484);u({target:"String",proto:!0,forced:h("anchor")},{anchor:function(y){return d(this,"a","name",y)}})},function(x,b,r){var u=r(14),d=r(16),h=r(68),p=/"/g,y=u("".replace);x.exports=function(T,$,A,E){var R=h(d(T)),I="<"+$;return A!==""&&(I+=" "+A+'="'+y(h(E),p,""")+'"'),I+">"+R+""}},function(x,b,r){var u=r(7);x.exports=function(d){return u(function(){var h=""[d]('"');return h!==h.toLowerCase()||h.split('"').length>3})}},function(x,b,r){var u=r(3),d=r(483),h=r(484);u({target:"String",proto:!0,forced:h("big")},{big:function(){return d(this,"big","","")}})},function(x,b,r){var u=r(3),d=r(483),h=r(484);u({target:"String",proto:!0,forced:h("blink")},{blink:function(){return d(this,"blink","","")}})},function(x,b,r){var u=r(3),d=r(483),h=r(484);u({target:"String",proto:!0,forced:h("bold")},{bold:function(){return d(this,"b","","")}})},function(x,b,r){var u=r(3),d=r(483),h=r(484);u({target:"String",proto:!0,forced:h("fixed")},{fixed:function(){return d(this,"tt","","")}})},function(x,b,r){var u=r(3),d=r(483),h=r(484);u({target:"String",proto:!0,forced:h("fontcolor")},{fontcolor:function(y){return d(this,"font","color",y)}})},function(x,b,r){var u=r(3),d=r(483),h=r(484);u({target:"String",proto:!0,forced:h("fontsize")},{fontsize:function(y){return d(this,"font","size",y)}})},function(x,b,r){var u=r(3),d=r(483),h=r(484);u({target:"String",proto:!0,forced:h("italics")},{italics:function(){return d(this,"i","","")}})},function(x,b,r){var u=r(3),d=r(483),h=r(484);u({target:"String",proto:!0,forced:h("link")},{link:function(y){return d(this,"a","href",y)}})},function(x,b,r){var u=r(3),d=r(483),h=r(484);u({target:"String",proto:!0,forced:h("small")},{small:function(){return d(this,"small","","")}})},function(x,b,r){var u=r(3),d=r(483),h=r(484);u({target:"String",proto:!0,forced:h("strike")},{strike:function(){return d(this,"strike","","")}})},function(x,b,r){var u=r(3),d=r(483),h=r(484);u({target:"String",proto:!0,forced:h("sub")},{sub:function(){return d(this,"sub","","")}})},function(x,b,r){var u=r(3),d=r(483),h=r(484);u({target:"String",proto:!0,forced:h("sup")},{sup:function(){return d(this,"sup","","")}})},function(x,b,r){var u=r(498);u("Float32",function(d){return function(p,y,T){return d(this,p,y,T)}})},function(x,b,r){var u=r(3),d=r(4),h=r(8),p=r(6),y=r(499),T=r(219),$=r(208),A=r(211),E=r(11),R=r(43),I=r(315),O=r(64),C=r(212),D=r(500),M=r(501),F=r(18),z=r(38),U=r(69),j=r(20),G=r(22),B=r(71),V=r(24),Y=r(113),Z=r(57).f,J=r(502),q=r(83).forEach,nt=r(194),rt=r(77),_=r(44),tt=r(5),et=r(199),lt=r(51),mt=r(118),gt=lt.get,xt=lt.set,yt=lt.enforce,Ut=_.f,Dt=tt.f,Xt=d.RangeError,Qt=$.ArrayBuffer,kt=Qt.prototype,me=$.DataView,ge=T.NATIVE_ARRAY_BUFFER_VIEWS,ae=T.TYPED_ARRAY_TAG,Mt=T.TypedArray,Ht=T.TypedArrayPrototype,re=T.isTypedArray,se="BYTES_PER_ELEMENT",ee="Wrong length",fe=function(Ae,Te){rt(Ae,Te,{configurable:!0,get:function(){return gt(this)[Te]}})},Pe=function(Ae){var Te;return V(kt,Ae)||(Te=U(Ae))==="ArrayBuffer"||Te==="SharedArrayBuffer"},Me=function(Ae,Te){return re(Ae)&&!G(Te)&&Te in Ae&&I(+Te)&&Te>=0},$e=function(Te,de){return de=F(de),Me(Te,de)?E(2,Te[de]):Dt(Te,de)},ce=function(Te,de,bt){return de=F(de),Me(Te,de)&&j(bt)&&z(bt,"value")&&!z(bt,"get")&&!z(bt,"set")&&!bt.configurable&&(!z(bt,"writable")||bt.writable)&&(!z(bt,"enumerable")||bt.enumerable)?(Te[de]=bt.value,Te):Ut(Te,de,bt)};p?(ge||(tt.f=$e,_.f=ce,fe(Ht,"buffer"),fe(Ht,"byteOffset"),fe(Ht,"byteLength"),fe(Ht,"length")),u({target:"Object",stat:!0,forced:!ge},{getOwnPropertyDescriptor:$e,defineProperty:ce}),x.exports=function(Ae,Te,de){var bt=Ae.match(/\d+/)[0]/8,Ft=Ae+(de?"Clamped":"")+"Array",Tt="get"+Ae,qt="set"+Ae,te=d[Ft],Zt=te,Yt=Zt&&Zt.prototype,Ye={},Ze=function(Ct,Nt){var Et=gt(Ct);return Et.view[Tt](Nt*bt+Et.byteOffset,!0)},ut=function(Ct,Nt,Et){var ie=gt(Ct);ie.view[qt](Nt*bt+ie.byteOffset,de?M(Et):Et,!0)},It=function(Ct,Nt){Ut(Ct,Nt,{get:function(){return Ze(this,Nt)},set:function(Et){return ut(this,Nt,Et)},enumerable:!0})};ge?y&&(Zt=Te(function(Ct,Nt,Et,ie){return A(Ct,Yt),mt(function(){return j(Nt)?Pe(Nt)?ie!==void 0?new te(Nt,D(Et,bt),ie):Et!==void 0?new te(Nt,D(Et,bt)):new te(Nt):re(Nt)?et(Zt,Nt):h(J,Zt,Nt):new te(C(Nt))}(),Ct,Zt)}),Y&&Y(Zt,Mt),q(Z(te),function(Ct){Ct in Zt||R(Zt,Ct,te[Ct])}),Zt.prototype=Yt):(Zt=Te(function(Ct,Nt,Et,ie){A(Ct,Yt);var we=0,Rt=0,zt,jt,Wt;if(!j(Nt))Wt=C(Nt),jt=Wt*bt,zt=new Qt(jt);else if(Pe(Nt)){zt=Nt,Rt=D(Et,bt);var ue=Nt.byteLength;if(ie===void 0){if(ue%bt)throw new Xt(ee);if(jt=ue-Rt,jt<0)throw new Xt(ee)}else if(jt=O(ie)*bt,jt+Rt>ue)throw new Xt(ee);Wt=jt/bt}else return re(Nt)?et(Zt,Nt):h(J,Zt,Nt);for(xt(Ct,{buffer:zt,byteOffset:Rt,byteLength:jt,length:Wt,view:new me(zt)});we255?255:u&255}},function(x,b,r){var u=r(84),d=r(8),h=r(365),p=r(39),y=r(63),T=r(133),$=r(134),A=r(131),E=r(503),R=r(219).aTypedArrayConstructor,I=r(504);x.exports=function(C){var D=h(this),M=p(C),F=arguments.length,z=F>1?arguments[1]:void 0,U=z!==void 0,j=$(M),G,B,V,Y,Z,J,q,nt;if(j&&!A(j))for(q=T(M,j),nt=q.next,M=[];!(J=d(nt,q)).done;)M.push(J.value);for(U&&F>2&&(z=u(z,arguments[2])),B=y(M),V=new(R(D))(B),Y=E(V),G=0;B>G;G++)Z=U?z(M[G],G):M[G],V[G]=Y?I(Z):+Z;return V}},function(x,b,r){var u=r(69);x.exports=function(d){var h=u(d);return h==="BigInt64Array"||h==="BigUint64Array"}},function(x,b,r){var u=r(19),d=TypeError;x.exports=function(h){var p=u(h,"number");if(typeof p=="number")throw new d("Can't convert number to bigint");return BigInt(p)}},function(x,b,r){var u=r(498);u("Float64",function(d){return function(p,y,T){return d(this,p,y,T)}})},function(x,b,r){var u=r(498);u("Int8",function(d){return function(p,y,T){return d(this,p,y,T)}})},function(x,b,r){var u=r(498);u("Int16",function(d){return function(p,y,T){return d(this,p,y,T)}})},function(x,b,r){var u=r(498);u("Int32",function(d){return function(p,y,T){return d(this,p,y,T)}})},function(x,b,r){var u=r(498);u("Uint8",function(d){return function(p,y,T){return d(this,p,y,T)}})},function(x,b,r){var u=r(498);u("Uint8",function(d){return function(p,y,T){return d(this,p,y,T)}},!0)},function(x,b,r){var u=r(498);u("Uint16",function(d){return function(p,y,T){return d(this,p,y,T)}})},function(x,b,r){var u=r(498);u("Uint32",function(d){return function(p,y,T){return d(this,p,y,T)}})},function(x,b,r){var u=r(219),d=r(63),h=r(61),p=u.aTypedArray,y=u.exportTypedArrayMethod;y("at",function($){var A=p(this),E=d(A),R=h($),I=R>=0?R:E+R;return I<0||I>=E?void 0:A[I]})},function(x,b,r){var u=r(14),d=r(219),h=r(144),p=u(h),y=d.aTypedArray,T=d.exportTypedArrayMethod;T("copyWithin",function(A,E){return p(y(this),A,E,arguments.length>2?arguments[2]:void 0)})},function(x,b,r){var u=r(219),d=r(83).every,h=u.aTypedArray,p=u.exportTypedArrayMethod;p("every",function(T){return d(h(this),T,arguments.length>1?arguments[1]:void 0)})},function(x,b,r){var u=r(219),d=r(149),h=r(504),p=r(69),y=r(8),T=r(14),$=r(7),A=u.aTypedArray,E=u.exportTypedArrayMethod,R=T("".slice),I=$(function(){var O=0;return new Int8Array(2).fill({valueOf:function(){return O++}}),O!==1});E("fill",function(C){var D=arguments.length;A(this);var M=R(p(this),0,3)==="Big"?h(C):+C;return y(d,this,M,D>1?arguments[1]:void 0,D>2?arguments[2]:void 0)},I)},function(x,b,r){var u=r(219),d=r(83).filter,h=r(518),p=u.aTypedArray,y=u.exportTypedArrayMethod;y("filter",function($){var A=d(p(this),$,arguments.length>1?arguments[1]:void 0);return h(this,A)})},function(x,b,r){var u=r(199),d=r(219).getTypedArrayConstructor;x.exports=function(h,p){return u(d(h),p)}},function(x,b,r){var u=r(219),d=r(83).find,h=u.aTypedArray,p=u.exportTypedArrayMethod;p("find",function(T){return d(h(this),T,arguments.length>1?arguments[1]:void 0)})},function(x,b,r){var u=r(219),d=r(83).findIndex,h=u.aTypedArray,p=u.exportTypedArrayMethod;p("findIndex",function(T){return d(h(this),T,arguments.length>1?arguments[1]:void 0)})},function(x,b,r){var u=r(219),d=r(154).findLast,h=u.aTypedArray,p=u.exportTypedArrayMethod;p("findLast",function(T){return d(h(this),T,arguments.length>1?arguments[1]:void 0)})},function(x,b,r){var u=r(219),d=r(154).findLastIndex,h=u.aTypedArray,p=u.exportTypedArrayMethod;p("findLastIndex",function(T){return d(h(this),T,arguments.length>1?arguments[1]:void 0)})},function(x,b,r){var u=r(219),d=r(83).forEach,h=u.aTypedArray,p=u.exportTypedArrayMethod;p("forEach",function(T){d(h(this),T,arguments.length>1?arguments[1]:void 0)})},function(x,b,r){var u=r(499),d=r(219).exportTypedArrayStaticMethod,h=r(502);d("from",h,u)},function(x,b,r){var u=r(219),d=r(59).includes,h=u.aTypedArray,p=u.exportTypedArrayMethod;p("includes",function(T){return d(h(this),T,arguments.length>1?arguments[1]:void 0)})},function(x,b,r){var u=r(219),d=r(59).indexOf,h=u.aTypedArray,p=u.exportTypedArrayMethod;p("indexOf",function(T){return d(h(this),T,arguments.length>1?arguments[1]:void 0)})},function(x,b,r){var u=r(4),d=r(7),h=r(14),p=r(219),y=r(168),T=r(33),$=T("iterator"),A=u.Uint8Array,E=h(y.values),R=h(y.keys),I=h(y.entries),O=p.aTypedArray,C=p.exportTypedArrayMethod,D=A&&A.prototype,M=!d(function(){D[$].call([1])}),F=!!D&&D.values&&D[$]===D.values&&D.values.name==="values",z=function(){return E(O(this))};C("entries",function(){return I(O(this))},M),C("keys",function(){return R(O(this))},M),C("values",z,M||!F,{name:"values"}),C($,z,M||!F,{name:"values"})},function(x,b,r){var u=r(219),d=r(14),h=u.aTypedArray,p=u.exportTypedArrayMethod,y=d([].join);p("join",function($){return y(h(this),$)})},function(x,b,r){var u=r(219),d=r(94),h=r(175),p=u.aTypedArray,y=u.exportTypedArrayMethod;y("lastIndexOf",function($){var A=arguments.length;return d(h,p(this),A>1?[$,arguments[1]]:[$])})},function(x,b,r){var u=r(219),d=r(83).map,h=u.aTypedArray,p=u.getTypedArrayConstructor,y=u.exportTypedArrayMethod;y("map",function($){return d(h(this),$,arguments.length>1?arguments[1]:void 0,function(A,E){return new(p(A))(E)})})},function(x,b,r){var u=r(219),d=r(499),h=u.aTypedArrayConstructor,p=u.exportTypedArrayStaticMethod;p("of",function(){for(var T=0,$=arguments.length,A=new(h(this))($);$>T;)A[T]=arguments[T++];return A},d)},function(x,b,r){var u=r(219),d=r(181).left,h=u.aTypedArray,p=u.exportTypedArrayMethod;p("reduce",function(T){var $=arguments.length;return d(h(this),T,$,$>1?arguments[1]:void 0)})},function(x,b,r){var u=r(219),d=r(181).right,h=u.aTypedArray,p=u.exportTypedArrayMethod;p("reduceRight",function(T){var $=arguments.length;return d(h(this),T,$,$>1?arguments[1]:void 0)})},function(x,b,r){var u=r(219),d=u.aTypedArray,h=u.exportTypedArrayMethod,p=Math.floor;h("reverse",function(){for(var T=this,$=d(T).length,A=p($/2),E=0,R;E1?arguments[1]:void 0,1),j=T(z);if(D)return d(I,this,j,U);var G=this.length,B=p(j),V=0;if(B+U>G)throw new A("Wrong length");for(;VC;)M[C]=I[C++];return M},$)},function(x,b,r){var u=r(219),d=r(83).some,h=u.aTypedArray,p=u.exportTypedArrayMethod;p("some",function(T){return d(h(this),T,arguments.length>1?arguments[1]:void 0)})},function(x,b,r){var u=r(4),d=r(85),h=r(7),p=r(30),y=r(189),T=r(219),$=r(190),A=r(191),E=r(27),R=r(192),I=T.aTypedArray,O=T.exportTypedArrayMethod,C=u.Uint16Array,D=C&&d(C.prototype.sort),M=!!D&&!(h(function(){D(new C(2),null)})&&h(function(){D(new C(2),{})})),F=!!D&&!h(function(){if(E)return E<74;if($)return $<67;if(A)return!0;if(R)return R<602;var U=new C(516),j=Array(516),G,B;for(G=0;G<516;G++)B=G%4,U[G]=515-G,j[G]=G-2*B+3;for(D(U,function(V,Y){return(V/4|0)-(Y/4|0)}),G=0;G<516;G++)if(U[G]!==j[G])return!0}),z=function(U){return function(j,G){return U!==void 0?+U(j,G)||0:G!==G?-1:j!==j?1:j===0&&G===0?1/j>0&&1/G<0?1:-1:j>G}};O("sort",function(j){return j!==void 0&&p(j),F?D(this,j):y(I(this),z(j))},!F||M)},function(x,b,r){var u=r(219),d=r(64),h=r(60),p=u.aTypedArray,y=u.getTypedArrayConstructor,T=u.exportTypedArrayMethod;T("subarray",function(A,E){var R=p(this),I=R.length,O=h(A,I),C=y(R);return new C(R.buffer,R.byteOffset+O*R.BYTES_PER_ELEMENT,d((E===void 0?I:h(E,I))-O))})},function(x,b,r){var u=r(4),d=r(94),h=r(219),p=r(7),y=r(76),T=u.Int8Array,$=h.aTypedArray,A=h.exportTypedArrayMethod,E=[].toLocaleString,R=!!T&&p(function(){E.call(new T(1))}),I=p(function(){return[1,2].toLocaleString()!==new T([1,2]).toLocaleString()})||!p(function(){T.prototype.toLocaleString.call([1,2])});A("toLocaleString",function(){return d(E,R?y($(this)):$(this),y(arguments))},I)},function(x,b,r){var u=r(197),d=r(219),h=d.aTypedArray,p=d.exportTypedArrayMethod,y=d.getTypedArrayConstructor;p("toReversed",function(){return u(h(this),y(this))})},function(x,b,r){var u=r(219),d=r(14),h=r(30),p=r(199),y=u.aTypedArray,T=u.getTypedArrayConstructor,$=u.exportTypedArrayMethod,A=d(u.TypedArrayPrototype.sort);$("toSorted",function(R){R!==void 0&&h(R);var I=y(this),O=p(T(I),I);return A(O,R)})},function(x,b,r){var u=r(219).exportTypedArrayMethod,d=r(7),h=r(4),p=r(14),y=h.Uint8Array,T=y&&y.prototype||{},$=[].toString,A=p([].join);d(function(){$.call({})})&&($=function(){return A(this)});var E=T.toString!==$;u("toString",$,E)},function(x,b,r){var u=r(206),d=r(219),h=r(503),p=r(61),y=r(504),T=d.aTypedArray,$=d.getTypedArrayConstructor,A=d.exportTypedArrayMethod,E=!!function(){try{new Int8Array(1).with(2,{valueOf:function(){throw 8}})}catch(R){return R===8}}();A("with",function(R,I){var O=T(this),C=p(R),D=h(O)?y(I):+I;return u(O,$(O),C,D)},!E)},function(x,b,r){var u=r(3),d=r(14),h=r(68),p=String.fromCharCode,y=d("".charAt),T=d(/./.exec),$=d("".slice),A=/^[\da-f]{2}$/i,E=/^[\da-f]{4}$/i;u({global:!0},{unescape:function(I){for(var O=h(I),C="",D=O.length,M=0,F,z;M>(-2*_&6)));return nt}})},function(x){var b="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",r=b+"+/",u=b+"-_",d=function(h){for(var p={},y=0;y<64;y++)p[h.charAt(y)]=y;return p};x.exports={i2c:r,c2i:d(r),i2cUrl:u,c2iUrl:d(u)}},function(x,b,r){var u=r(3),d=r(4),h=r(23),p=r(14),y=r(8),T=r(7),$=r(68),A=r(367),E=r(552).i2c,R=h("btoa"),I=p("".charAt),O=p("".charCodeAt),C=!!R&&!T(function(){return R("hi")!=="aGk="}),D=C&&!T(function(){R()}),M=C&&T(function(){return R(null)!=="bnVsbA=="}),F=C&&R.length!==1;u({global:!0,bind:!0,enumerable:!0,forced:!C||D||M||F},{btoa:function(U){if(A(arguments.length,1),C)return y(R,d,$(U));for(var j=$(U),G="",B=0,V=E,Y,Z;I(j,B)||(V="=",B%1);){if(Z=O(j,B+=.75),Z>255)throw new(h("DOMException"))("The string contains characters outside of the Latin1 range","InvalidCharacterError");Y=Y<<8|Z,G+=I(V,63&Y>>8-B%1*8)}return G}})},function(x,b,r){var u=r(4),d=r(555),h=r(556),p=r(160),y=r(43),T=function(A){if(A&&A.forEach!==p)try{y(A,"forEach",p)}catch(E){A.forEach=p}};for(var $ in d)d[$]&&T(u[$]&&u[$].prototype);T(h)},function(x){x.exports={CSSRuleList:0,CSSStyleDeclaration:0,CSSValueList:0,ClientRectList:0,DOMRectList:0,DOMStringList:0,DOMTokenList:1,DataTransferItemList:0,FileList:0,HTMLAllCollection:0,HTMLCollection:0,HTMLFormElement:0,HTMLSelectElement:0,MediaList:0,MimeTypeArray:0,NamedNodeMap:0,NodeList:1,PaintRequestList:0,Plugin:0,PluginArray:0,SVGLengthList:0,SVGNumberList:0,SVGPathSegList:0,SVGPointList:0,SVGStringList:0,SVGTransformList:0,SourceBufferList:0,StyleSheetList:0,TextTrackCueList:0,TextTrackList:0,TouchList:0}},function(x,b,r){var u=r(42),d=u("span").classList,h=d&&d.constructor&&d.constructor.prototype;x.exports=h===Object.prototype?void 0:h},function(x,b,r){var u=r(4),d=r(555),h=r(556),p=r(168),y=r(43),T=r(82),$=r(33),A=$("iterator"),E=p.values,R=function(O,C){if(O){if(O[A]!==E)try{y(O,A,E)}catch(M){O[A]=E}if(T(O,C,!0),d[C]){for(var D in p)if(O[D]!==p[D])try{y(O,D,p[D])}catch(M){O[D]=p[D]}}}};for(var I in d)R(u[I]&&u[I].prototype,I);R(h,"DOMTokenList")},function(x,b,r){var u=r(3),d=r(23),h=r(234),p=r(7),y=r(71),T=r(11),$=r(44).f,A=r(47),E=r(77),R=r(38),I=r(211),O=r(46),C=r(125),D=r(119),M=r(559),F=r(122),z=r(51),U=r(6),j=r(36),G="DOMException",B="DATA_CLONE_ERR",V=d("Error"),Y=d(G)||function(){try{var Mt=d("MessageChannel")||h("worker_threads").MessageChannel;new Mt().port1.postMessage(new WeakMap)}catch(Ht){if(Ht.name===B&&Ht.code===25)return Ht.constructor}}(),Z=Y&&Y.prototype,J=V.prototype,q=z.set,nt=z.getterFor(G),rt="stack"in new V(G),_=function(Mt){return R(M,Mt)&&M[Mt].m?M[Mt].c:0},tt=function(){I(this,et);var Ht=arguments.length,re=D(Ht<1?void 0:arguments[0]),se=D(Ht<2?void 0:arguments[1],"Error"),ee=_(se);if(q(this,{type:G,name:se,message:re,code:ee}),U||(this.name=se,this.message=re,this.code=ee),rt){var fe=new V(re);fe.name=G,$(this,"stack",T(1,F(fe.stack,1)))}},et=tt.prototype=y(J),lt=function(Mt){return{enumerable:!0,configurable:!0,get:Mt}},mt=function(Mt){return lt(function(){return nt(this)[Mt]})};U&&(E(et,"code",mt("code")),E(et,"message",mt("message")),E(et,"name",mt("name"))),$(et,"constructor",T(1,tt));var gt=p(function(){return!(new Y instanceof V)}),xt=gt||p(function(){return J.toString!==C||String(new Y(1,2))!=="2: 1"}),yt=gt||p(function(){return new Y(1,"DataCloneError").code!==25}),Ut=gt||Y[B]!==25||Z[B]!==25,Dt=j?xt||yt||Ut:gt;u({global:!0,constructor:!0,forced:Dt},{DOMException:Dt?tt:Y});var Xt=d(G),Qt=Xt.prototype;xt&&(j||Y===Xt)&&A(Qt,"toString",C),yt&&U&&Y===Xt&&E(Qt,"code",lt(function(){return _(O(this).name)}));for(var kt in M)if(R(M,kt)){var me=M[kt],ge=me.s,ae=T(6,me.c);R(Xt,ge)||$(Xt,ge,ae),R(Qt,ge)||$(Qt,ge,ae)}},function(x){x.exports={IndexSizeError:{s:"INDEX_SIZE_ERR",c:1,m:1},DOMStringSizeError:{s:"DOMSTRING_SIZE_ERR",c:2,m:0},HierarchyRequestError:{s:"HIERARCHY_REQUEST_ERR",c:3,m:1},WrongDocumentError:{s:"WRONG_DOCUMENT_ERR",c:4,m:1},InvalidCharacterError:{s:"INVALID_CHARACTER_ERR",c:5,m:1},NoDataAllowedError:{s:"NO_DATA_ALLOWED_ERR",c:6,m:0},NoModificationAllowedError:{s:"NO_MODIFICATION_ALLOWED_ERR",c:7,m:1},NotFoundError:{s:"NOT_FOUND_ERR",c:8,m:1},NotSupportedError:{s:"NOT_SUPPORTED_ERR",c:9,m:1},InUseAttributeError:{s:"INUSE_ATTRIBUTE_ERR",c:10,m:1},InvalidStateError:{s:"INVALID_STATE_ERR",c:11,m:1},SyntaxError:{s:"SYNTAX_ERR",c:12,m:1},InvalidModificationError:{s:"INVALID_MODIFICATION_ERR",c:13,m:1},NamespaceError:{s:"NAMESPACE_ERR",c:14,m:1},InvalidAccessError:{s:"INVALID_ACCESS_ERR",c:15,m:1},ValidationError:{s:"VALIDATION_ERR",c:16,m:0},TypeMismatchError:{s:"TYPE_MISMATCH_ERR",c:17,m:1},SecurityError:{s:"SECURITY_ERR",c:18,m:1},NetworkError:{s:"NETWORK_ERR",c:19,m:1},AbortError:{s:"ABORT_ERR",c:20,m:1},URLMismatchError:{s:"URL_MISMATCH_ERR",c:21,m:1},QuotaExceededError:{s:"QUOTA_EXCEEDED_ERR",c:22,m:1},TimeoutError:{s:"TIMEOUT_ERR",c:23,m:1},InvalidNodeTypeError:{s:"INVALID_NODE_TYPE_ERR",c:24,m:1},DataCloneError:{s:"DATA_CLONE_ERR",c:25,m:1}}},function(x,b,r){var u=r(3),d=r(4),h=r(23),p=r(11),y=r(44).f,T=r(38),$=r(211),A=r(118),E=r(119),R=r(559),I=r(122),O=r(6),C=r(36),D="DOMException",M=h("Error"),F=h(D),z=function(){$(this,U);var tt=arguments.length,et=E(tt<1?void 0:arguments[0]),lt=E(tt<2?void 0:arguments[1],"Error"),mt=new F(et,lt),gt=new M(et);return gt.name=D,y(mt,"stack",p(1,I(gt.stack,1))),A(mt,this,z),mt},U=z.prototype=F.prototype,j="stack"in new M(D),G="stack"in new F(1,2),B=F&&O&&Object.getOwnPropertyDescriptor(d,D),V=!!B&&!(B.writable&&B.configurable),Y=j&&!V&&!G;u({global:!0,constructor:!0,forced:C||Y},{DOMException:Y?z:F});var Z=h(D),J=Z.prototype;if(J.constructor!==Z){C||y(J,"constructor",p(1,Z));for(var q in R)if(T(R,q)){var nt=R[q],rt=nt.s;T(Z,rt)||y(Z,rt,p(6,nt.c))}}},function(x,b,r){var u=r(23),d=r(82),h="DOMException";d(u(h),h)},function(x,b,r){r(563),r(564)},function(x,b,r){var u=r(3),d=r(4),h=r(366).clear;u({global:!0,bind:!0,enumerable:!0,forced:d.clearImmediate!==h},{clearImmediate:h})},function(x,b,r){var u=r(3),d=r(4),h=r(366).set,p=r(565),y=d.setImmediate?p(h,!1):h;u({global:!0,bind:!0,enumerable:!0,forced:d.setImmediate!==y},{setImmediate:y})},function(x,b,r){var u=r(4),d=r(94),h=r(21),p=r(183),y=r(28),T=r(76),$=r(367),A=u.Function,E=/MSIE .\./.test(y)||p==="BUN"&&function(){var R=u.Bun.version.split(".");return R.length<3||R[0]==="0"&&(R[1]<3||R[1]==="3"&&R[2]==="0")}();x.exports=function(R,I){var O=I?2:1;return E?function(C,D){var M=$(arguments.length,1)>O,F=h(C)?C:A(C),z=M?T(arguments,O):[],U=M?function(){d(F,this,z)}:F;return I?R(U,D):R(U)}:R}},function(x,b,r){var u=r(3),d=r(4),h=r(369),p=r(30),y=r(367),T=r(7),$=r(6),A=T(function(){return $&&Object.getOwnPropertyDescriptor(d,"queueMicrotask").value.length!==1});u({global:!0,enumerable:!0,dontCallGetSet:!0,forced:A},{queueMicrotask:function(R){y(arguments.length,1),h(p(R))}})},function(x,b,r){var u=r(3),d=r(4),h=r(77),p=r(6),y=TypeError,T=Object.defineProperty,$=d.self!==d;try{if(p){var A=Object.getOwnPropertyDescriptor(d,"self");($||!A||!A.get||!A.enumerable)&&h(d,"self",{get:function(){return d},set:function(R){if(this!==d)throw new y("Illegal invocation");T(d,"self",{value:R,writable:!0,configurable:!0,enumerable:!0})},configurable:!0,enumerable:!0})}else u({global:!0,simple:!0,forced:$},{self:d})}catch(E){}},function(x,b,r){var u=r(36),d=r(3),h=r(4),p=r(23),y=r(14),T=r(7),$=r(40),A=r(21),E=r(89),R=r(17),I=r(20),O=r(22),C=r(130),D=r(46),M=r(69),F=r(38),z=r(141),U=r(43),j=r(63),G=r(367),B=r(408),V=r(284),Y=r(427),Z=r(429),J=r(233),q=r(123),nt=r(235),rt=h.Object,_=h.Array,tt=h.Date,et=h.Error,lt=h.TypeError,mt=h.PerformanceMark,gt=p("DOMException"),xt=V.Map,yt=V.has,Ut=V.get,Dt=V.set,Xt=Y.Set,Qt=Y.add,kt=Y.has,me=p("Object","keys"),ge=y([].push),ae=y((!0).valueOf),Mt=y(1 .valueOf),Ht=y("".valueOf),re=y(tt.prototype.getTime),se=$("structuredClone"),ee="DataCloneError",fe="Transferring",Pe=function(ut){return!T(function(){var It=new h.Set([7]),Pt=ut(It),Ct=ut(rt(7));return Pt===It||!Pt.has(7)||!I(Ct)||+Ct!=7})&&ut},Me=function(ut,It){return!T(function(){var Pt=new It,Ct=ut({a:Pt,b:Pt});return!(Ct&&Ct.a===Ct.b&&Ct.a instanceof It&&Ct.a.stack===Pt.stack)})},$e=function(ut){return!T(function(){var It=ut(new h.AggregateError([1],se,{cause:3}));return It.name!=="AggregateError"||It.errors[0]!==1||It.message!==se||It.cause!==3})},ce=h.structuredClone,Ae=u||!Me(ce,et)||!Me(ce,gt)||!$e(ce),Te=!ce&&Pe(function(ut){return new mt(se,{detail:ut}).detail}),de=Pe(ce)||Te,bt=function(ut){throw new gt("Uncloneable type: "+ut,ee)},Ft=function(ut,It){throw new gt((It||"Cloning")+" of "+ut+" cannot be properly polyfilled in this engine",ee)},Tt=function(ut,It){return de||Ft(It),de(ut)},qt=function(){var ut;try{ut=new h.DataTransfer}catch(It){try{ut=new h.ClipboardEvent("").clipboardData}catch(Pt){}}return ut&&ut.items&&ut.files?ut:null},te=function(ut,It,Pt){if(yt(It,ut))return Ut(It,ut);var Ct=Pt||M(ut),Nt,Et,ie,we,Rt,zt;if(Ct==="SharedArrayBuffer")de?Nt=de(ut):Nt=ut;else{var jt=h.DataView;!jt&&!A(ut.slice)&&Ft("ArrayBuffer");try{if(A(ut.slice)&&!ut.resizable)Nt=ut.slice(0);else for(Et=ut.byteLength,ie=("maxByteLength"in ut)?{maxByteLength:ut.maxByteLength}:void 0,Nt=new ArrayBuffer(Et,ie),we=new jt(ut),Rt=new jt(Nt),zt=0;zt1&&!R(arguments[1])?D(arguments[1]):void 0,Ct=Pt?Pt.transfer:void 0,Nt,Et;Ct!==void 0&&(Nt=new xt,Et=Ye(Ct,Nt));var ie=Yt(It,Nt);return Et&&Ze(Et),ie}})},function(x,b,r){r(570),r(571)},function(x,b,r){var u=r(3),d=r(4),h=r(565),p=h(d.setInterval,!0);u({global:!0,bind:!0,forced:d.setInterval!==p},{setInterval:p})},function(x,b,r){var u=r(3),d=r(4),h=r(565),p=h(d.setTimeout,!0);u({global:!0,bind:!0,forced:d.setTimeout!==p},{setTimeout:p})},function(x,b,r){r(573)},function(x,b,r){r(455);var u=r(3),d=r(6),h=r(574),p=r(4),y=r(84),T=r(14),$=r(47),A=r(77),E=r(211),R=r(38),I=r(328),O=r(162),C=r(76),D=r(448).codeAt,M=r(575),F=r(68),z=r(82),U=r(367),j=r(576),G=r(51),B=G.set,V=G.getterFor("URL"),Y=j.URLSearchParams,Z=j.getState,J=p.URL,q=p.TypeError,nt=p.parseInt,rt=Math.floor,_=Math.pow,tt=T("".charAt),et=T(/./.exec),lt=T([].join),mt=T(1 .toString),gt=T([].pop),xt=T([].push),yt=T("".replace),Ut=T([].shift),Dt=T("".split),Xt=T("".slice),Qt=T("".toLowerCase),kt=T([].unshift),me="Invalid authority",ge="Invalid scheme",ae="Invalid host",Mt="Invalid port",Ht=/[a-z]/i,re=/[\d+-.a-z]/i,se=/\d/,ee=/^0x/i,fe=/^[0-7]+$/,Pe=/^\d+$/,Me=/^[\da-f]+$/i,$e=/[\0\t\n\r #%/:<>?@[\\\]^|]/,ce=/[\0\t\n\r #/:<>?@[\\\]^|]/,Ae=/^[\u0000-\u0020]+/,Te=/(^|[^\u0000-\u0020])[\u0000-\u0020]+$/,de=/[\t\n\r]/g,bt,Ft=function(ft){var wt=Dt(ft,"."),pt,it,Ot,ye,_t,Ie,rn;if(wt.length&&wt[wt.length-1]===""&&wt.length--,pt=wt.length,pt>4)return ft;for(it=[],Ot=0;Ot1&&tt(ye,0)==="0"&&(_t=et(ee,ye)?16:8,ye=Xt(ye,_t===8?1:2)),ye==="")Ie=0;else{if(!et(_t===10?Pe:_t===8?fe:Me,ye))return ft;Ie=nt(ye,_t)}xt(it,Ie)}for(Ot=0;Ot=_(256,5-pt))return null}else if(Ie>255)return null;for(rn=gt(it),Ot=0;Ot6))return;for(Ie=0;an();){if(rn=null,Ie>0)if(an()==="."&&Ie<4)Ot++;else return;if(!et(se,an()))return;for(;et(se,an());){if(hn=nt(an(),10),rn===null)rn=hn;else{if(rn===0)return;rn=rn*10+hn}if(rn>255)return;Ot++}wt[pt]=wt[pt]*256+rn,Ie++,(Ie===2||Ie===4)&&pt++}if(Ie!==4)return;break}else if(an()===":"){if(Ot++,!an())return}else if(an())return;wt[pt++]=ye}if(it!==null)for(pn=pt-it,pt=7;pt!==0&&pn>0;)ot=wt[pt],wt[pt--]=wt[it+pn-1],wt[it+--pn]=ot;else if(pt!==8)return;return wt},qt=function(ft){for(var wt=null,pt=1,it=null,Ot=0,ye=0;ye<8;ye++)ft[ye]!==0?(Ot>pt&&(wt=it,pt=Ot),it=null,Ot=0):(it===null&&(it=ye),++Ot);return Ot>pt?it:wt},te=function(ft){var wt,pt,it,Ot;if(typeof ft=="number"){for(wt=[],pt=0;pt<4;pt++)kt(wt,ft%256),ft=rt(ft/256);return lt(wt,".")}if(typeof ft=="object"){for(wt="",it=qt(ft),pt=0;pt<8;pt++)Ot&&ft[pt]===0||(Ot&&(Ot=!1),it===pt?(wt+=pt?":":"::",Ot=!0):(wt+=mt(ft[pt],16),pt<7&&(wt+=":")));return"["+wt+"]"}return ft},Zt={},Yt=I({},Zt,{" ":1,'"':1,"<":1,">":1,"`":1}),Ye=I({},Yt,{"#":1,"?":1,"{":1,"}":1}),Ze=I({},Ye,{"/":1,":":1,";":1,"=":1,"@":1,"[":1,"\\":1,"]":1,"^":1,"|":1}),ut=function(ft,wt){var pt=D(ft,0);return pt>32&&pt<127&&!R(wt,ft)?ft:encodeURIComponent(ft)},It={ftp:21,file:null,http:80,https:443,ws:80,wss:443},Pt=function(ft,wt){var pt;return ft.length===2&&et(Ht,tt(ft,0))&&((pt=tt(ft,1))===":"||!wt&&pt==="|")},Ct=function(ft){var wt;return ft.length>1&&Pt(Xt(ft,0,2))&&(ft.length===2||(wt=tt(ft,2))==="/"||wt==="\\"||wt==="?"||wt==="#")},Nt=function(ft){return ft==="."||Qt(ft)==="%2e"},Et=function(ft){return ft=Qt(ft),ft===".."||ft==="%2e."||ft===".%2e"||ft==="%2e%2e"},ie={},we={},Rt={},zt={},jt={},Wt={},ue={},Ee={},Xe={},Je={},nn={},vn={},jn={},Hr={},Ya={},ga={},yr={},Vn={},Wa={},ir={},Wn={},va=function(ft,wt,pt){var it=F(ft),Ot,ye,_t;if(wt){if(ye=this.parse(it),ye)throw new q(ye);this.searchParams=null}else{if(pt!==void 0&&(Ot=new va(pt,!0)),ye=this.parse(it,null,Ot),ye)throw new q(ye);_t=Z(new Y),_t.bindURL(this),this.searchParams=_t}};va.prototype={type:"URL",parse:function(ft,wt,pt){var it=this,Ot=wt||ie,ye=0,_t="",Ie=!1,rn=!1,hn=!1,pn,ot,an,Fn;for(ft=F(ft),wt||(it.scheme="",it.username="",it.password="",it.host=null,it.port=null,it.path=[],it.query=null,it.fragment=null,it.cannotBeABaseURL=!1,ft=yt(ft,Ae,""),ft=yt(ft,Te,"$1")),ft=yt(ft,de,""),pn=O(ft);ye<=pn.length;){switch(ot=pn[ye],Ot){case ie:if(ot&&et(Ht,ot))_t+=Qt(ot),Ot=we;else{if(wt)return ge;Ot=Rt;continue}break;case we:if(ot&&(et(re,ot)||ot==="+"||ot==="-"||ot==="."))_t+=Qt(ot);else if(ot===":"){if(wt&&(it.isSpecial()!==R(It,_t)||_t==="file"&&(it.includesCredentials()||it.port!==null)||it.scheme==="file"&&!it.host))return;if(it.scheme=_t,wt){it.isSpecial()&&It[it.scheme]===it.port&&(it.port=null);return}_t="",it.scheme==="file"?Ot=Hr:it.isSpecial()&&pt&&pt.scheme===it.scheme?Ot=zt:it.isSpecial()?Ot=Ee:pn[ye+1]==="/"?(Ot=jt,ye++):(it.cannotBeABaseURL=!0,xt(it.path,""),Ot=Wa)}else{if(wt)return ge;_t="",Ot=Rt,ye=0;continue}break;case Rt:if(!pt||pt.cannotBeABaseURL&&ot!=="#")return ge;if(pt.cannotBeABaseURL&&ot==="#"){it.scheme=pt.scheme,it.path=C(pt.path),it.query=pt.query,it.fragment="",it.cannotBeABaseURL=!0,Ot=Wn;break}Ot=pt.scheme==="file"?Hr:Wt;continue;case zt:if(ot==="/"&&pn[ye+1]==="/")Ot=Xe,ye++;else{Ot=Wt;continue}break;case jt:if(ot==="/"){Ot=Je;break}else{Ot=Vn;continue}case Wt:if(it.scheme=pt.scheme,ot===bt)it.username=pt.username,it.password=pt.password,it.host=pt.host,it.port=pt.port,it.path=C(pt.path),it.query=pt.query;else if(ot==="/"||ot==="\\"&&it.isSpecial())Ot=ue;else if(ot==="?")it.username=pt.username,it.password=pt.password,it.host=pt.host,it.port=pt.port,it.path=C(pt.path),it.query="",Ot=ir;else if(ot==="#")it.username=pt.username,it.password=pt.password,it.host=pt.host,it.port=pt.port,it.path=C(pt.path),it.query=pt.query,it.fragment="",Ot=Wn;else{it.username=pt.username,it.password=pt.password,it.host=pt.host,it.port=pt.port,it.path=C(pt.path),it.path.length--,Ot=Vn;continue}break;case ue:if(it.isSpecial()&&(ot==="/"||ot==="\\"))Ot=Xe;else if(ot==="/")Ot=Je;else{it.username=pt.username,it.password=pt.password,it.host=pt.host,it.port=pt.port,Ot=Vn;continue}break;case Ee:if(Ot=Xe,ot!=="/"||tt(_t,ye+1)!=="/")continue;ye++;break;case Xe:if(ot!=="/"&&ot!=="\\"){Ot=Je;continue}break;case Je:if(ot==="@"){Ie&&(_t="%40"+_t),Ie=!0,an=O(_t);for(var en=0;en65535)return Mt;it.port=it.isSpecial()&&Bn===It[it.scheme]?null:Bn,_t=""}if(wt)return;Ot=yr;continue}else return Mt;break;case Hr:if(it.scheme="file",ot==="/"||ot==="\\")Ot=Ya;else if(pt&&pt.scheme==="file")switch(ot){case bt:it.host=pt.host,it.path=C(pt.path),it.query=pt.query;break;case"?":it.host=pt.host,it.path=C(pt.path),it.query="",Ot=ir;break;case"#":it.host=pt.host,it.path=C(pt.path),it.query=pt.query,it.fragment="",Ot=Wn;break;default:Ct(lt(C(pn,ye),""))||(it.host=pt.host,it.path=C(pt.path),it.shortenPath()),Ot=Vn;continue}else{Ot=Vn;continue}break;case Ya:if(ot==="/"||ot==="\\"){Ot=ga;break}pt&&pt.scheme==="file"&&!Ct(lt(C(pn,ye),""))&&(Pt(pt.path[0],!0)?xt(it.path,pt.path[0]):it.host=pt.host),Ot=Vn;continue;case ga:if(ot===bt||ot==="/"||ot==="\\"||ot==="?"||ot==="#"){if(!wt&&Pt(_t))Ot=Vn;else if(_t===""){if(it.host="",wt)return;Ot=yr}else{if(Fn=it.parseHost(_t),Fn)return Fn;if(it.host==="localhost"&&(it.host=""),wt)return;_t="",Ot=yr}continue}else _t+=ot;break;case yr:if(it.isSpecial()){if(Ot=Vn,ot!=="/"&&ot!=="\\")continue}else if(!wt&&ot==="?")it.query="",Ot=ir;else if(!wt&&ot==="#")it.fragment="",Ot=Wn;else if(ot!==bt&&(Ot=Vn,ot!=="/"))continue;break;case Vn:if(ot===bt||ot==="/"||ot==="\\"&&it.isSpecial()||!wt&&(ot==="?"||ot==="#")){if(Et(_t)?(it.shortenPath(),ot!=="/"&&!(ot==="\\"&&it.isSpecial())&&xt(it.path,"")):Nt(_t)?ot!=="/"&&!(ot==="\\"&&it.isSpecial())&&xt(it.path,""):(it.scheme==="file"&&!it.path.length&&Pt(_t)&&(it.host&&(it.host=""),_t=tt(_t,0)+":"),xt(it.path,_t)),_t="",it.scheme==="file"&&(ot===bt||ot==="?"||ot==="#"))for(;it.path.length>1&&it.path[0]==="";)Ut(it.path);ot==="?"?(it.query="",Ot=ir):ot==="#"&&(it.fragment="",Ot=Wn)}else _t+=ut(ot,Ye);break;case Wa:ot==="?"?(it.query="",Ot=ir):ot==="#"?(it.fragment="",Ot=Wn):ot!==bt&&(it.path[0]+=ut(ot,Zt));break;case ir:!wt&&ot==="#"?(it.fragment="",Ot=Wn):ot!==bt&&(ot==="'"&&it.isSpecial()?it.query+="%27":ot==="#"?it.query+="%23":it.query+=ut(ot,Zt));break;case Wn:ot!==bt&&(it.fragment+=ut(ot,Yt));break}ye++}},parseHost:function(ft){var wt,pt,it;if(tt(ft,0)==="["){if(tt(ft,ft.length-1)!=="]"||(wt=Tt(Xt(ft,1,-1)),!wt))return ae;this.host=wt}else if(this.isSpecial()){if(ft=M(ft),et($e,ft)||(wt=Ft(ft),wt===null))return ae;this.host=wt}else{if(et(ce,ft))return ae;for(wt="",pt=O(ft),it=0;it1?arguments[1]:void 0,Ot=B(pt,new va(wt,!1,it));d||(pt.href=Ot.serialize(),pt.origin=Ot.getOrigin(),pt.protocol=Ot.getProtocol(),pt.username=Ot.getUsername(),pt.password=Ot.getPassword(),pt.host=Ot.getHost(),pt.hostname=Ot.getHostname(),pt.port=Ot.getPort(),pt.pathname=Ot.getPathname(),pt.search=Ot.getSearch(),pt.searchParams=Ot.getSearchParams(),pt.hash=Ot.getHash())},xn=xr.prototype,Mn=function(ft,wt){return{get:function(){return V(this)[ft]()},set:wt&&function(pt){return V(this)[wt](pt)},configurable:!0,enumerable:!0}};if(d&&(A(xn,"href",Mn("serialize","setHref")),A(xn,"origin",Mn("getOrigin")),A(xn,"protocol",Mn("getProtocol","setProtocol")),A(xn,"username",Mn("getUsername","setUsername")),A(xn,"password",Mn("getPassword","setPassword")),A(xn,"host",Mn("getHost","setHost")),A(xn,"hostname",Mn("getHostname","setHostname")),A(xn,"port",Mn("getPort","setPort")),A(xn,"pathname",Mn("getPathname","setPathname")),A(xn,"search",Mn("getSearch","setSearch")),A(xn,"searchParams",Mn("getSearchParams")),A(xn,"hash",Mn("getHash","setHash"))),$(xn,"toJSON",function(){return V(this).serialize()},{enumerable:!0}),$(xn,"toString",function(){return V(this).serialize()},{enumerable:!0}),J){var Ka=J.createObjectURL,Za=J.revokeObjectURL;Ka&&$(xr,"createObjectURL",y(Ka,J)),Za&&$(xr,"revokeObjectURL",y(Za,J))}z(xr,"URL"),u({global:!0,constructor:!0,forced:!h,sham:!d},{URL:xr})},function(x,b,r){var u=r(7),d=r(33),h=r(6),p=r(36),y=d("iterator");x.exports=!u(function(){var T=new URL("b?a=1&b=2&c=3","https://a"),$=T.searchParams,A=new URLSearchParams("a=1&a=2&b=3"),E="";return T.pathname="c%20d",$.forEach(function(R,I){$.delete("b"),E+=I+R}),A.delete("a",2),A.delete("b",void 0),p&&(!T.toJSON||!A.has("a",1)||A.has("a",2)||!A.has("a",void 0)||A.has("b"))||!$.size&&(p||!h)||!$.sort||T.href!=="https://a/c%20d?a=1&c=3"||$.get("c")!=="3"||String(new URLSearchParams("?a=1"))!=="a=1"||!$[y]||new URL("https://a@b").username!=="a"||new URLSearchParams(new URLSearchParams("a=b")).get("a")!=="b"||new URL("https://\u0442\u0435\u0441\u0442").host!=="xn--e1aybc"||new URL("https://a#\u0431").hash!=="#%D0%B1"||E!=="a1c3"||new URL("https://x",void 0).host!=="x"})},function(x,b,r){var u=r(14),d=2147483647,h=36,p=1,y=26,T=38,$=700,A=72,E=128,R="-",I=/[^\0-\u007E]/,O=/[.\u3002\uFF0E\uFF61]/g,C="Overflow: input needs wider integers to process",D=h-p,M=RangeError,F=u(O.exec),z=Math.floor,U=String.fromCharCode,j=u("".charCodeAt),G=u([].join),B=u([].push),V=u("".replace),Y=u("".split),Z=u("".toLowerCase),J=function(_){for(var tt=[],et=0,lt=_.length;et=55296&&mt<=56319&&et>1,_+=z(_/tt);_>D*y>>1;)_=z(_/D),lt+=h;return z(lt+(D+1)*_/(_+T))},rt=function(_){var tt=[];_=J(_);var et=_.length,lt=E,mt=0,gt=A,xt,yt;for(xt=0;xt<_.length;xt++)yt=_[xt],yt<128&&B(tt,U(yt));var Ut=tt.length,Dt=Ut;for(Ut&&B(tt,R);Dt=lt&&ytz((d-mt)/Qt))throw new M(C);for(mt+=(Xt-lt)*Qt,lt=Xt,xt=0;xt<_.length;xt++){if(yt=_[xt],ytd)throw new M(C);if(yt===lt){for(var kt=mt,me=h;;){var ge=me<=gt?p:me>=gt+y?y:me-gt;if(kt0&&(Rt&jt)!==0;jt>>=1)zt++;return zt},qt=function(Rt){var zt=null;switch(Rt.length){case 1:zt=Rt[0];break;case 2:zt=(Rt[0]&31)<<6|Rt[1]&63;break;case 3:zt=(Rt[0]&15)<<12|(Rt[1]&63)<<6|Rt[2]&63;break;case 4:zt=(Rt[0]&7)<<18|(Rt[1]&63)<<12|(Rt[2]&63)<<6|Rt[3]&63;break}return zt>1114111?null:zt},te=function(Rt){Rt=fe(Rt,Te," ");for(var zt=Rt.length,jt="",Wt=0;Wtzt){jt+="%",Wt++;continue}var Ee=Ft(Rt,Wt+1);if(Ee!==Ee){jt+=ue,Wt++;continue}Wt+=2;var Xe=Tt(Ee);if(Xe===0)ue=ae(Ee);else{if(Xe===1||Xe>4){jt+=de,Wt++;continue}for(var Je=[Ee],nn=1;nnzt||re(Rt,Wt)!=="%"));){var vn=Ft(Rt,Wt+1);if(vn!==vn){Wt+=3;break}if(vn>191||vn<128)break;ee(Je,vn),Wt+=2,nn++}if(Je.length!==Xe){jt+=de;continue}var jn=qt(Je);jn===null?jt+=de:ue=Mt(jn)}}jt+=ue,Wt++}return jt},Zt=/[!'()~]|%20/g,Yt={"!":"%21","'":"%27","(":"%28",")":"%29","~":"%7E","%20":"+"},Ye=function(Rt){return Yt[Rt]},Ze=function(Rt){return fe(ge(Rt),Zt,Ye)},ut=C(function(zt,jt){gt(this,{type:mt,target:xt(zt).entries,index:0,kind:jt})},lt,function(){var zt=yt(this),jt=zt.target,Wt=zt.index++;if(!jt||Wt>=jt.length)return zt.target=null,nt(void 0,!0);var ue=jt[Wt];switch(zt.kind){case"keys":return nt(ue.key,!1);case"values":return nt(ue.value,!1)}return nt([ue.key,ue.value],!1)},!0),It=function(Rt){this.entries=[],this.url=null,Rt!==void 0&&(B(Rt)?this.parseObject(Rt):this.parseQuery(typeof Rt=="string"?re(Rt,0)==="?"?ce(Rt,1):Rt:V(Rt)))};It.prototype={type:lt,bindURL:function(Rt){this.url=Rt,this.update()},parseObject:function(Rt){var zt=this.entries,jt=q(Rt),Wt,ue,Ee,Xe,Je,nn,vn;if(jt)for(Wt=J(Rt,jt),ue=Wt.next;!(Ee=y(ue,Wt)).done;){if(Xe=J(G(Ee.value)),Je=Xe.next,(nn=y(Je,Xe)).done||(vn=y(Je,Xe)).done||!y(Je,Xe).done)throw new me("Expected sequence with length 2");ee(zt,{key:V(nn.value),value:V(vn.value)})}else for(var jn in Rt)z(Rt,jn)&&ee(zt,{key:jn,value:V(Rt[jn])})},parseQuery:function(Rt){if(Rt)for(var zt=this.entries,jt=$e(Rt,"&"),Wt=0,ue,Ee;Wt0?arguments[0]:void 0,jt=gt(this,new It(zt));$||(this.size=jt.entries.length)},Ct=Pt.prototype;if(I(Ct,{append:function(zt,jt){var Wt=xt(this);rt(arguments.length,2),ee(Wt.entries,{key:V(zt),value:V(jt)}),$||this.length++,Wt.updateURL()},delete:function(Rt){for(var zt=xt(this),jt=rt(arguments.length,1),Wt=zt.entries,ue=V(Rt),Ee=jt<2?void 0:arguments[1],Xe=Ee===void 0?Ee:V(Ee),Je=0;JeWt.key?1:-1}),zt.updateURL()},forEach:function(zt){for(var jt=xt(this).entries,Wt=U(zt,arguments.length>1?arguments[1]:void 0),ue=0,Ee;ue1?ie(arguments[1]):{})}}),F(Dt)){var we=function(zt){return M(this,Qt),new Dt(zt,arguments.length>1?ie(arguments[1]):{})};Qt.constructor=we,we.prototype=Qt,u({global:!0,constructor:!0,dontCallGetSet:!0,forced:!0},{Request:we})}}x.exports={URLSearchParams:Pt,getState:xt}},function(x,b,r){var u=r(3),d=r(23),h=r(7),p=r(367),y=r(68),T=r(574),$=d("URL"),A=T&&h(function(){$.canParse()}),E=h(function(){return $.canParse.length!==1});u({target:"URL",stat:!0,forced:!A||E},{canParse:function(I){var O=p(arguments.length,1),C=y(I),D=O<2||arguments[1]===void 0?void 0:y(arguments[1]);try{return!!new $(C,D)}catch(M){return!1}}})},function(x,b,r){var u=r(3),d=r(23),h=r(367),p=r(68),y=r(574),T=d("URL");u({target:"URL",stat:!0,forced:!y},{parse:function(A){var E=h(arguments.length,1),R=p(A),I=E<2||arguments[1]===void 0?void 0:p(arguments[1]);try{return new T(R,I)}catch(O){return null}}})},function(x,b,r){var u=r(3),d=r(8);u({target:"URL",proto:!0,enumerable:!0},{toJSON:function(){return d(URL.prototype.toString,this)}})},function(x,b,r){r(576)},function(x,b,r){var u=r(47),d=r(14),h=r(68),p=r(367),y=URLSearchParams,T=y.prototype,$=d(T.append),A=d(T.delete),E=d(T.forEach),R=d([].push),I=new y("a=1&a=2&b=3");I.delete("a",1),I.delete("b",void 0),I+""!="a=2"&&u(T,"delete",function(O){var C=arguments.length,D=C<2?void 0:arguments[1];if(C&&D===void 0)return A(this,O);var M=[];E(this,function(Y,Z){R(M,{key:Z,value:Y})}),p(C,1);for(var F=h(O),z=h(D),U=0,j=0,G=!1,B=M.length,V;U=W&&(W=X+1);!(k=L[W])&&++W=0;)(s=a[i])&&(o&&s.compareDocumentPosition(o)^4&&o.parentNode.insertBefore(s,o),o=s);return this}function xt(t){t||(t=yt);function e(v,m){return v&&m?t(v.__data__,m.__data__):!v-!m}for(var n=this._groups,a=n.length,i=new Array(a),o=0;oe?1:t>=e?0:NaN}function Ut(){var t=arguments[0];return arguments[0]=this,t.apply(null,arguments),this}function Dt(){return Array.from(this)}function Xt(){for(var t=this._groups,e=0,n=t.length;e=0&&(e=t.slice(0,n))!=="xmlns"&&(t=t.slice(n+1)),ae.hasOwnProperty(e)?{space:ae[e],local:t}:t}function Ht(t){return function(){this.removeAttribute(t)}}function re(t){return function(){this.removeAttributeNS(t.space,t.local)}}function se(t,e){return function(){this.setAttribute(t,e)}}function ee(t,e){return function(){this.setAttributeNS(t.space,t.local,e)}}function fe(t,e){return function(){var n=e.apply(this,arguments);n==null?this.removeAttribute(t):this.setAttribute(t,n)}}function Pe(t,e){return function(){var n=e.apply(this,arguments);n==null?this.removeAttributeNS(t.space,t.local):this.setAttributeNS(t.space,t.local,n)}}function Me(t,e){var n=Mt(t);if(arguments.length<2){var a=this.node();return n.local?a.getAttributeNS(n.space,n.local):a.getAttribute(n)}return this.each((e==null?n.local?re:Ht:typeof e=="function"?n.local?Pe:fe:n.local?ee:se)(n,e))}function $e(t){return t.ownerDocument&&t.ownerDocument.defaultView||t.document&&t||t.defaultView}function ce(t){return function(){this.style.removeProperty(t)}}function Ae(t,e,n){return function(){this.style.setProperty(t,e,n)}}function Te(t,e,n){return function(){var a=e.apply(this,arguments);a==null?this.style.removeProperty(t):this.style.setProperty(t,a,n)}}function de(t,e,n){return arguments.length>1?this.each((e==null?ce:typeof e=="function"?Te:Ae)(t,e,n==null?"":n)):bt(this.node(),t)}function bt(t,e){return t.style.getPropertyValue(e)||$e(t).getComputedStyle(t,null).getPropertyValue(e)}function Ft(t){return function(){delete this[t]}}function Tt(t,e){return function(){this[t]=e}}function qt(t,e){return function(){var n=e.apply(this,arguments);n==null?delete this[t]:this[t]=n}}function te(t,e){return arguments.length>1?this.each((e==null?Ft:typeof e=="function"?qt:Tt)(t,e)):this.node()[t]}function Zt(t){return t.trim().split(/^|\s+/)}function Yt(t){return t.classList||new Ye(t)}function Ye(t){this._node=t,this._names=Zt(t.getAttribute("class")||"")}Ye.prototype={add:function(t){var e=this._names.indexOf(t);e<0&&(this._names.push(t),this._node.setAttribute("class",this._names.join(" ")))},remove:function(t){var e=this._names.indexOf(t);e>=0&&(this._names.splice(e,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};function Ze(t,e){for(var n=Yt(t),a=-1,i=e.length;++a=0&&(n=e.slice(a+1),e=e.slice(0,a)),{type:e,name:n}})}function Ka(t){return function(){var e=this.__on;if(e){for(var n=0,a=-1,i=e.length,o;n(t(o=new Date(+o)),o),i.ceil=o=>(t(o=new Date(o-1)),e(o,1),t(o),o),i.round=o=>{const s=i(o),l=i.ceil(o);return o-s(e(o=new Date(+o),s==null?1:Math.floor(s)),o),i.range=(o,s,l)=>{const c=[];if(o=i.ceil(o),l=l==null?1:Math.floor(l),!(o0))return c;let f;do c.push(f=new Date(+o)),e(o,l),t(o);while(fen(s=>{if(s>=s)for(;t(s),!o(s);)s.setTime(s-1)},(s,l)=>{if(s>=s)if(l<0)for(;++l<=0;)for(;e(s,-1),!o(s););else for(;--l>=0;)for(;e(s,1),!o(s););}),n&&(i.count=(o,s)=>(an.setTime(+o),Fn.setTime(+s),t(an),t(Fn),Math.floor(n(an,Fn))),i.every=o=>(o=Math.floor(o),!isFinite(o)||!(o>0)?null:o>1?i.filter(a?s=>a(s)%o===0:s=>i.count(0,s)%o===0):i)),i}const Gn=1e3,In=Gn*60,Bn=In*60,or=Bn*24,to=or*7,Ps=or*30,eo=or*365;function Rr(t){return en(e=>{e.setDate(e.getDate()-(e.getDay()+7-t)%7),e.setHours(0,0,0,0)},(e,n)=>{e.setDate(e.getDate()+n*7)},(e,n)=>(n-e-(n.getTimezoneOffset()-e.getTimezoneOffset())*In)/to)}const Ja=Rr(0),Qa=Rr(1),Rf=Rr(2),If=Rr(3),Yr=Rr(4),Of=Rr(5),Cf=Rr(6),I0=Ja.range,O0=Qa.range,C0=Rf.range,P0=If.range,w0=Yr.range,M0=Of.range,D0=Cf.range;function Ir(t){return en(e=>{e.setUTCDate(e.getUTCDate()-(e.getUTCDay()+7-t)%7),e.setUTCHours(0,0,0,0)},(e,n)=>{e.setUTCDate(e.getUTCDate()+n*7)},(e,n)=>(n-e)/to)}const ka=Ir(0),qa=Ir(1),Pf=Ir(2),wf=Ir(3),Wr=Ir(4),Mf=Ir(5),Df=Ir(6),L0=ka.range,N0=qa.range,F0=Pf.range,B0=wf.range,U0=Wr.range,z0=Mf.range,j0=Df.range,pa=en(t=>t.setHours(0,0,0,0),(t,e)=>t.setDate(t.getDate()+e),(t,e)=>(e-t-(e.getTimezoneOffset()-t.getTimezoneOffset())*In)/or,t=>t.getDate()-1),V0=pa.range,_a=en(t=>{t.setUTCHours(0,0,0,0)},(t,e)=>{t.setUTCDate(t.getUTCDate()+e)},(t,e)=>(e-t)/or,t=>t.getUTCDate()-1),G0=_a.range,ws=en(t=>{t.setUTCHours(0,0,0,0)},(t,e)=>{t.setUTCDate(t.getUTCDate()+e)},(t,e)=>(e-t)/or,t=>Math.floor(t/or)),X0=ws.range,sr=en(t=>{t.setMonth(0,1),t.setHours(0,0,0,0)},(t,e)=>{t.setFullYear(t.getFullYear()+e)},(t,e)=>e.getFullYear()-t.getFullYear(),t=>t.getFullYear());sr.every=t=>!isFinite(t=Math.floor(t))||!(t>0)?null:en(e=>{e.setFullYear(Math.floor(e.getFullYear()/t)*t),e.setMonth(0,1),e.setHours(0,0,0,0)},(e,n)=>{e.setFullYear(e.getFullYear()+n*t)});const H0=sr.range,lr=en(t=>{t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)},(t,e)=>{t.setUTCFullYear(t.getUTCFullYear()+e)},(t,e)=>e.getUTCFullYear()-t.getUTCFullYear(),t=>t.getUTCFullYear());lr.every=t=>!isFinite(t=Math.floor(t))||!(t>0)?null:en(e=>{e.setUTCFullYear(Math.floor(e.getUTCFullYear()/t)*t),e.setUTCMonth(0,1),e.setUTCHours(0,0,0,0)},(e,n)=>{e.setUTCFullYear(e.getUTCFullYear()+n*t)});const Y0=lr.range;function no(t){if(0<=t.y&&t.y<100){var e=new Date(-1,t.m,t.d,t.H,t.M,t.S,t.L);return e.setFullYear(t.y),e}return new Date(t.y,t.m,t.d,t.H,t.M,t.S,t.L)}function ro(t){if(0<=t.y&&t.y<100){var e=new Date(Date.UTC(-1,t.m,t.d,t.H,t.M,t.S,t.L));return e.setUTCFullYear(t.y),e}return new Date(Date.UTC(t.y,t.m,t.d,t.H,t.M,t.S,t.L))}function ma(t,e,n){return{y:t,m:e,d:n,H:0,M:0,S:0,L:0}}function Lf(t){var e=t.dateTime,n=t.date,a=t.time,i=t.periods,o=t.days,s=t.shortDays,l=t.months,c=t.shortMonths,f=ya(i),g=xa(i),v=ya(o),m=xa(o),S=ya(s),P=xa(s),N=ya(l),L=xa(l),w=ya(c),X=xa(c),W={a:At,A:Gt,b:Bt,B:Kt,c:null,d:Bs,e:Bs,f:rd,g:hd,G:vd,H:td,I:ed,j:nd,L:Us,m:ad,M:id,p:ne,q:le,Q:Hs,s:Ys,S:od,u:sd,U:ld,V:cd,w:ud,W:fd,x:null,X:null,y:dd,Y:gd,Z:pd,"%":Xs},H={a:be,A:Oe,b:Ce,B:He,c:null,d:js,e:js,f:Td,g:Pd,G:Md,H:md,I:yd,j:xd,L:Vs,m:$d,M:Sd,p:Fe,q:dn,Q:Hs,s:Ys,S:Ad,u:Ed,U:bd,V:Rd,w:Id,W:Od,x:null,X:null,y:Cd,Y:wd,Z:Dd,"%":Xs},k={a:dt,A:st,b:Vt,B:vt,c:Q,d:Ns,e:Ns,f:Qf,g:Ls,G:Ds,H:Fs,I:Fs,j:Wf,L:Jf,m:Yf,M:Kf,p:$t,q:Hf,Q:qf,s:_f,S:Zf,u:zf,U:jf,V:Vf,w:Uf,W:Gf,x:St,X:ct,y:Ls,Y:Ds,Z:Xf,"%":kf};W.x=K(n,W),W.X=K(a,W),W.c=K(e,W),H.x=K(n,H),H.X=K(a,H),H.c=K(e,H);function K(Jt,xe){return function(Re){var Lt=[],un=-1,Ge=0,Pn=Jt.length,wn,pe,fn;for(Re instanceof Date||(Re=new Date(+Re));++un53)return null;"w"in Lt||(Lt.w=1),"Z"in Lt?(Ge=ro(ma(Lt.y,0,1)),Pn=Ge.getUTCDay(),Ge=Pn>4||Pn===0?qa.ceil(Ge):qa(Ge),Ge=_a.offset(Ge,(Lt.V-1)*7),Lt.y=Ge.getUTCFullYear(),Lt.m=Ge.getUTCMonth(),Lt.d=Ge.getUTCDate()+(Lt.w+6)%7):(Ge=no(ma(Lt.y,0,1)),Pn=Ge.getDay(),Ge=Pn>4||Pn===0?Qa.ceil(Ge):Qa(Ge),Ge=pa.offset(Ge,(Lt.V-1)*7),Lt.y=Ge.getFullYear(),Lt.m=Ge.getMonth(),Lt.d=Ge.getDate()+(Lt.w+6)%7)}else("W"in Lt||"U"in Lt)&&("w"in Lt||(Lt.w="u"in Lt?Lt.u%7:"W"in Lt?1:0),Pn="Z"in Lt?ro(ma(Lt.y,0,1)).getUTCDay():no(ma(Lt.y,0,1)).getDay(),Lt.m=0,Lt.d="W"in Lt?(Lt.w+6)%7+Lt.W*7-(Pn+5)%7:Lt.w+Lt.U*7-(Pn+6)%7);return"Z"in Lt?(Lt.H+=Lt.Z/100|0,Lt.M+=Lt.Z%100,ro(Lt)):no(Lt)}}function ht(Jt,xe,Re,Lt){for(var un=0,Ge=xe.length,Pn=Re.length,wn,pe;un=Pn)return-1;if(wn=xe.charCodeAt(un++),wn===37){if(wn=xe.charAt(un++),pe=k[wn in Ms?xe.charAt(un++):wn],!pe||(Lt=pe(Jt,Re,Lt))<0)return-1}else if(wn!=Re.charCodeAt(Lt++))return-1}return Lt}function $t(Jt,xe,Re){var Lt=f.exec(xe.slice(Re));return Lt?(Jt.p=g.get(Lt[0].toLowerCase()),Re+Lt[0].length):-1}function dt(Jt,xe,Re){var Lt=S.exec(xe.slice(Re));return Lt?(Jt.w=P.get(Lt[0].toLowerCase()),Re+Lt[0].length):-1}function st(Jt,xe,Re){var Lt=v.exec(xe.slice(Re));return Lt?(Jt.w=m.get(Lt[0].toLowerCase()),Re+Lt[0].length):-1}function Vt(Jt,xe,Re){var Lt=w.exec(xe.slice(Re));return Lt?(Jt.m=X.get(Lt[0].toLowerCase()),Re+Lt[0].length):-1}function vt(Jt,xe,Re){var Lt=N.exec(xe.slice(Re));return Lt?(Jt.m=L.get(Lt[0].toLowerCase()),Re+Lt[0].length):-1}function Q(Jt,xe,Re){return ht(Jt,e,xe,Re)}function St(Jt,xe,Re){return ht(Jt,n,xe,Re)}function ct(Jt,xe,Re){return ht(Jt,a,xe,Re)}function At(Jt){return s[Jt.getDay()]}function Gt(Jt){return o[Jt.getDay()]}function Bt(Jt){return c[Jt.getMonth()]}function Kt(Jt){return l[Jt.getMonth()]}function ne(Jt){return i[+(Jt.getHours()>=12)]}function le(Jt){return 1+~~(Jt.getMonth()/3)}function be(Jt){return s[Jt.getUTCDay()]}function Oe(Jt){return o[Jt.getUTCDay()]}function Ce(Jt){return c[Jt.getUTCMonth()]}function He(Jt){return l[Jt.getUTCMonth()]}function Fe(Jt){return i[+(Jt.getUTCHours()>=12)]}function dn(Jt){return 1+~~(Jt.getUTCMonth()/3)}return{format:function(Jt){var xe=K(Jt+="",W);return xe.toString=function(){return Jt},xe},parse:function(Jt){var xe=at(Jt+="",!1);return xe.toString=function(){return Jt},xe},utcFormat:function(Jt){var xe=K(Jt+="",H);return xe.toString=function(){return Jt},xe},utcParse:function(Jt){var xe=at(Jt+="",!0);return xe.toString=function(){return Jt},xe}}}var Ms={"-":"",_:" ",0:"0"},mn=/^\s*\d+/,Nf=/^%/,Ff=/[\\^$*+?|[\]().{}]/g;function Ne(t,e,n){var a=t<0?"-":"",i=(a?-t:t)+"",o=i.length;return a+(o[e.toLowerCase(),n]))}function Uf(t,e,n){var a=mn.exec(e.slice(n,n+1));return a?(t.w=+a[0],n+a[0].length):-1}function zf(t,e,n){var a=mn.exec(e.slice(n,n+1));return a?(t.u=+a[0],n+a[0].length):-1}function jf(t,e,n){var a=mn.exec(e.slice(n,n+2));return a?(t.U=+a[0],n+a[0].length):-1}function Vf(t,e,n){var a=mn.exec(e.slice(n,n+2));return a?(t.V=+a[0],n+a[0].length):-1}function Gf(t,e,n){var a=mn.exec(e.slice(n,n+2));return a?(t.W=+a[0],n+a[0].length):-1}function Ds(t,e,n){var a=mn.exec(e.slice(n,n+4));return a?(t.y=+a[0],n+a[0].length):-1}function Ls(t,e,n){var a=mn.exec(e.slice(n,n+2));return a?(t.y=+a[0]+(+a[0]>68?1900:2e3),n+a[0].length):-1}function Xf(t,e,n){var a=/^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(e.slice(n,n+6));return a?(t.Z=a[1]?0:-(a[2]+(a[3]||"00")),n+a[0].length):-1}function Hf(t,e,n){var a=mn.exec(e.slice(n,n+1));return a?(t.q=a[0]*3-3,n+a[0].length):-1}function Yf(t,e,n){var a=mn.exec(e.slice(n,n+2));return a?(t.m=a[0]-1,n+a[0].length):-1}function Ns(t,e,n){var a=mn.exec(e.slice(n,n+2));return a?(t.d=+a[0],n+a[0].length):-1}function Wf(t,e,n){var a=mn.exec(e.slice(n,n+3));return a?(t.m=0,t.d=+a[0],n+a[0].length):-1}function Fs(t,e,n){var a=mn.exec(e.slice(n,n+2));return a?(t.H=+a[0],n+a[0].length):-1}function Kf(t,e,n){var a=mn.exec(e.slice(n,n+2));return a?(t.M=+a[0],n+a[0].length):-1}function Zf(t,e,n){var a=mn.exec(e.slice(n,n+2));return a?(t.S=+a[0],n+a[0].length):-1}function Jf(t,e,n){var a=mn.exec(e.slice(n,n+3));return a?(t.L=+a[0],n+a[0].length):-1}function Qf(t,e,n){var a=mn.exec(e.slice(n,n+6));return a?(t.L=Math.floor(a[0]/1e3),n+a[0].length):-1}function kf(t,e,n){var a=Nf.exec(e.slice(n,n+1));return a?n+a[0].length:-1}function qf(t,e,n){var a=mn.exec(e.slice(n));return a?(t.Q=+a[0],n+a[0].length):-1}function _f(t,e,n){var a=mn.exec(e.slice(n));return a?(t.s=+a[0],n+a[0].length):-1}function Bs(t,e){return Ne(t.getDate(),e,2)}function td(t,e){return Ne(t.getHours(),e,2)}function ed(t,e){return Ne(t.getHours()%12||12,e,2)}function nd(t,e){return Ne(1+pa.count(sr(t),t),e,3)}function Us(t,e){return Ne(t.getMilliseconds(),e,3)}function rd(t,e){return Us(t,e)+"000"}function ad(t,e){return Ne(t.getMonth()+1,e,2)}function id(t,e){return Ne(t.getMinutes(),e,2)}function od(t,e){return Ne(t.getSeconds(),e,2)}function sd(t){var e=t.getDay();return e===0?7:e}function ld(t,e){return Ne(Ja.count(sr(t)-1,t),e,2)}function zs(t){var e=t.getDay();return e>=4||e===0?Yr(t):Yr.ceil(t)}function cd(t,e){return t=zs(t),Ne(Yr.count(sr(t),t)+(sr(t).getDay()===4),e,2)}function ud(t){return t.getDay()}function fd(t,e){return Ne(Qa.count(sr(t)-1,t),e,2)}function dd(t,e){return Ne(t.getFullYear()%100,e,2)}function hd(t,e){return t=zs(t),Ne(t.getFullYear()%100,e,2)}function gd(t,e){return Ne(t.getFullYear()%1e4,e,4)}function vd(t,e){var n=t.getDay();return t=n>=4||n===0?Yr(t):Yr.ceil(t),Ne(t.getFullYear()%1e4,e,4)}function pd(t){var e=t.getTimezoneOffset();return(e>0?"-":(e*=-1,"+"))+Ne(e/60|0,"0",2)+Ne(e%60,"0",2)}function js(t,e){return Ne(t.getUTCDate(),e,2)}function md(t,e){return Ne(t.getUTCHours(),e,2)}function yd(t,e){return Ne(t.getUTCHours()%12||12,e,2)}function xd(t,e){return Ne(1+_a.count(lr(t),t),e,3)}function Vs(t,e){return Ne(t.getUTCMilliseconds(),e,3)}function Td(t,e){return Vs(t,e)+"000"}function $d(t,e){return Ne(t.getUTCMonth()+1,e,2)}function Sd(t,e){return Ne(t.getUTCMinutes(),e,2)}function Ad(t,e){return Ne(t.getUTCSeconds(),e,2)}function Ed(t){var e=t.getUTCDay();return e===0?7:e}function bd(t,e){return Ne(ka.count(lr(t)-1,t),e,2)}function Gs(t){var e=t.getUTCDay();return e>=4||e===0?Wr(t):Wr.ceil(t)}function Rd(t,e){return t=Gs(t),Ne(Wr.count(lr(t),t)+(lr(t).getUTCDay()===4),e,2)}function Id(t){return t.getUTCDay()}function Od(t,e){return Ne(qa.count(lr(t)-1,t),e,2)}function Cd(t,e){return Ne(t.getUTCFullYear()%100,e,2)}function Pd(t,e){return t=Gs(t),Ne(t.getUTCFullYear()%100,e,2)}function wd(t,e){return Ne(t.getUTCFullYear()%1e4,e,4)}function Md(t,e){var n=t.getUTCDay();return t=n>=4||n===0?Wr(t):Wr.ceil(t),Ne(t.getUTCFullYear()%1e4,e,4)}function Dd(){return"+0000"}function Xs(){return"%"}function Hs(t){return+t}function Ys(t){return Math.floor(+t/1e3)}var Kr,ao,Ws,io,Ks;Ld({dateTime:"%x, %X",date:"%-m/%-d/%Y",time:"%-I:%M:%S %p",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});function Ld(t){return Kr=Lf(t),ao=Kr.format,Ws=Kr.parse,io=Kr.utcFormat,Ks=Kr.utcParse,Kr}var Nd=Object.defineProperty,Zs=Object.getOwnPropertySymbols,Fd=Object.prototype.hasOwnProperty,Bd=Object.prototype.propertyIsEnumerable,Js=(t,e,n)=>e in t?Nd(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,ke=(t,e)=>{for(var n in e||(e={}))Fd.call(e,n)&&Js(t,n,e[n]);if(Zs)for(var n of Zs(e))Bd.call(e,n)&&Js(t,n,e[n]);return t};const Se={button:"bb-button",chart:"bb-chart",empty:"bb-empty",main:"bb-main",target:"bb-target",EXPANDED:"_expanded_"},Ve={arc:"bb-arc",arcLabelLine:"bb-arc-label-line",arcRange:"bb-arc-range",arcs:"bb-arcs",chartArc:"bb-chart-arc",chartArcs:"bb-chart-arcs",chartArcsBackground:"bb-chart-arcs-background",chartArcsTitle:"bb-chart-arcs-title",needle:"bb-needle"},ti={area:"bb-area",areas:"bb-areas"},Tn={axis:"bb-axis",axisX:"bb-axis-x",axisXLabel:"bb-axis-x-label",axisY:"bb-axis-y",axisY2:"bb-axis-y2",axisY2Label:"bb-axis-y2-label",axisYLabel:"bb-axis-y-label",axisXTooltip:"bb-axis-x-tooltip",axisYTooltip:"bb-axis-y-tooltip",axisY2Tooltip:"bb-axis-y2-tooltip"},Kn={bar:"bb-bar",bars:"bb-bars",chartBar:"bb-chart-bar",chartBars:"bb-chart-bars"},cr={candlestick:"bb-candlestick",candlesticks:"bb-candlesticks",chartCandlestick:"bb-chart-candlestick",chartCandlesticks:"bb-chart-candlesticks",valueDown:"bb-value-down",valueUp:"bb-value-up"},$n={chartCircles:"bb-chart-circles",circle:"bb-circle",circles:"bb-circles"},oo={colorPattern:"bb-color-pattern",colorScale:"bb-colorscale"},Or={dragarea:"bb-dragarea",INCLUDED:"_included_"},Ta={funnel:"bb-funnel",chartFunnel:"bb-chart-funnel",chartFunnels:"bb-chart-funnels",funnelBackground:"bb-funnel-background"},Un={chartArcsGaugeMax:"bb-chart-arcs-gauge-max",chartArcsGaugeMin:"bb-chart-arcs-gauge-min",chartArcsGaugeUnit:"bb-chart-arcs-gauge-unit",chartArcsGaugeTitle:"bb-chart-arcs-gauge-title",gaugeValue:"bb-gauge-value"},We={legend:"bb-legend",legendBackground:"bb-legend-background",legendItem:"bb-legend-item",legendItemEvent:"bb-legend-item-event",legendItemHidden:"bb-legend-item-hidden",legendItemPoint:"bb-legend-item-point",legendItemTile:"bb-legend-item-tile"},ur={chartLine:"bb-chart-line",chartLines:"bb-chart-lines",line:"bb-line",lines:"bb-lines"},Zn={eventRect:"bb-event-rect",eventRects:"bb-event-rects",eventRectsMultiple:"bb-event-rects-multiple",eventRectsSingle:"bb-event-rects-single"},qe={focused:"bb-focused",defocused:"bb-defocused",legendItemFocused:"bb-legend-item-focused",xgridFocus:"bb-xgrid-focus",ygridFocus:"bb-ygrid-focus"},on={grid:"bb-grid",gridLines:"bb-grid-lines",xgrid:"bb-xgrid",xgridLine:"bb-xgrid-line",xgridLines:"bb-xgrid-lines",xgrids:"bb-xgrids",ygrid:"bb-ygrid",ygridLine:"bb-ygrid-line",ygridLines:"bb-ygrid-lines",ygrids:"bb-ygrids"},Tr={level:"bb-level",levels:"bb-levels"},Qs={chartRadar:"bb-chart-radar",chartRadars:"bb-chart-radars"},$a={region:"bb-region",regions:"bb-regions"},tn={selectedCircle:"bb-selected-circle",selectedCircles:"bb-selected-circles",SELECTED:"_selected_"},sn={shape:"bb-shape",shapes:"bb-shapes"},ks={brush:"bb-brush",subchart:"bb-subchart"},On={chartText:"bb-chart-text",chartTexts:"bb-chart-texts",text:"bb-text",texts:"bb-texts",title:"bb-title",TextOverlapping:"text-overlapping"},ei={tooltip:"bb-tooltip",tooltipContainer:"bb-tooltip-container",tooltipName:"bb-tooltip-name"},qs={treemap:"bb-treemap",chartTreemap:"bb-chart-treemap",chartTreemaps:"bb-chart-treemaps"},so={buttonZoomReset:"bb-zoom-reset",zoomBrush:"bb-zoom-brush"};var Ue=ke(ke(ke(ke(ke(ke(ke(ke(ke(ke(ke(ke(ke(ke(ke(ke(ke(ke(ke(ke(ke(ke(ke(ke(ke({},Se),Ve),ti),Tn),Kn),cr),$n),oo),Or),Un),We),ur),Zn),qe),Ta),on),Qs),$a),tn),sn),ks),On),ei),qs),so),Ud={boost_useCssRule:!1,boost_useWorker:!1},zd={color_pattern:[],color_tiles:void 0,color_threshold:{},color_onover:void 0},jd={legend_contents_bindto:void 0,legend_contents_template:"{=TITLE}",legend_equally:!1,legend_hide:!1,legend_inset_anchor:"top-left",legend_inset_x:10,legend_inset_y:0,legend_inset_step:void 0,legend_item_interaction:!0,legend_item_dblclick:!1,legend_item_onclick:void 0,legend_item_onover:void 0,legend_item_onout:void 0,legend_item_tile_width:10,legend_item_tile_height:10,legend_item_tile_r:5,legend_item_tile_type:"rectangle",legend_format:void 0,legend_padding:0,legend_position:"bottom",legend_show:!0,legend_tooltip:!1,legend_usePoint:!1},Vd={bindto:"#chart",background:{},clipPath:!0,svg_classname:void 0,size_width:void 0,size_height:void 0,padding:!0,padding_mode:void 0,padding_left:void 0,padding_right:void 0,padding_top:void 0,padding_bottom:void 0,resize_auto:!0,resize_timer:!0,onclick:void 0,onover:void 0,onout:void 0,onresize:void 0,onresized:void 0,onbeforeinit:void 0,oninit:void 0,onafterinit:void 0,onrendered:void 0,transition_duration:250,plugins:[],render:{},regions:[]},Gd={title_text:void 0,title_padding:{top:0,right:0,bottom:0,left:0},title_position:"center"},Xd={tooltip_show:!0,tooltip_doNotHide:!1,tooltip_grouped:!0,tooltip_format_title:void 0,tooltip_format_name:void 0,tooltip_format_value:void 0,tooltip_position:void 0,tooltip_contents:{},tooltip_init_show:!1,tooltip_init_x:0,tooltip_init_position:void 0,tooltip_linked:!1,tooltip_linked_name:"",tooltip_onshow:()=>{},tooltip_onhide:()=>{},tooltip_onshown:()=>{},tooltip_onhidden:()=>{},tooltip_order:null},Hd={data_x:void 0,data_idConverter:t=>t,data_names:{},data_classes:{},data_type:void 0,data_types:{},data_order:"desc",data_groups:[],data_groupsZeroAs:"positive",data_color:void 0,data_colors:{},data_labels:{},data_labels_backgroundColors:void 0,data_labels_colors:void 0,data_labels_position:{},data_hide:!1,data_filter:void 0,data_onclick:()=>{},data_onover:()=>{},data_onout:()=>{},data_onshown:void 0,data_onhidden:void 0,data_onmin:void 0,data_onmax:void 0,data_url:void 0,data_headers:void 0,data_json:void 0,data_rows:void 0,data_columns:void 0,data_mimeType:"csv",data_keys:void 0,data_empty_label_text:""},Yd={interaction_enabled:!0,interaction_brighten:!0,interaction_inputType_mouse:!0,interaction_inputType_touch:{},interaction_onout:!0},Wd={value:()=>{}};function _s(){for(var t=0,e=arguments.length,n={},a;t=0&&(a=n.slice(i+1),n=n.slice(0,i)),n&&!e.hasOwnProperty(n))throw new Error("unknown type: "+n);return{type:n,name:a}})}ni.prototype=_s.prototype={constructor:ni,on:function(t,e){var n=this._,a=Kd(t+"",n),i,o=-1,s=a.length;if(arguments.length<2){for(;++o0)for(var n=new Array(i),a=0,i,o;a>8&15|e>>4&240,e>>4&15|e&240,(e&15)<<4|e&15,1):n===8?ii(e>>24&255,e>>16&255,e>>8&255,(e&255)/255):n===4?ii(e>>12&15|e>>8&240,e>>8&15|e>>4&240,e>>4&15|e&240,((e&15)<<4|e&15)/255):null):(e=kd.exec(t))?new Dn(e[1],e[2],e[3],1):(e=qd.exec(t))?new Dn(e[1]*255/100,e[2]*255/100,e[3]*255/100,1):(e=_d.exec(t))?ii(e[1],e[2],e[3],e[4]):(e=th.exec(t))?ii(e[1]*255/100,e[2]*255/100,e[3]*255/100,e[4]):(e=eh.exec(t))?ll(e[1],e[2]/100,e[3]/100,1):(e=nh.exec(t))?ll(e[1],e[2]/100,e[3]/100,e[4]):nl.hasOwnProperty(t)?il(nl[t]):t==="transparent"?new Dn(NaN,NaN,NaN,0):null}function il(t){return new Dn(t>>16&255,t>>8&255,t&255,1)}function ii(t,e,n,a){return a<=0&&(t=e=n=NaN),new Dn(t,e,n,a)}function ih(t){return t instanceof Aa||(t=Cr(t)),t?(t=t.rgb(),new Dn(t.r,t.g,t.b,t.opacity)):new Dn}function oi(t,e,n,a){return arguments.length===1?ih(t):new Dn(t,e,n,a==null?1:a)}function Dn(t,e,n,a){this.r=+t,this.g=+e,this.b=+n,this.opacity=+a}fo(Dn,oi,el(Aa,{brighter(t){return t=t==null?ai:Math.pow(ai,t),new Dn(this.r*t,this.g*t,this.b*t,this.opacity)},darker(t){return t=t==null?Ea:Math.pow(Ea,t),new Dn(this.r*t,this.g*t,this.b*t,this.opacity)},rgb(){return this},clamp(){return new Dn(Pr(this.r),Pr(this.g),Pr(this.b),si(this.opacity))},displayable(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:ol,formatHex:ol,formatHex8:oh,formatRgb:sl,toString:sl}));function ol(){return`#${wr(this.r)}${wr(this.g)}${wr(this.b)}`}function oh(){return`#${wr(this.r)}${wr(this.g)}${wr(this.b)}${wr((isNaN(this.opacity)?1:this.opacity)*255)}`}function sl(){const t=si(this.opacity);return`${t===1?"rgb(":"rgba("}${Pr(this.r)}, ${Pr(this.g)}, ${Pr(this.b)}${t===1?")":`, ${t})`}`}function si(t){return isNaN(t)?1:Math.max(0,Math.min(1,t))}function Pr(t){return Math.max(0,Math.min(255,Math.round(t)||0))}function wr(t){return t=Pr(t),(t<16?"0":"")+t.toString(16)}function ll(t,e,n,a){return a<=0?t=e=n=NaN:n<=0||n>=1?t=e=NaN:e<=0&&(t=NaN),new Jn(t,e,n,a)}function cl(t){if(t instanceof Jn)return new Jn(t.h,t.s,t.l,t.opacity);if(t instanceof Aa||(t=Cr(t)),!t)return new Jn;if(t instanceof Jn)return t;t=t.rgb();var e=t.r/255,n=t.g/255,a=t.b/255,i=Math.min(e,n,a),o=Math.max(e,n,a),s=NaN,l=o-i,c=(o+i)/2;return l?(e===o?s=(n-a)/l+(n0&&c<1?0:s,new Jn(s,l,c,t.opacity)}function sh(t,e,n,a){return arguments.length===1?cl(t):new Jn(t,e,n,a==null?1:a)}function Jn(t,e,n,a){this.h=+t,this.s=+e,this.l=+n,this.opacity=+a}fo(Jn,sh,el(Aa,{brighter(t){return t=t==null?ai:Math.pow(ai,t),new Jn(this.h,this.s,this.l*t,this.opacity)},darker(t){return t=t==null?Ea:Math.pow(Ea,t),new Jn(this.h,this.s,this.l*t,this.opacity)},rgb(){var t=this.h%360+(this.h<0)*360,e=isNaN(t)||isNaN(this.s)?0:this.s,n=this.l,a=n+(n<.5?n:1-n)*e,i=2*n-a;return new Dn(ho(t>=240?t-240:t+120,i,a),ho(t,i,a),ho(t<120?t+240:t-120,i,a),this.opacity)},clamp(){return new Jn(ul(this.h),li(this.s),li(this.l),si(this.opacity))},displayable(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl(){const t=si(this.opacity);return`${t===1?"hsl(":"hsla("}${ul(this.h)}, ${li(this.s)*100}%, ${li(this.l)*100}%${t===1?")":`, ${t})`}`}}));function ul(t){return t=(t||0)%360,t<0?t+360:t}function li(t){return Math.max(0,Math.min(1,t||0))}function ho(t,e,n){return(t<60?e+(n-e)*t/60:t<180?n:t<240?e+(n-e)*(240-t)/60:e)*255}function fl(t,e,n,a,i){var o=t*t,s=o*t;return((1-3*t+3*o-s)*e+(4-6*o+3*s)*n+(1+3*t+3*o-3*s)*a+s*i)/6}function lh(t){var e=t.length-1;return function(n){var a=n<=0?n=0:n>=1?(n=1,e-1):Math.floor(n*e),i=t[a],o=t[a+1],s=a>0?t[a-1]:2*i-o,l=a()=>t;function dl(t,e){return function(n){return t+n*e}}function uh(t,e,n){return t=Math.pow(t,n),e=Math.pow(e,n)-t,n=1/n,function(a){return Math.pow(t+a*e,n)}}function W0(t,e){var n=e-t;return n?dl(t,n>180||n<-180?n-360*Math.round(n/360):n):ci(isNaN(t)?e:t)}function fh(t){return(t=+t)==1?hl:function(e,n){return n-e?uh(e,n,t):ci(isNaN(e)?n:e)}}function hl(t,e){var n=e-t;return n?dl(t,n):ci(isNaN(t)?e:t)}var ui=function t(e){var n=fh(e);function a(i,o){var s=n((i=oi(i)).r,(o=oi(o)).r),l=n(i.g,o.g),c=n(i.b,o.b),f=hl(i.opacity,o.opacity);return function(g){return i.r=s(g),i.g=l(g),i.b=c(g),i.opacity=f(g),i+""}}return a.gamma=t,a}(1);function gl(t){return function(e){var n=e.length,a=new Array(n),i=new Array(n),o=new Array(n),s,l;for(s=0;sn&&(o=e.slice(n,o),l[s]?l[s]+=o:l[++s]=o),(a=a[0])===(i=i[0])?l[s]?l[s]+=i:l[++s]=i:(l[++s]=null,c.push({i:s,x:Qn(a,i)})),n=vo.lastIndex;return n=0&&t._call.call(void 0,e),t=t._next;--kr}function Sl(){Mr=(di=Ca.now())+hi,kr=Ra=0;try{yh()}finally{kr=0,Th(),Mr=0}}function xh(){var t=Ca.now(),e=t-di;e>xl&&(hi-=e,di=t)}function Th(){for(var t,e=fi,n,a=1/0;e;)e._call?(a>e._time&&(a=e._time),t=e,e=e._next):(n=e._next,e._next=null,e=t?t._next=n:fi=n);Oa=t,mo(a)}function mo(t){if(!kr){Ra&&(Ra=clearTimeout(Ra));var e=t-Mr;e>24?(t<1/0&&(Ra=setTimeout(Sl,t-Ca.now()-hi)),Ia&&(Ia=clearInterval(Ia))):(Ia||(di=Ca.now(),Ia=setInterval(xh,xl)),kr=1,Tl(Sl))}}function Al(t,e,n){var a=new gi;return e=e==null?0:+e,a.restart(i=>{a.stop(),t(i+e)},e,n),a}var $h=ri("start","end","cancel","interrupt"),Sh=[],El=0,bl=1,yo=2,vi=3,Rl=4,xo=5,pi=6;function mi(t,e,n,a,i,o){var s=t.__transition;if(!s)t.__transition={};else if(n in s)return;Ah(t,n,{name:e,index:a,group:i,on:$h,tween:Sh,time:o.time,delay:o.delay,duration:o.duration,ease:o.ease,timer:null,state:El})}function To(t,e){var n=kn(t,e);if(n.state>El)throw new Error("too late; already scheduled");return n}function er(t,e){var n=kn(t,e);if(n.state>vi)throw new Error("too late; already running");return n}function kn(t,e){var n=t.__transition;if(!n||!(n=n[e]))throw new Error("transition not found");return n}function Ah(t,e,n){var a=t.__transition,i;a[e]=n,n.timer=$l(o,0,n.time);function o(f){n.state=bl,n.timer.restart(s,n.delay,n.time),n.delay<=f&&s(f-n.delay)}function s(f){var g,v,m,S;if(n.state!==bl)return c();for(g in a)if(S=a[g],S.name===n.name){if(S.state===vi)return Al(s);S.state===Rl?(S.state=pi,S.timer.stop(),S.on.call("interrupt",t,t.__data__,S.index,S.group),delete a[g]):+gyo&&a.state180?g+=360:g-f>180&&(f+=360),m.push({i:v.push(i(v)+"rotate(",null,a)-2,x:Qn(f,g)})):g&&v.push(i(v)+"rotate("+g+a)}function l(f,g,v,m){f!==g?m.push({i:v.push(i(v)+"skewX(",null,a)-2,x:Qn(f,g)}):g&&v.push(i(v)+"skewX("+g+a)}function c(f,g,v,m,S,P){if(f!==v||g!==m){var N=S.push(i(S)+"scale(",null,",",null,")");P.push({i:N-4,x:Qn(f,v)},{i:N-2,x:Qn(g,m)})}else(v!==1||m!==1)&&S.push(i(S)+"scale("+v+","+m+")")}return function(f,g){var v=[],m=[];return f=t(f),g=t(g),o(f.translateX,f.translateY,g.translateX,g.translateY,v,m),s(f.rotate,g.rotate,v,m),l(f.skewX,g.skewX,v,m),c(f.scaleX,f.scaleY,g.scaleX,g.scaleY,v,m),f=g=null,function(S){for(var P=-1,N=m.length,L;++P=0&&(e=e.slice(0,n)),!e||e==="start"})}function rg(t,e,n){var a,i,o=ng(e)?To:er;return function(){var s=o(this,t),l=s.on;l!==a&&(i=(a=l).copy()).on(e,n),s.on=i}}function ag(t,e){var n=this._id;return arguments.length<2?kn(this.node(),n).on.on(t):this.each(rg(n,t,e))}function ig(t){return function(){var e=this.parentNode;for(var n in this.__transition)if(+n!==t)return;e&&e.removeChild(this)}}function og(){return this.on("end.remove",ig(this._id))}function sg(t){var e=this._name,n=this._id;typeof t!="function"&&(t=p(t));for(var a=this._groups,i=a.length,o=new Array(i),s=0;s()=>t;function Mg(t,{sourceEvent:e,target:n,selection:a,mode:i,dispatch:o}){Object.defineProperties(this,{type:{value:t,enumerable:!0,configurable:!0},sourceEvent:{value:e,enumerable:!0,configurable:!0},target:{value:n,enumerable:!0,configurable:!0},selection:{value:a,enumerable:!0,configurable:!0},mode:{value:i,enumerable:!0,configurable:!0},_:{value:o}})}function Dg(t){t.stopImmediatePropagation()}function Eo(t){t.preventDefault(),t.stopImmediatePropagation()}var Ll={name:"drag"},bo={name:"space"},_r={name:"handle"},ta={name:"center"};const{abs:Nl,max:Sn,min:An}=Math;function Fl(t){return[+t[0],+t[1]]}function Ro(t){return[Fl(t[0]),Fl(t[1])]}var xi={name:"x",handles:["w","e"].map(Pa),input:function(t,e){return t==null?null:[[+t[0],e[0][1]],[+t[1],e[1][1]]]},output:function(t){return t&&[t[0][0],t[1][0]]}},Ti={name:"y",handles:["n","s"].map(Pa),input:function(t,e){return t==null?null:[[e[0][0],+t[0]],[e[1][0],+t[1]]]},output:function(t){return t&&[t[0][1],t[1][1]]}},Lg={name:"xy",handles:["n","w","e","s","nw","ne","sw","se"].map(Pa),input:function(t){return t==null?null:Ro(t)},output:function(t){return t}},hr={overlay:"crosshair",selection:"move",n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},Bl={e:"w",w:"e",nw:"ne",ne:"nw",se:"sw",sw:"se"},Ul={n:"s",s:"n",nw:"sw",ne:"se",se:"ne",sw:"nw"},Ng={overlay:1,selection:1,n:null,e:1,s:null,w:-1,nw:-1,ne:1,se:1,sw:-1},Fg={overlay:1,selection:1,n:-1,e:null,s:1,w:null,nw:-1,ne:-1,se:1,sw:1};function Pa(t){return{type:t}}function Bg(t){return!t.ctrlKey&&!t.button}function Ug(){var t=this.ownerSVGElement||this;return t.hasAttribute("viewBox")?(t=t.viewBox.baseVal,[[t.x,t.y],[t.x+t.width,t.y+t.height]]):[[0,0],[t.width.baseVal.value,t.height.baseVal.value]]}function zg(){return navigator.maxTouchPoints||"ontouchstart"in this}function Io(t){for(;!t.__brush;)if(!(t=t.parentNode))return;return t.__brush}function jg(t){return t[0][0]===t[1][0]||t[0][1]===t[1][1]}function zl(t){var e=t.__brush;return e?e.dim.output(e.selection):null}function Vg(){return Oo(xi)}function Gg(){return Oo(Ti)}function q0(){return Oo(Lg)}function Oo(t){var e=Ug,n=Bg,a=zg,i=!0,o=ri("start","brush","end"),s=6,l;function c(L){var w=L.property("__brush",N).selectAll(".overlay").data([Pa("overlay")]);w.enter().append("rect").attr("class","overlay").attr("pointer-events","all").attr("cursor",hr.overlay).merge(w).each(function(){var W=Io(this).extent;ot(this).attr("x",W[0][0]).attr("y",W[0][1]).attr("width",W[1][0]-W[0][0]).attr("height",W[1][1]-W[0][1])}),L.selectAll(".selection").data([Pa("selection")]).enter().append("rect").attr("class","selection").attr("cursor",hr.selection).attr("fill","#777").attr("fill-opacity",.3).attr("stroke","#fff").attr("shape-rendering","crispEdges");var X=L.selectAll(".handle").data(t.handles,function(W){return W.type});X.exit().remove(),X.enter().append("rect").attr("class",function(W){return"handle handle--"+W.type}).attr("cursor",function(W){return hr[W.type]}),L.each(f).attr("fill","none").attr("pointer-events","all").on("mousedown.brush",m).filter(a).on("touchstart.brush",m).on("touchmove.brush",S).on("touchend.brush touchcancel.brush",P).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}c.move=function(L,w,X){L.tween?L.on("start.brush",function(W){g(this,arguments).beforestart().start(W)}).on("interrupt.brush end.brush",function(W){g(this,arguments).end(W)}).tween("brush",function(){var W=this,H=W.__brush,k=g(W,arguments),K=H.selection,at=t.input(typeof w=="function"?w.apply(this,arguments):w,H.extent),ht=Qr(K,at);function $t(dt){H.selection=dt===1&&at===null?null:ht(dt),f.call(W),k.brush()}return K!==null&&at!==null?$t:$t(1)}):L.each(function(){var W=this,H=arguments,k=W.__brush,K=t.input(typeof w=="function"?w.apply(W,H):w,k.extent),at=g(W,H).beforestart();qr(W),k.selection=K===null?null:K,f.call(W),at.start(X).brush(X).end(X)})},c.clear=function(L,w){c.move(L,null,w)};function f(){var L=ot(this),w=Io(this).selection;w?(L.selectAll(".selection").style("display",null).attr("x",w[0][0]).attr("y",w[0][1]).attr("width",w[1][0]-w[0][0]).attr("height",w[1][1]-w[0][1]),L.selectAll(".handle").style("display",null).attr("x",function(X){return X.type[X.type.length-1]==="e"?w[1][0]-s/2:w[0][0]-s/2}).attr("y",function(X){return X.type[0]==="s"?w[1][1]-s/2:w[0][1]-s/2}).attr("width",function(X){return X.type==="n"||X.type==="s"?w[1][0]-w[0][0]+s:s}).attr("height",function(X){return X.type==="e"||X.type==="w"?w[1][1]-w[0][1]+s:s})):L.selectAll(".selection,.handle").style("display","none").attr("x",null).attr("y",null).attr("width",null).attr("height",null)}function g(L,w,X){var W=L.__brush.emitter;return W&&(!X||!W.clean)?W:new v(L,w,X)}function v(L,w,X){this.that=L,this.args=w,this.state=L.__brush,this.active=0,this.clean=X}v.prototype={beforestart:function(){return++this.active===1&&(this.state.emitter=this,this.starting=!0),this},start:function(L,w){return this.starting?(this.starting=!1,this.emit("start",L,w)):this.emit("brush",L),this},brush:function(L,w){return this.emit("brush",L,w),this},end:function(L,w){return--this.active===0&&(delete this.state.emitter,this.emit("end",L,w)),this},emit:function(L,w,X){var W=ot(this.that).datum();o.call(L,this.that,new Mg(L,{sourceEvent:w,target:c,selection:t.output(this.state.selection),mode:X,dispatch:o}),W)}};function m(L){if(l&&!L.touches||!n.apply(this,arguments))return;var w=this,X=L.target.__data__.type,W=(i&&L.metaKey?X="overlay":X)==="selection"?Ll:i&&L.altKey?ta:_r,H=t===Ti?null:Ng[X],k=t===xi?null:Fg[X],K=Io(w),at=K.extent,ht=K.selection,$t=at[0][0],dt,st,Vt=at[0][1],vt,Q,St=at[1][0],ct,At,Gt=at[1][1],Bt,Kt,ne=0,le=0,be,Oe=H&&k&&i&&L.shiftKey,Ce,He,Fe=Array.from(L.touches||[L],pe=>{const fn=pe.identifier;return pe=Xn(pe,w),pe.point0=pe.slice(),pe.identifier=fn,pe});qr(w);var dn=g(w,arguments,!0).beforestart();if(X==="overlay"){ht&&(be=!0);const pe=[Fe[0],Fe[1]||Fe[0]];K.selection=ht=[[dt=t===Ti?$t:An(pe[0][0],pe[1][0]),vt=t===xi?Vt:An(pe[0][1],pe[1][1])],[ct=t===Ti?St:Sn(pe[0][0],pe[1][0]),Bt=t===xi?Gt:Sn(pe[0][1],pe[1][1])]],Fe.length>1&&un(L)}else dt=ht[0][0],vt=ht[0][1],ct=ht[1][0],Bt=ht[1][1];st=dt,Q=vt,At=ct,Kt=Bt;var Jt=ot(w).attr("pointer-events","none"),xe=Jt.selectAll(".overlay").attr("cursor",hr[X]);if(L.touches)dn.moved=Lt,dn.ended=Ge;else{var Re=ot(L.view).on("mousemove.brush",Lt,!0).on("mouseup.brush",Ge,!0);i&&Re.on("keydown.brush",Pn,!0).on("keyup.brush",wn,!0),co(L.view)}f.call(w),dn.start(L,W.name);function Lt(pe){for(const fn of pe.changedTouches||[pe])for(const Ga of Fe)Ga.identifier===fn.identifier&&(Ga.cur=Xn(fn,w));if(Oe&&!Ce&&!He&&Fe.length===1){const fn=Fe[0];Nl(fn.cur[0]-fn[0])>Nl(fn.cur[1]-fn[1])?He=!0:Ce=!0}for(const fn of Fe)fn.cur&&(fn[0]=fn.cur[0],fn[1]=fn.cur[1]);be=!0,Eo(pe),un(pe)}function un(pe){const fn=Fe[0],Ga=fn.point0;var br;switch(ne=fn[0]-Ga[0],le=fn[1]-Ga[1],W){case bo:case Ll:{H&&(ne=Sn($t-dt,An(St-ct,ne)),st=dt+ne,At=ct+ne),k&&(le=Sn(Vt-vt,An(Gt-Bt,le)),Q=vt+le,Kt=Bt+le);break}case _r:{Fe[1]?(H&&(st=Sn($t,An(St,Fe[0][0])),At=Sn($t,An(St,Fe[1][0])),H=1),k&&(Q=Sn(Vt,An(Gt,Fe[0][1])),Kt=Sn(Vt,An(Gt,Fe[1][1])),k=1)):(H<0?(ne=Sn($t-dt,An(St-dt,ne)),st=dt+ne,At=ct):H>0&&(ne=Sn($t-ct,An(St-ct,ne)),st=dt,At=ct+ne),k<0?(le=Sn(Vt-vt,An(Gt-vt,le)),Q=vt+le,Kt=Bt):k>0&&(le=Sn(Vt-Bt,An(Gt-Bt,le)),Q=vt,Kt=Bt+le));break}case ta:{H&&(st=Sn($t,An(St,dt-ne*H)),At=Sn($t,An(St,ct+ne*H))),k&&(Q=Sn(Vt,An(Gt,vt-le*k)),Kt=Sn(Vt,An(Gt,Bt+le*k)));break}}At0&&(dt=st-ne),k<0?Bt=Kt-le:k>0&&(vt=Q-le),W=bo,xe.attr("cursor",hr.selection),un(pe));break}default:return}Eo(pe)}function wn(pe){switch(pe.keyCode){case 16:{Oe&&(Ce=He=Oe=!1,un(pe));break}case 18:{W===ta&&(H<0?ct=At:H>0&&(dt=st),k<0?Bt=Kt:k>0&&(vt=Q),W=_r,un(pe));break}case 32:{W===bo&&(pe.altKey?(H&&(ct=At-ne*H,dt=st+ne*H),k&&(Bt=Kt-le*k,vt=Q+le*k),W=ta):(H<0?ct=At:H>0&&(dt=st),k<0?Bt=Kt:k>0&&(vt=Q),W=_r),xe.attr("cursor",hr[X]),un(pe));break}default:return}Eo(pe)}}function S(L){g(this,arguments).moved(L)}function P(L){g(this,arguments).ended(L)}function N(){var L=this.__brush||{selection:null};return L.extent=Ro(e.apply(this,arguments)),L.dim=t,L}return c.extent=function(L){return arguments.length?(e=typeof L=="function"?L:Ao(Ro(L)),c):e},c.filter=function(L){return arguments.length?(n=typeof L=="function"?L:Ao(!!L),c):n},c.touchable=function(L){return arguments.length?(a=typeof L=="function"?L:Ao(!!L),c):a},c.handleSize=function(L){return arguments.length?(s=+L,c):s},c.keyModifiers=function(L){return arguments.length?(i=!!L,c):i},c.on=function(){var L=o.on.apply(o,arguments);return L===o?c:L},c}function Xg(){return typeof globalThis=="object"&&globalThis!==null&&globalThis.Object===Object&&globalThis||typeof global=="object"&&global!==null&&global.Object===Object&&global||typeof self=="object"&&self!==null&&self.Object===Object&&self||Function("return this")()}function Hg(t){const e=typeof(t==null?void 0:t.requestAnimationFrame)=="function"&&typeof(t==null?void 0:t.cancelAnimationFrame)=="function",n=typeof(t==null?void 0:t.requestIdleCallback)=="function"&&typeof(t==null?void 0:t.cancelIdleCallback)=="function",a=o=>setTimeout(o,1),i=o=>clearTimeout(o);return[e?t.requestAnimationFrame:a,e?t.cancelAnimationFrame:i,n?t.requestIdleCallback:a,n?t.cancelIdleCallback:i]}const Ke=Xg(),gn=Ke==null?void 0:Ke.document,[Yg,_0,jl,t1]=Hg(Ke);var Wg=Object.defineProperty,Vl=Object.getOwnPropertySymbols,Kg=Object.prototype.hasOwnProperty,Zg=Object.prototype.propertyIsEnumerable,Gl=(t,e,n)=>e in t?Wg(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,Xl=(t,e)=>{for(var n in e||(e={}))Kg.call(e,n)&&Gl(t,n,e[n]);if(Vl)for(var n of Vl(e))Zg.call(e,n)&&Gl(t,n,e[n]);return t};const De=t=>t||t===0,ve=t=>typeof t=="function",ze=t=>typeof t=="string",he=t=>typeof t=="number",ln=t=>typeof t=="undefined",Qe=t=>typeof t!="undefined",Co=t=>typeof t=="boolean",Jg=t=>Math.ceil(t/10)*10,$i=t=>Math.ceil(t)+.5,Dr=t=>t[1]-t[0],nr=t=>typeof t=="object",qn=t=>ln(t)||t===null||ze(t)&&t.length===0||nr(t)&&!(t instanceof Date)&&Object.keys(t).length===0||he(t)&&isNaN(t),cn=t=>!qn(t),je=t=>Array.isArray(t),Be=t=>t&&!(t!=null&&t.nodeType)&&nr(t)&&!je(t);function $r(t,e,n){return Qe(t[e])?t[e]:n}function Qg(t,e){let n=!1;return Object.keys(t).forEach(a=>t[a]===e&&(n=!0)),n}function _e(t,e,...n){const a=ve(t);return a&&t.call(e,...n),a}function Si(t,e){let n=0;const a=function(...i){!--n&&e.apply(this,...i)};"duration"in t?t.each(()=>++n).on("end",a):(++n,t.call(a))}function Po(t){return ze(t)?t.replace(/<(script|img)?/ig,"<").replace(/(script)?>/ig,">"):t}function wa(t,e,n=[-1,1],a=!1){if(!(!t||!ze(e)))if(e.indexOf(` +`)===-1)t.text(e);else{const i=[t.text(),e].map(o=>o.replace(/[\s\n]/g,""));if(i[0]!==i[1]){const o=e.split(` +`),s=a?o.length-1:1;t.html(""),o.forEach((l,c)=>{t.append("tspan").attr("x",0).attr("dy",`${c===0?n[0]*s:n[1]}em`).text(l)})}}}function Hl(t){const{x:e,y:n,width:a,height:i}=t.getBBox();return[{x:e,y:n+i},{x:e,y:n},{x:e+a,y:n},{x:e+a,y:n+i}]}function Yl(t){const{width:e,height:n}=t.getBoundingClientRect(),a=Hl(t),i=a[0].x,o=Math.min(a[0].y,a[1].y);return{x:i,y:o,width:e,height:n}}function Hn(t,e){var n;const a=t&&((n=t.touches||t.sourceEvent&&t.sourceEvent.touches)==null?void 0:n[0]);let i=[0,0];try{i=Xn(a||t,e)}catch(o){}return i.map(o=>isNaN(o)?0:o)}function Wl(t){const{event:e,$el:n}=t,a=n.subchart.main||n.main;let i;return e&&e.type==="brush"?i=e.selection:a&&(i=a.select(".bb-brush").node())&&(i=zl(i)),i}function Ma(t){return!("rect"in t)||"rect"in t&&t.hasAttribute("width")&&t.rect.width!==+t.getAttribute("width")?t.rect=t.getBoundingClientRect():t.rect}function gr(t=!0,e=0,n=1e4){const a=Ke.crypto||Ke.msCrypto,i=a?e+a.getRandomValues(new Uint32Array(1))[0]%(n-e+1):Math.floor(Math.random()*(n-e)+e);return t?String(i):i}function wo(t,e,n,a,i){if(n>a)return-1;const o=Math.floor((n+a)/2);let{x:s,w:l=0}=t[o];return i&&(s=t[o].y,l=t[o].h),e>=s&&e<=s+l?o:e{if(Be(n)&&n.constructor){const a=new n.constructor;for(const i in n)a[i]=e(n[i]);return a}return n};return t.map(n=>e(n)).reduce((n,a)=>Xl(Xl({},n),a))}function yn(t={},e){je(e)&&e.forEach(n=>yn(t,n));for(const n in e)/^\d+$/.test(n)||n in t||(t[n]=e[n]);return t}const Cn=t=>t.charAt(0).toUpperCase()+t.slice(1);function qg(t,e="-"){return t.split(e).map((n,a)=>a?n.charAt(0).toUpperCase()+n.slice(1).toLowerCase():n.toLowerCase()).join("")}const Lr=t=>[].slice.call(t);function _g(t,e,n){const{rootSelector:a="",sheet:i}=t,s=`${a} ${(l=>l.replace(/\s?(bb-)/g,".$1").replace(/\.+/g,"."))(e)} {${n.join(";")}}`;return i[i.insertRule?"insertRule":"addRule"](s,i.cssRules.length)}function tv(t){let e=[];return t.forEach(n=>{var a;try{n.cssRules&&n.cssRules.length&&(e=e.concat(Lr(n.cssRules)))}catch(i){(a=Ke.console)==null||a.warn(`Error while reading rules from ${n.href}: ${i.toString()}`)}}),e}function Zl(t){var e,n,a,i,o,s;return{x:((n=(e=Ke.pageXOffset)!=null?e:Ke.scrollX)!=null?n:0)+((a=t.scrollLeft)!=null?a:0),y:((o=(i=Ke.pageYOffset)!=null?i:Ke.scrollY)!=null?o:0)+((s=t.scrollTop)!=null?s:0)}}function Ai(t,e=0,n=0,a=!0){const i=new DOMPoint(e,n),o=t.getScreenCTM(),s=i.matrixTransform(a?o==null?void 0:o.inverse():o);if(a===!1){const l=t.getBoundingClientRect();s.x-=l.x,s.y-=l.y}return s}function Jl(t){const e=t?t.transform:null,n=e&&e.baseVal;return n&&n.numberOfItems?n.getItem(0).matrix:{a:0,b:0,c:0,d:0,e:0,f:0}}function Mo(t){const e=t[0]instanceof Date,n=(e?t.map(Number):t).filter((a,i,o)=>o.indexOf(a)===i);return e?n.map(a=>new Date(a)):n}function Do(t){return t&&t.length?t.reduce((e,n)=>e.concat(n)):[]}function ea(t,...e){if(!e.length||e.length===1&&!e[0])return t;const n=e.shift();return Be(t)&&Be(n)&&Object.keys(n).forEach(a=>{if(!/^(__proto__|constructor|prototype)$/i.test(a)){const i=n[a];Be(i)?(!t[a]&&(t[a]={}),t[a]=ea(t[a],i)):t[a]=je(i)?i.concat():i}}),ea(t,...e)}function na(t,e=!0){let n;return t[0]instanceof Date?n=e?(a,i)=>a-i:(a,i)=>i-a:e&&!t.every(isNaN)?n=(a,i)=>a-i:e||(n=(a,i)=>a>i&&-1||acn(a));return n.length?he(n[0])?n=Math[t](...n):n[0]instanceof Date&&(n=na(n,t==="min")[0]):n=void 0,n}const Ei=(t,e,n=1)=>{const a=[],i=Math.max(0,Math.ceil((e-t)/n))|0;for(let o=t;o{const t=()=>({bubbles:!1,cancelable:!1,screenX:0,screenY:0,clientX:0,clientY:0});try{return new MouseEvent("t"),(e,n,a=t())=>{e.dispatchEvent(new MouseEvent(n,a))}}catch(e){return(n,a,i=t())=>{const o=gn.createEvent("MouseEvent");o.initMouseEvent(a,i.bubbles,i.cancelable,Ke,0,i.screenX,i.screenY,i.clientX,i.clientY,!1,!1,!1,!1,0,null),n.dispatchEvent(o)}}})(),touch:(t,e,n)=>{const a=new Touch(ea({identifier:Date.now(),target:t,radiusX:2.5,radiusY:2.5,rotationAngle:10,force:.5},n));t.dispatchEvent(new TouchEvent(e,{cancelable:!0,bubbles:!0,shiftKey:!0,touches:[a],targetTouches:[],changedTouches:[a]}))}};function bi(t,e){let n=t;for(const a in e)n=n.replace(new RegExp(`{=${a}}`,"g"),e[a]);return n}function Yn(t){var e;let n;if(t instanceof Date)n=t;else if(ze(t)){const{config:a,format:i}=this;n=(e=i.dataTime(a.data_xFormat)(t))!=null?e:new Date(t)}else he(t)&&!isNaN(t)&&(n=new Date(+t));return(!n||isNaN(+n))&&console&&console.error&&console.error(`Failed to parse x '${t}' to Date object`),n}function Lo(t){const e=t.attr("viewBox");return e?/(\d+(\.\d+)?){3}/.test(e):!1}function nv(t,e,n=!1){const a=!!t.node;let i=!1;for(const[o,s]of Object.entries(e))if(i=a?t.style(o)===s:t.style[o]===s,n===!1&&i)break;return i}function Da(){var t,e;return((t=gn)==null?void 0:t.hidden)===!1||((e=gn)==null?void 0:e.visibilityState)==="visible"}function rv(t,e){const{DocumentTouch:n,matchMedia:a,navigator:i}=Ke,o=a==null?void 0:a("(pointer:coarse)").matches;let s=!1;if(e)if(i&&"maxTouchPoints"in i)s=i.maxTouchPoints>0;else if("ontouchmove"in Ke||n&&gn instanceof n)s=!0;else if(o)s=!0;else{const c=i.userAgent;s=/\b(BlackBerry|webOS|iPhone|IEMobile)\b/i.test(c)||/\b(Android|Windows Phone|iPad|iPod)\b/i.test(c)}return t&&!o&&(a==null?void 0:a("(pointer:fine)").matches)&&"mouse"||s&&"touch"||"mouse"}function Ql(t,e){e()===!1?Yg(()=>Ql(t,e)):t()}var av=Object.defineProperty,kl=Object.getOwnPropertySymbols,iv=Object.prototype.hasOwnProperty,ov=Object.prototype.propertyIsEnumerable,No=(t,e,n)=>e in t?av(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,ql=(t,e)=>{for(var n in e||(e={}))iv.call(e,n)&&No(t,n,e[n]);if(kl)for(var n of kl(e))ov.call(e,n)&&No(t,n,e[n]);return t},sv=(t,e,n)=>No(t,typeof e!="symbol"?e+"":e,n);const _l=class bf{static setOptions(e){this.data=e.reduce((n,a)=>ql(ql({},n),a),this.data)}constructor(){return kg(Vd,Ud,Hd,zd,Yd,jd,Gd,Xd,bf.data)}};sv(_l,"data",{});let Nr=_l;class lv{constructor(){return{chart:null,main:null,svg:null,axis:{x:null,y:null,y2:null,subX:null},axisTooltip:{x:null,y:null,y2:null},defs:null,tooltip:null,legend:null,title:null,subchart:{main:null,bar:null,line:null,area:null},arcs:null,bar:null,candlestick:null,line:null,area:null,circle:null,radar:null,text:null,grid:{main:null,x:null,y:null},gridLines:{main:null,x:null,y:null},region:{main:null,list:null},eventRect:null,zoomResetBtn:null}}}class cv{constructor(){return{width:0,width2:0,height:0,height2:0,margin:{top:0,bottom:0,left:0,right:0},margin2:{top:0,bottom:0,left:0,right:0},margin3:{top:0,bottom:0,left:0,right:0},arcWidth:0,arcHeight:0,xAxisHeight:0,hasAxis:!1,hasFunnel:!1,hasRadar:!1,hasTreemap:!1,cssRule:{},current:{domain:void 0,width:0,height:0,dataMax:0,maxTickSize:{x:{width:0,height:0,ticks:[],clipPath:0,domain:""},y:{width:0,height:0,domain:""},y2:{width:0,height:0,domain:""}},types:[],needle:void 0},isLegendRight:!1,isLegendInset:!1,isLegendTop:!1,isLegendLeft:!1,legendStep:0,legendItemWidth:0,legendItemHeight:0,legendHasRendered:!1,eventReceiver:{currentIdx:-1,rect:{},data:[],coords:[]},axis:{x:{padding:{left:0,right:0},tickCount:0}},rotatedPadding:{left:30,right:0,top:5},withoutFadeIn:{},inputType:"",datetimeId:"",clip:{id:"",idXAxis:"",idYAxis:"",idXAxisTickTexts:"",idGrid:"",idSubchart:"",path:"",pathXAxis:"",pathYAxis:"",pathXAxisTickTexts:"",pathGrid:""},event:null,dragStart:null,dragging:!1,flowing:!1,cancelClick:!1,mouseover:!1,rendered:!1,transiting:!1,redrawing:!1,resizing:!1,toggling:!1,zooming:!1,hasNegativeValue:!1,hasPositiveValue:!0,orgAreaOpacity:"0.2",orgConfig:{},hiddenTargetIds:[],hiddenLegendIds:[],focusedTargetIds:[],defocusedTargetIds:[],radius:0,innerRadius:0,outerRadius:void 0,innerRadiusRatio:0,gaugeArcWidth:0,radiusExpanded:0,xgridAttr:{x1:null,x2:null,y1:null,y2:null}}}}const tc={element:lv,state:cv};class uv{constructor(){Object.keys(tc).forEach(e=>{this[e]=new tc[e]})}getStore(e){return this[e]}}var fv=Object.defineProperty,dv=(t,e,n)=>e in t?fv(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,hv=(t,e,n)=>dv(t,typeof e!="symbol"?e+"":e,n);const Ln={bubbleBaseLength:"$baseLength",colorPattern:"__colorPattern__",dataMinMax:"$dataMinMax",dataTotalSum:"$dataTotalSum",dataTotalPerIndex:"$totalPerIndex",legendItemTextBox:"legendItemTextBox",radarPoints:"$radarPoints",radarTextWidth:"$radarTextWidth",setOverOut:"setOverOut",callOverOutForTouch:"callOverOutForTouch",textRect:"textRect"};class gv{constructor(){hv(this,"cache",{})}add(e,n,a=!1){return this.cache[e]=a?this.cloneTarget(n):n,this.cache[e]}remove(e){(ze(e)?[e]:e).forEach(n=>delete this.cache[n])}get(e,n=!1){if(n&&Array.isArray(e)){const a=[];for(let i=0,o;o=e[i];i++)o in this.cache&&a.push(this.cloneTarget(this.cache[o]));return a}else{const a=this.cache[e];return De(a)?a:null}}reset(e){const n=this;for(const a in n.cache)(e||/^\$/.test(a))&&(n.cache[a]=null)}cloneTarget(e){return{id:e.id,id_org:e.id_org,values:e.values.map(n=>({x:n.x,value:n.value,id:n.id}))}}}const oe={AREA:"area",AREA_LINE_RANGE:"area-line-range",AREA_SPLINE:"area-spline",AREA_SPLINE_RANGE:"area-spline-range",AREA_STEP:"area-step",AREA_STEP_RANGE:"area-step-range",BAR:"bar",BUBBLE:"bubble",CANDLESTICK:"candlestick",DONUT:"donut",FUNNEL:"funnel",GAUGE:"gauge",LINE:"line",PIE:"pie",POLAR:"polar",RADAR:"radar",SCATTER:"scatter",SPLINE:"spline",STEP:"step",TREEMAP:"treemap"},Fo={AREA:"initArea",AREA_LINE_RANGE:"initArea",AREA_SPLINE:"initArea",AREA_SPLINE_RANGE:"initArea",AREA_STEP:"initArea",AREA_STEP_RANGE:"initArea",BAR:"initBar",BUBBLE:"initCircle",CANDLESTICK:"initCandlestick",DONUT:"initArc",FUNNEL:"initFunnel",GAUGE:"initArc",LINE:"initLine",PIE:"initArc",POLAR:"initPolar",RADAR:"initCircle",SCATTER:"initCircle",SPLINE:"initLine",STEP:"initLine",TREEMAP:"initTreemap"},Sr={Area:[oe.AREA,oe.AREA_SPLINE,oe.AREA_SPLINE_RANGE,oe.AREA_LINE_RANGE,oe.AREA_STEP,oe.AREA_STEP_RANGE],AreaRange:[oe.AREA_SPLINE_RANGE,oe.AREA_LINE_RANGE,oe.AREA_STEP_RANGE],Arc:[oe.PIE,oe.DONUT,oe.GAUGE,oe.POLAR,oe.RADAR],Line:[oe.LINE,oe.SPLINE,oe.AREA,oe.AREA_SPLINE,oe.AREA_SPLINE_RANGE,oe.AREA_LINE_RANGE,oe.STEP,oe.AREA_STEP,oe.AREA_STEP_RANGE],Step:[oe.STEP,oe.AREA_STEP,oe.AREA_STEP_RANGE],Spline:[oe.SPLINE,oe.AREA_SPLINE,oe.AREA_SPLINE_RANGE]};function vv(t){const e=t,{config:n}=e;let a="";if(qn(n.data_type||n.data_types)&&!e[Fo.LINE])a="line";else for(const i in Fo){const o=oe[i];if(e.hasType(o)&&!e[Fo[i]]){a=o;break}}a&&pv(`Please, make sure if %c${qg(a)}`,"module has been imported and specified correctly.","https://github.com/naver/billboard.js/wiki/CHANGELOG-v2#modularization-by-its-functionality")}function pv(t,e,n){var a;const i="[billboard.js]";if((a=Ke.console)==null?void 0:a.error){const s=e?["background:red;color:white;display:block;font-size:15px",e]:[];console.error(`\u274C ${i} ${t}`,"background:red;color:white;display:block;font-size:15px",...s),n&&console.info("%c\u2139\uFE0F","font-size:15px",n)}throw Error(`${i} ${t.replace(/\%c([a-z-]+)/i,"'$1' ")} ${e!=null?e:""}`)}const{setTimeout:mv,clearTimeout:yv}=Ke;function xv(t){const e=[];let n;const a=function(){a.clear(),t===!1?jl(()=>{e.forEach(i=>i())},{timeout:200}):n=mv(()=>{e.forEach(i=>i())},he(t)?t:200)};return a.clear=()=>{n&&(yv(n),n=null)},a.add=i=>e.push(i),a.remove=i=>e.splice(e.indexOf(i),1),a}function ec(){let t=[];const e=function(n,a){function i(){var o;let s=0;for(let l=0,c;c=t[l];l++){if(c===!0||(o=c.empty)!=null&&o.call(c)){s++;continue}if(Da()===!1){s=t.length;break}try{c.transition()}catch(f){s++}}return s===t.length}Ql(()=>{a==null||a()},i)};return e.add=function(n){je(n)?t=t.concat(n):t.push(n)},e}const Bo={};function Tv(t,e){var n;const a=t.toString(),i=a.replace(/(function|[\s\W\n])/g,"").substring(0,15);return i in Bo||(Bo[i]=new Ke.Blob([`${(n=e==null?void 0:e.map(String).join(";"))!=null?n:""} + + self.onmessage=function({data}) { + const result = (${a}).apply(null, data); + self.postMessage(result); + };`],{type:"text/javascript"})),Ke.URL.createObjectURL(Bo[i])}function $v(t){const e=new Ke.Worker(t);return e.onerror=function(n){console.error?console.error(n):console.log(n)},e}function Uo(t=!0,e,n,a){let i=function(...o){const s=e(...o);n(s)};if(Ke.Worker&&t){const o=Tv(e,a),s=$v(o);i=function(...l){s.postMessage(l),s.onmessage=function(c){return Ke.URL.revokeObjectURL(o),n(c.data)}}}return i}var nc={},zo={},jo=34,La=10,Vo=13;function rc(t){return new Function("d","return {"+t.map(function(e,n){return JSON.stringify(e)+": d["+n+'] || ""'}).join(",")+"}")}function Sv(t,e){var n=rc(t);return function(a,i){return e(n(a),i,t)}}function ac(t){var e=Object.create(null),n=[];return t.forEach(function(a){for(var i in a)i in e||n.push(e[i]=i)}),n}function Nn(t,e){var n=t+"",a=n.length;return a9999?"+"+Nn(t,6):Nn(t,4)}function Ev(t){var e=t.getUTCHours(),n=t.getUTCMinutes(),a=t.getUTCSeconds(),i=t.getUTCMilliseconds();return isNaN(t)?"Invalid Date":Av(t.getUTCFullYear(),4)+"-"+Nn(t.getUTCMonth()+1,2)+"-"+Nn(t.getUTCDate(),2)+(i?"T"+Nn(e,2)+":"+Nn(n,2)+":"+Nn(a,2)+"."+Nn(i,3)+"Z":a?"T"+Nn(e,2)+":"+Nn(n,2)+":"+Nn(a,2)+"Z":n||e?"T"+Nn(e,2)+":"+Nn(n,2)+"Z":"")}function ic(t){var e=new RegExp('["'+t+` +\r]`),n=t.charCodeAt(0);function a(v,m){var S,P,N=i(v,function(L,w){if(S)return S(L,w-1);P=L,S=m?Sv(L,m):rc(L)});return N.columns=P||[],N}function i(v,m){var S=[],P=v.length,N=0,L=0,w,X=P<=0,W=!1;v.charCodeAt(P-1)===La&&--P,v.charCodeAt(P-1)===Vo&&--P;function H(){if(X)return zo;if(W)return W=!1,nc;var K,at=N,ht;if(v.charCodeAt(at)===jo){for(;N++=P?X=!0:(ht=v.charCodeAt(N++))===La?W=!0:ht===Vo&&(W=!0,v.charCodeAt(N)===La&&++N),v.slice(at+1,K-1).replace(/""/g,'"')}for(;N0){if(typeof e[s-1]=="undefined"&&(e[s-1]={}),typeof o=="undefined")throw new Error(`Source data is missing a component at (${a}, ${s})!`);e[s-1][i]=o}})}),e}function Xo(t){const e=t[0],n=[];return t.forEach(function(a,i){if(i>0){const o={};a.forEach(function(s,l){if(typeof s=="undefined")throw new Error(`Source data is missing a component at (${i}, ${l})!`);o[e[l]]=s}),n.push(o)}}),n}function oc(t,e){const n=[];let a,i;if(Array.isArray(t)){const o=function(s,l){if(s[l]!==void 0)return s[l];const f=l.replace(/\[(\w+)\]/g,".$1").replace(/^\./,"").split(".");let g=s;return f.some(function(v){return!(g=g&&v in g?g[v]:void 0)}),g};e.x?a=e.value.concat(e.x):a=e.value,n.push(a),t.forEach(function(s){const l=a.map(function(c){let f=o(s,c);return typeof f=="undefined"&&(f=null),f});n.push(l)}),i=Xo(n)}else Object.keys(t).forEach(function(o){var s;const l=t[o].concat();(s=l.unshift)==null||s.call(l,o),n.push(l)}),i=Go(n);return i}function Cv(t,e="csv",n,a,i){const o=new XMLHttpRequest,s={csv:Pv,tsv:wv,json:oc};o.open("GET",t),n&&Object.keys(n).forEach(function(l){o.setRequestHeader(l,n[l])}),o.onreadystatechange=function(){if(o.readyState===4)if(o.status===200){const l=o.responseText;l&&i.call(this,s[e](e==="json"?JSON.parse(l):l,a))}else throw new Error(`${t}: Something went wrong loading!`)},o.send()}function sc(t,e){const n=t.rows(e);let a;return n.length===1?(a=[{}],n[0].forEach(i=>{a[0][i]=null})):a=t.parse(e),a}function Pv(t){return sc({rows:Rv,parse:bv},t)}function wv(t){return sc({rows:Ov,parse:Iv},t)}function lc(t,e){const n=t||(e==null?void 0:e.data_keys);return n!=null&&n.x&&(e.data_x=n.x),n}var Mv={convertData(t,e){const{config:n}=this,a=n.boost_useWorker;let i=t;if(t.bindto&&(i={},["url","mimeType","headers","keys","json","keys","rows","columns"].forEach(o=>{const s=`data_${o}`;s in t&&(i[o]=t[s])})),i.url&&e)Cv(i.url,i.mimeType,i.headers,lc(i.keys,n),e);else if(i.json)Uo(a,oc,e,[Go,Xo])(i.json,lc(i.keys,n));else if(i.rows)Uo(a,Xo,e)(i.rows);else if(i.columns)Uo(a,Go,e)(i.columns);else if(t.bindto)throw Error("url or json or rows or columns is required.")},convertDataToTargets(t,e){const n=this,{axis:a,config:i,state:o}=n,s=i.data_type;let l=!1,c=!1,f=!1;a&&(l=a.isCategorized(),c=a.isTimeSeries(),f=a.isCustomX());const g=Object.keys(t[0]||{}),v=g.length?g.filter(n.isNotX,n):[],m=g.length?g.filter(n.isX,n):[];let S;v.forEach(N=>{const L=this.getXKey(N);f||c?m.indexOf(L)>=0?S=(e&&n.data.xs[N]||[]).concat(t.map(w=>w[L]).filter(De).map((w,X)=>n.generateTargetX(w,N,X))):i.data_x?S=this.getOtherTargetXs():cn(i.data_xs)&&(S=n.getXValuesOfXKey(L,n.data.targets)):S=t.map((w,X)=>X),S&&(this.data.xs[N]=S)}),v.forEach(N=>{if(!this.data.xs[N])throw new Error(`x is not defined for id = "${N}".`)});const P=v.map((N,L)=>{const w=i.data_idConverter.bind(n.api)(N),X=n.getXKey(N),W=f&&l,H=W&&t.map(at=>at.x).every(at=>i.axis_x_categories.indexOf(at)>-1),k=t.__append__,K=X===null&&k?n.api.data.values(N).length:0;return{id:w,id_org:N,values:t.map((at,ht)=>{const $t=at[X];let dt=at[N],st;return dt=dt!==null&&!isNaN(dt)&&!Be(dt)?+dt:je(dt)||Be(dt)?dt:null,(W||o.hasRadar)&&L===0&&!ln($t)?(!H&&L===0&&ht===0&&!k&&(i.axis_x_categories=[]),st=i.axis_x_categories.indexOf($t),st===-1&&(st=i.axis_x_categories.length,i.axis_x_categories.push($t))):st=n.generateTargetX($t,N,K+ht),(ln(dt)||n.data.xs[N].length<=ht)&&(st=void 0),{x:st,value:dt,id:w,index:-1}}).filter(at=>Qe(at.x))}});if(P.forEach(N=>{var L;i.data_xSort&&(N.values=N.values.sort((w,X)=>{const W=w.x||w.x===0?w.x:1/0,H=X.x||X.x===0?X.x:1/0;return W-H})),N.values.forEach((w,X)=>w.index=X),(L=n.data.xs[N.id])==null||L.sort((w,X)=>w-X)}),o.hasNegativeValue=n.hasNegativeValueInTargets(P),o.hasPositiveValue=n.hasPositiveValueInTargets(P),s&&n.isValidChartType(s)){const N=n.mapToIds(P).filter(L=>!(L in i.data_types)||!n.isValidChartType(i.data_types[L]));n.setTargetType(N,s)}return P.forEach(N=>n.cache.add(N.id_org,N,!0)),P}},Dv={isX(t){const e=this,{config:n}=e,a=n.data_x&&t===n.data_x,i=cn(n.data_xs)&&Qg(n.data_xs,t);return a||i},isNotX(t){return!this.isX(t)},isStackNormalized(){const{config:t}=this;return!!(t.data_stack_normalize&&t.data_groups.length)},isGrouped(t){const e=this.config.data_groups;return t?e.some(n=>n.indexOf(t)>=0&&n.length>1):e.length>0},getXKey(t){const e=this,{config:n}=e;return n.data_x?n.data_x:cn(n.data_xs)?n.data_xs[t]:null},getXValuesOfXKey(t,e){const n=this,a=e&&cn(e)?n.mapToIds(e):[];let i;return a.forEach(o=>{n.getXKey(o)===t&&(i=n.data.xs[o])}),i},getIndexByX(t,e){const n=this;return e?e.indexOf(ze(t)?t:+t):(n.filterByX(n.data.targets,t)[0]||{index:null}).index},getXValue(t,e){const n=this;return t in n.data.xs&&n.data.xs[t]&&De(n.data.xs[t][e])?n.data.xs[t][e]:e},getOtherTargetXs(){const t=this,e=Object.keys(t.data.xs);return e.length?t.data.xs[e[0]]:null},getOtherTargetX(t){const e=this.getOtherTargetXs();return e&&t{n.data_xs[a]=t[a]})},isMultipleX(){return!this.config.axis_x_forceAsSingle&&(cn(this.config.data_xs)||this.hasType("bubble")||this.hasType("scatter"))},addName(t){const e=this,{config:n}=e;let a;return t&&(a=n.data_names[t.id],t.name=a!==void 0?a:t.id),t},getAllValuesOnIndex(t,e=!1){const n=this;let a=n.filterTargetsToShow(n.data.targets).map(i=>n.addName(n.getValueOnIndex(i.values,t)));return e&&(a=a.filter(i=>i&&"value"in i&&De(i.value))),a},getValueOnIndex(t,e){const n=t.filter(a=>a.index===e);return n.length?n[0]:null},updateTargetX(t,e){const n=this;t.forEach(a=>{a.values.forEach((i,o)=>{i.x=n.generateTargetX(e[o],a.id,o)}),n.data.xs[a.id]=e})},updateTargetXs(t,e){const n=this;t.forEach(a=>{e[a.id]&&n.updateTargetX([a],e[a.id])})},generateTargetX(t,e,n){const a=this,{axis:i}=a;let o=i!=null&&i.isCategorized()?n:t||n;if(i!=null&&i.isTimeSeries()){const s=Yn.bind(a);o=s(t||a.getXValue(e,n))}else i!=null&&i.isCustomX()&&!(i!=null&&i.isCategorized())&&(o=De(t)?+t:a.getXValue(e,n));return o},updateXs(t){t.length&&(this.axis.xs=t.map(e=>e.x))},getPrevX(t){const e=this.axis.xs[t-1];return Qe(e)?e:null},getNextX(t){const e=this.axis.xs[t+1];return Qe(e)?e:null},getBaseValue(t){const e=this,{hasAxis:n}=e.state;let{value:a}=t;return a&&n&&(e.isAreaRangeType(t)?a=e.getRangedData(t,"mid"):e.isBubbleZType(t)&&(a=e.getBubbleZData(a,"y"))),a},getMinMaxValue(t){const e=this.getBaseValue.bind(this);let n,a;return(t||this.data.targets.map(i=>i.values)).forEach((i,o)=>{const s=i.map(e).filter(he);n=Math.min(o?n:1/0,...s),a=Math.max(o?a:-1/0,...s)}),{min:n,max:a}},getMinMaxData(){const t=this,e=Ln.dataMinMax;let n=t.cache.get(e);if(!n){const a=t.data.targets.map(l=>l.values),i=t.getMinMaxValue(a);let o=[],s=[];a.forEach(l=>{const c=t.getFilteredDataByValue(l,i.min),f=t.getFilteredDataByValue(l,i.max);c.length&&(o=o.concat(c)),f.length&&(s=s.concat(f))}),t.cache.add(e,n={min:o,max:s})}return n},getTotalPerIndex(){const t=this,e=Ln.dataTotalPerIndex;let n=t.cache.get(e);return(t.config.data_groups.length||t.isStackNormalized())&&!n&&(n=[],t.data.targets.forEach(a=>{a.values.forEach((i,o)=>{n[o]||(n[o]=0),n[o]+=he(i.value)?i.value:0})})),n},getTotalDataSum(t){const e=this,n=Ln.dataTotalSum;let a=e.cache.get(n);if(!he(a)){const i=Do(e.data.targets.map(o=>o.values)).map(o=>o.value);a=i.length?i.reduce((o,s)=>o+s):0,e.cache.add(n,a)}return t&&(a-=e.getHiddenTotalDataSum()),a},getHiddenTotalDataSum(){const t=this,{api:e,state:{hiddenTargetIds:n}}=t;let a=0;return n.length&&(a=e.data.values.bind(e)(n).reduce((i,o)=>i+o)),a},getFilteredDataByValue(t,e){return t.filter(n=>this.getBaseValue(n)===e)},getMaxDataCount(){return Math.max(...this.data.targets.map(t=>t.values.length),0)},getMaxDataCountTarget(){let t=this.filterTargetsToShow()||[];const e=t.length,n=this.config.axis_x_inverted;return e>1?(t=t.map(a=>a.values).reduce((a,i)=>a.concat(i)).map(a=>a.x),t=na(Mo(t)).map((a,i,o)=>({x:a,index:n?o.length-i-1:i}))):e&&(t=t[0].values.concat()),t},mapToIds(t){return t.map(e=>e.id)},mapToTargetIds(t){const e=this;return t?je(t)?t.concat():[t]:e.mapToIds(e.data.targets)},hasTarget(t,e){const n=this.mapToIds(t);for(let a=0,i;i=n[a];a++)if(i===e)return!0;return!1},isTargetToShow(t){return this.state.hiddenTargetIds.indexOf(t)<0},isLegendToShow(t){return this.state.hiddenLegendIds.indexOf(t)<0},filterTargetsToShow(t){const e=this;return(t||e.data.targets).filter(n=>e.isTargetToShow(n.id))},mapTargetsToUniqueXs(t){const e=this,{axis:n}=e;let a=[];return t!=null&&t.length&&(a=Mo(Do(t.map(i=>i.values.map(o=>+o.x)))),a=n!=null&&n.isTimeSeries()?a.map(i=>new Date(+i)):a.map(Number)),na(a)},addTargetIds(t,e){const{state:n}=this;(je(e)?e:[e]).forEach(i=>{n[t].indexOf(i)<0&&n[t].push(i)})},removeTargetIds(t,e){const{state:n}=this;(je(e)?e:[e]).forEach(i=>{const o=n[t].indexOf(i);o>=0&&n[t].splice(o,1)})},addHiddenTargetIds(t){this.addTargetIds("hiddenTargetIds",t)},removeHiddenTargetIds(t){this.removeTargetIds("hiddenTargetIds",t)},addHiddenLegendIds(t){this.addTargetIds("hiddenLegendIds",t)},removeHiddenLegendIds(t){this.removeTargetIds("hiddenLegendIds",t)},getValuesAsIdKeyed(t){const e=this,{hasAxis:n}=e.state,a={},i=e.isMultipleX(),o=i?e.mapTargetsToUniqueXs(t).map(s=>ze(s)?s:+s):null;return t.forEach(s=>{const l=[];s.values.filter(({value:c})=>De(c)||c===null).forEach(c=>{let{value:f}=c;f!==null&&e.isCandlestickType(c)&&(f=je(f)?f.slice(0,4):[f.open,f.high,f.low,f.close]),je(f)?l.push(...f):Be(f)&&"high"in f?l.push(...Object.values(f)):e.isBubbleZType(c)?l.push(n&&e.getBubbleZData(f,"y")):i?l[e.getIndexByX(c.x,o)]=f:l.push(f)}),a[s.id]=l}),a},checkValueInTargets(t,e){const n=Object.keys(t);let a;for(let i=0;i1},hasNegativeValueInTargets(t){return this.checkValueInTargets(t,e=>e<0)},hasPositiveValueInTargets(t){return this.checkValueInTargets(t,e=>e>0)},orderTargets(t){const e=this,n=[...t],a=e.getSortCompareFn();return a&&n.sort(a),n},getSortCompareFn(t=!1){const e=this,{config:n}=e,a=n.data_order,i=/asc/i.test(a),o=/desc/i.test(a);let s;if(i||o){const l=(f,g)=>f+Math.abs(g.value),c=f=>he(f)?f:"values"in f?f.values.reduce(l,0):f.value;s=(f,g)=>{const v=c(f),m=c(g);return t?i?v-m:m-v:i?m-v:v-m}}else ve(a)&&(s=a.bind(e.api));return s||null},filterByX(t,e){return Do(t.map(n=>n.values)).filter(n=>n.x-e===0)},filterRemoveNull(t){return t.filter(e=>De(this.getBaseValue(e)))},filterByXDomain(t,e){return t.map(n=>({id:n.id,id_org:n.id_org,values:n.values.filter(a=>e[0]<=a.x&&a.x<=e[1])}))},hasDataLabel(){const t=this.config.data_labels;return Co(t)&&t||nr(t)&&cn(t)},hasNullDataValue(t){return t.some(({value:e})=>e===null)},getDataIndexFromEvent(t){const e=this,{$el:n,config:a,state:{hasRadar:i,inputType:o,eventReceiver:{coords:s,rect:l}}}=e;let c;if(i){let f=t.target;/tspan/i.test(f.tagName)&&(f=f.parentNode);const g=ot(f).datum();c=g&&Object.keys(g).length===1?g.index:void 0}else{const f=a.axis_rotated,g=Zl(n.chart.node()),v=o==="touch"&&t.changedTouches?t.changedTouches[0]:t;let m=f?v.clientY+g.y:v.clientX+g.x;if(Lo(n.svg)){const S=[m,0];f&&S.reverse(),m=Ai(n.eventRect.node(),...S)[f?"y":"x"]}else m-=f?l.top:l.left;c=wo(s,m,0,s.length-1,f)}return c},getDataLabelLength(t,e,n){const a=this,i=[0,0],o=1.3;return a.$el.chart.select("svg").selectAll(".dummy").data([t,e]).enter().append("text").text(s=>a.dataLabelFormat(s.id)(s)).each(function(s,l){i[l]=this.getBoundingClientRect()[n]*o}).remove(),i},isNoneArc(t){return this.hasTarget(this.data.targets,t.id)},isArc(t){return"data"in t&&this.hasTarget(this.data.targets,t.data.id)},findSameXOfValues(t,e){const n=t[e].x,a=[];let i;for(i=e-1;i>=0&&n===t[i].x;i--)a.push(t[i]);for(i=e;in.findClosest(i.values,e));return n.findClosest(a,e)},findClosest(t,e){const n=this,{$el:{main:a}}=n,i=t.filter(l=>l&&De(l.value));let o,s;return i.filter(l=>n.isBarType(l.id)||n.isCandlestickType(l.id)).forEach(l=>{const c=n.isBarType(l.id)?`.${Kn.chartBar}.${Se.target}${n.getTargetSelectorSuffix(l.id)} .${Kn.bar}-${l.index}`:`.${cr.chartCandlestick}.${Se.target}${n.getTargetSelectorSuffix(l.id)} .${cr.candlestick}-${l.index} path`;!s&&n.isWithinBar(a.select(c).node())&&(s=l)}),i.filter(l=>!n.isBarType(l.id)&&!n.isCandlestickType(l.id)).forEach(l=>{const c=n.dist(l,e);o=n.getPointSensitivity(l),c{const{x:i,id:o}=a;n.push({x:i,id:o,value:a.value[0]}),n.push({x:i,id:o,value:a.value[2]})}),n},updateDataAttributes(t,e){const n=this,{config:a}=n,i=a[`data_${t}`];return ln(e)||(Object.keys(e).forEach(o=>{i[o]=e[o]}),n.redraw({withLegend:!0})),i},getRangedData(t,e="",n="areaRange"){const a=t==null?void 0:t.value;if(je(a)){if(n==="bar")return a.reduce((i,o)=>o-i);{const i={areaRange:["high","mid","low"],candlestick:["open","high","low","close","volume"]}[n].indexOf(e);return i>=0&&a?a[i]:void 0}}else if(a&&e)return a[e];return a},setRatioForGroupedData(t){const e=this,{config:n}=e;if(n.data_groups.length&&t.some(a=>e.isGrouped(a.id))){const a=i=>e.getRatio("index",i,!0);t.forEach(i=>{"values"in i?i.values.forEach(a):a(i)})}},getRatio(t,e,n=!1){const a=this,{config:i,state:o}=a,s=a.api;let l=0;if(e&&s.data.shown().length)if(l=e.ratio||e.value,t==="arc")if(a.pie.padAngle()())l=e.value/a.getTotalDataSum(!0);else{const c=i.gauge_fullCircle?a.getArcLength():a.getStartingAngle()*-2,f=a.hasType("gauge")?c:Math.PI*2;l=(e.endAngle-e.startAngle)/f}else if(t==="index"){const c=s.data.values.bind(s);let f=this.getTotalPerIndex();if(o.hiddenTargetIds.length){let v=c(o.hiddenTargetIds,!1);v.length&&(v=v.reduce((m,S)=>m.map((P,N)=>(he(P)?P:0)+S[N])),f=f.map((m,S)=>m-v[S]))}const g=f[e.index];e.ratio=he(e.value)&&f&&g?e.value/g:0,l=e.ratio}else if(t==="radar")l=parseFloat(String(Math.max(e.value,0)))/o.current.dataMax*i.radar_size_ratio;else if(t==="bar"){const f=a.getYScaleById.bind(a)(e.id).domain().reduce((g,v)=>v-g);l=f===0?0:Math.abs(a.getRangedData(e,null,t)/f)}else t==="treemap"&&(l/=a.getTotalDataSum(!0));return n&&l?l*100:l},updateDataIndexByX(t){const e=this,n=t.reduce((a,i,o)=>(a[Number(i.x)]=o,a),{});e.data.targets.forEach(a=>{a.values.forEach((i,o)=>{let s=n[Number(i.x)];s===void 0&&(s=o),i.index=s})})},isBubbleZType(t){return this.isBubbleType(t)&&(Be(t.value)&&("z"in t.value||"y"in t.value)||je(t.value)&&t.value.length>=2)},isBarRangeType(t){const e=this,{value:n}=t;return e.isBarType(t)&&je(n)&&n.length>=2&&n.every(a=>he(a))},getDataById(t){var e;const n=this.cache.get(t)||this.api.data(t);return(e=n==null?void 0:n[0])!=null?e:n}};function cc(t,e=!1){const n=this,{api:a}=n;e&&n.api.flush(!0),t==null||t.call(a)}var Lv={load(t,e){const n=this,{axis:a,data:i,org:o,scale:s}=n,{append:l}=e,c={domain:null,currentDomain:null,x:null};let f=t;f&&(e.filter&&(f=f.filter(e.filter)),(e.type||e.types)&&f.forEach(g=>{var v;const m=((v=e.types)==null?void 0:v[g.id])||e.type;n.setTargetType(g.id,m)}),i.targets.forEach(g=>{for(let v=0;v{const a=t.data||n;t.append&&(a.__append__=!0),a&&e.load(e.convertDataToTargets(a),t)}))},unload(t,e){var n;const a=this,{state:i,$el:o,$T:s}=a,l=!!((n=a.hasLegendDefsPoint)!=null&&n.call(a));let c=e,f=t;if(a.cache.reset(),c||(c=()=>{}),f=f.filter(v=>a.hasTarget(a.data.targets,v)),!f||f.length===0){c();return}const g=o.svg.selectAll(f.map(v=>a.selectorTarget(v)));s(g).style("opacity","0").remove().call(Si,c),f.forEach(v=>{var m;const S=a.getTargetSelectorSuffix(v);i.withoutFadeIn[v]=!1,o.legend&&o.legend.selectAll(`.${We.legendItem}${S}`).remove(),a.data.targets=a.data.targets.filter(P=>P.id!==v),l&&((m=o.defs)==null||m.select(`#${a.getDefsPointId(S)}`).remove())}),i.hasFunnel&&a.updateFunnel(a.data.targets),i.hasTreemap&&a.updateTargetsForTreemap(a.data.targets),a.updateTypesElements()}},Ri=t=>()=>t;function Ho(t,{sourceEvent:e,subject:n,target:a,identifier:i,active:o,x:s,y:l,dx:c,dy:f,dispatch:g}){Object.defineProperties(this,{type:{value:t,enumerable:!0,configurable:!0},sourceEvent:{value:e,enumerable:!0,configurable:!0},subject:{value:n,enumerable:!0,configurable:!0},target:{value:a,enumerable:!0,configurable:!0},identifier:{value:i,enumerable:!0,configurable:!0},active:{value:o,enumerable:!0,configurable:!0},x:{value:s,enumerable:!0,configurable:!0},y:{value:l,enumerable:!0,configurable:!0},dx:{value:c,enumerable:!0,configurable:!0},dy:{value:f,enumerable:!0,configurable:!0},_:{value:g}})}Ho.prototype.on=function(){var t=this._.on.apply(this._,arguments);return t===this._?this:t};function Nv(t){return!t.ctrlKey&&!t.button}function Fv(){return this.parentNode}function Bv(t,e){return e==null?{x:t.x,y:t.y}:e}function Uv(){return navigator.maxTouchPoints||"ontouchstart"in this}function uc(){var t=Nv,e=Fv,n=Bv,a=Uv,i={},o=ri("start","drag","end"),s=0,l,c,f,g,v=0;function m(H){H.on("mousedown.drag",S).filter(a).on("touchstart.drag",L).on("touchmove.drag",w,Jd).on("touchend.drag touchcancel.drag",X).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function S(H,k){if(!(g||!t.call(this,H,k))){var K=W(this,e.call(this,H,k),H,k,"mouse");K&&(ot(H.view).on("mousemove.drag",P,Sa).on("mouseup.drag",N,Sa),co(H.view),lo(H),f=!1,l=H.clientX,c=H.clientY,K("start",H))}}function P(H){if(Zr(H),!f){var k=H.clientX-l,K=H.clientY-c;f=k*k+K*K>v}i.mouse("drag",H)}function N(H){ot(H.view).on("mousemove.drag mouseup.drag",null),uo(H.view,f),Zr(H),i.mouse("end",H)}function L(H,k){if(t.call(this,H,k)){var K=H.changedTouches,at=e.call(this,H,k),ht=K.length,$t,dt;for($t=0;$ti.$el[o]).forEach(o=>{a&&i.$el[o].classed(Se.EXPANDED,!1),i.getShapeByIndex(o,e,n).classed(Se.EXPANDED,t)})},setOverOut(t,e){const n=this,{config:a,state:{hasFunnel:i,hasRadar:o,hasTreemap:s},$el:{main:l}}=n,c=Be(e);if(c||e!==-1){const f=a[t?"data_onover":"data_onout"].bind(n.api);if(a.color_onover&&n.setOverColor(t,e,c),c){const g=n.getTargetSelectorSuffix(e.id),v=i||s?`${Se.target+g} .${sn.shape}`:Ve.arc+g;f(e,l.select(`.${v}`).node())}else if(a.tooltip_grouped)t&&(o&&n.isPointFocusOnly()?n.showCircleFocus(n.getAllValuesOnIndex(e,!0)):n.setExpand(e,null,!0)),!n.isMultipleX()&&l.selectAll(`.${sn.shape}-${e}`).each(function(g){f(g,this)});else{const g=n.cache.get(Ln.setOverOut)||[],v=l.selectAll(`.${sn.shape}-${e}`).filter(function(S){return n.isWithinShape(this,S)}),m=v.filter(function(){return g.every(S=>S!==this)});if(!t||v.empty()||g.length===m.size()&&m.nodes().every((S,P)=>S!==g[P]))for(;g.length;){const S=g.pop();a.data_onout.bind(n.api)(ot(S).datum(),S)}m.each(function(){t&&(f(ot(this).datum(),this),g.push(this))}),n.cache.add(Ln.setOverOut,g)}}},callOverOutForTouch(t){const e=this,n=e.cache.get(Ln.callOverOutForTouch);(Be(t)&&n?t.id!==n.id:t!==n)&&((n||he(n))&&e.setOverOut(!1,n),(t||he(t))&&e.setOverOut(!0,t),e.cache.add(Ln.callOverOutForTouch,t))},getDraggableSelection(){const t=this,{config:e,state:n}=t;return e.interaction_enabled&&e.data_selection_draggable&&t.drag?uc().on("drag",function(a){n.event=a,t.drag(Hn(a,this))}).on("start",function(a){n.event=a,t.dragstart(Hn(a,this))}).on("end",a=>{n.event=a,t.dragend()}):()=>{}},dispatchEvent(t,e,n){var a,i,o;const s=this,{config:l,state:{eventReceiver:c,hasAxis:f,hasFunnel:g,hasRadar:v,hasTreemap:m},$el:{eventRect:S,funnel:P,radar:N,svg:L,treemap:w}}=s;let X=(o=(i=(g||m)&&c.rect||v&&N.axes.select(`.${Tn.axis}-${e} text`)||S||((a=s.getArcElementByIdOrIndex)==null?void 0:a.call(s,e)))==null?void 0:i.node)==null?void 0:o.call(i);if(X){const W=s.isMultipleX(),H=l.axis_rotated;let{width:k,left:K,top:at}=X.getBoundingClientRect();if(f&&!v&&!W){const st=c.coords[e];st?(k=st.w,K+=st.x,at+=st.y):(k=0,K=0,at=0)}let ht=K+(n?n[0]:0)+(W||H?0:k/2),$t=at+(n?n[1]:0)+(H?4:0);if(Lo(L)){const st=Ai(s.$el.eventRect.node(),ht,$t,!1);ht=st.x,$t=st.y}const dt={screenX:ht,screenY:$t,clientX:ht,clientY:$t,bubbles:v};(g||m)&&(X=(P!=null?P:w).node()),ev[/^(mouse|click)/.test(t)?"mouse":"touch"](X,t,dt)}},setDragStatus(t){this.state.dragging=t},unbindZoomEvent(){const t=this,{$el:{eventRect:e,zoomResetBtn:n}}=t;e==null||e.on(".zoom wheel.zoom .drag",null),n==null||n.on("click",null).style("display","none")},unbindAllEvents(){var t;const e=this,{$el:{arcs:n,eventRect:a,legend:i,region:o,svg:s,treemap:l},brush:c}=e,f=["wheel","click","mouseover","mousemove","mouseout","touchstart","touchmove","touchend","touchstart.eventRect","touchmove.eventRect","touchend.eventRect",".brush",".drag",".zoom","wheel.zoom","dblclick.zoom"].join(" ");[s,a,o==null?void 0:o.list,c==null?void 0:c.getSelection(),n==null?void 0:n.selectAll("path"),i==null?void 0:i.selectAll("g"),l].forEach(g=>g==null?void 0:g.on(f,null)),(t=e.unbindZoomEvent)==null||t.call(e)}},jv={categoryName(t){var e;const{axis_x_categories:n}=this.config;return(e=n==null?void 0:n[t])!=null?e:t}},Vv={generateClass(t,e){return` ${t} ${t+this.getTargetSelectorSuffix(e)}`},getClass(t,e){const n=/s$/.test(t),a=/^(area|arc|line|funnel|treemap)s?$/.test(t),i=n?"id":"index";return o=>{const s=o.data||o;return((e?this.generateClass(Ue[n?"shapes":"shape"],s[i]):"")+this.generateClass(Ue[t],s[a?"id":i])).trim()}},getChartClass(t){return e=>Ue[`chart${t}`]+this.classTarget((e.data?e.data:e).id)},generateExtraLineClass(){const e=this.config.line_classes||[],n=[];return function(a){var i;const o=a.id||((i=a.data)==null?void 0:i.id)||a;return n.indexOf(o)<0&&n.push(o),e[n.indexOf(o)%e.length]}},classRegion(t,e){return`${this.generateClass(Ue.region,e)} ${"class"in t?t.class:""}`},classTarget(t){const e=this.config.data_classes[t];let n="";return e&&(n=` ${Ue.target}-${e}`),this.generateClass(Ue.target,t)+n},classFocus(t){return this.classFocused(t)+this.classDefocused(t)},classFocused(t){return` ${this.state.focusedTargetIds.indexOf(t.id)>=0?Ue.focused:""}`},classDefocused(t){return` ${this.state.defocusedTargetIds.indexOf(t.id)>=0?Ue.defocused:""}`},getTargetSelectorSuffix(t){return(t||t===0?`-${t}`:"").replace(/[\x00-\x20\x7F-\xA0\s?!@#$%^&*()_=+,.<>'":;\[\]\/|~`{}\\]/g,"-")},selectorTarget(t,e="",n=""){const a=this.getTargetSelectorSuffix(t);return`${e}.${Ue.target+a} ${n}, ${e}.${Ue.circles+a} ${n}`},selectorTargets(t,e){const n=t||[];return n.length?n.map(a=>this.selectorTarget(a,e)):null},selectorLegend(t){return`.${Ue.legendItem+this.getTargetSelectorSuffix(t)}`},selectorLegends(t){return t!=null&&t.length?t.map(e=>this.selectorLegend(e)):null}};class fc extends Map{constructor(e,n=gc){if(super(),Object.defineProperties(this,{_intern:{value:new Map},_key:{value:n}}),e!=null)for(const[a,i]of e)this.set(a,i)}get(e){return super.get(Yo(this,e))}has(e){return super.has(Yo(this,e))}set(e,n){return super.set(dc(this,e),n)}delete(e){return super.delete(hc(this,e))}}class f1 extends Set{constructor(e,n=gc){if(super(),Object.defineProperties(this,{_intern:{value:new Map},_key:{value:n}}),e!=null)for(const a of e)this.add(a)}has(e){return super.has(Yo(this,e))}add(e){return super.add(dc(this,e))}delete(e){return super.delete(hc(this,e))}}function Yo({_intern:t,_key:e},n){const a=e(n);return t.has(a)?t.get(a):n}function dc({_intern:t,_key:e},n){const a=e(n);return t.has(a)?t.get(a):(t.set(a,n),n)}function hc({_intern:t,_key:e},n){const a=e(n);return t.has(a)&&(n=t.get(a),t.delete(a)),n}function gc(t){return t!==null&&typeof t=="object"?t.valueOf():t}function ra(t,e){switch(arguments.length){case 0:break;case 1:this.range(t);break;default:this.range(e).domain(t);break}return this}function d1(t,e){switch(arguments.length){case 0:break;case 1:{typeof t=="function"?this.interpolator(t):this.range(t);break}default:{this.domain(t),typeof e=="function"?this.interpolator(e):this.range(e);break}}return this}const vc=Symbol("implicit");function pc(){var t=new fc,e=[],n=[],a=vc;function i(o){let s=t.get(o);if(s===void 0){if(a!==vc)return a;t.set(o,s=e.push(o)-1)}return n[s%n.length]}return i.domain=function(o){if(!arguments.length)return e.slice();e=[],t=new fc;for(const s of o)t.has(s)||t.set(s,e.push(s)-1);return i},i.range=function(o){return arguments.length?(n=Array.from(o),i):n.slice()},i.unknown=function(o){return arguments.length?(a=o,i):a},i.copy=function(){return pc(e,n).unknown(a)},ra.apply(i,arguments),i}const Gv=(t,e,n)=>{const a=ot(t.cloneNode(!0));return a.attr("id",n).insert("rect",":first-child").attr("width",a.attr("width")).attr("height",a.attr("height")).style("fill",e),{id:n,node:a.node()}};function Xv(t){const e=Ln.colorPattern,{body:n}=gn;let a=n[e];if(!a){const i=";",o=t.classed(oo.colorPattern,!0).style("background-image");t.classed(oo.colorPattern,!1),o.indexOf(i)>-1&&(a=o.replace(/url[^#]*|["'()]|(\s|%20)/g,"").split(i).map(s=>s.trim().replace(/[\"'\s]/g,"")).filter(Boolean),n[e]=a)}return a}const Hv=["#1f77b4","#ff7f0e","#2ca02c","#d62728","#9467bd","#8c564b","#e377c2","#7f7f7f","#bcbd22","#17becf"];var Yv={generateColor(){const t=this,{$el:e,config:n}=t,a=n.data_colors,i=n.data_color,o=[];let s=cn(n.color_pattern)?n.color_pattern:pc(Xv(e.chart)||Hv).range();const l=s;if(ve(n.color_tiles)){const c=n.color_tiles.bind(t.api)(),f=s.map((g,v)=>{const m=g.replace(/[#\(\)\s,]/g,""),S=`${t.state.datetimeId}-pattern-${m}-${v}`;return Gv(c[v%c.length],g,S)});s=f.map(g=>`url(#${g.id})`),t.patterns=f}return function(c){var f;const g=c.id||((f=c.data)==null?void 0:f.id)||c,v=t.isTypeOf(g,["line","spline","step"])||!n.data_types[g];let m;return ve(a[g])?m=a[g].bind(t.api)(c):a[g]?m=a[g]:(o.indexOf(g)<0&&o.push(g),m=v?l[o.indexOf(g)%l.length]:s[o.indexOf(g)%s.length],a[g]=m),ve(i)?i.bind(t.api)(m,c):m}},generateLevelColor(){const t=this,{config:e}=t,n=e.color_pattern,a=e.color_threshold,i=a.unit==="value",o=a.max||100,s=a.values&&a.values.length?a.values:[];return cn(a)?function(l){const c=i?l:l*100/o;let f=n[n.length-1];for(let g=0,v=s.length;g{const l=`${i.datetimeId}-labels-bg${n.getTargetSelectorSuffix(s)}${ze(t)?n.getTargetSelectorSuffix(t):""}`;a.defs.append("filter").attr("x",e.x).attr("y",e.y).attr("width",e.width).attr("height",e.height).attr("id",l).html(` + `)})}},getGradienColortUrl(t){return`url(#${this.state.datetimeId}-gradient${this.getTargetSelectorSuffix(t)})`},updateLinearGradient(){const t=this,{config:e,data:{targets:n},state:{datetimeId:a},$el:{defs:i}}=t;n.forEach(o=>{const s=`${a}-gradient${t.getTargetSelectorSuffix(o.id)}`,l=t.hasPointType()&&e.point_radialGradient,c=t.isAreaType(o)&&"area"||t.isBarType(o)&&"bar";if((l||c)&&i.select(`#${s}`).empty()){const f=t.color(o),g={defs:null,stops:[]};if(l){const{cx:v=.3,cy:m=.3,r:S=.7,stops:P=[[.1,f,0],[.9,f,1]]}=l;g.stops=P,g.defs=i.append("radialGradient").attr("id",`${s}`).attr("cx",v).attr("cy",m).attr("r",S)}else{const v=e.axis_rotated,{x:m=v?[1,0]:[0,0],y:S=v?[0,0]:[0,1],stops:P=[[0,f,1],[1,f,0]]}=e[`${c}_linearGradient`];g.stops=P,g.defs=i.append("linearGradient").attr("id",`${s}`).attr("x1",m[0]).attr("x2",m[1]).attr("y1",S[0]).attr("y2",S[1])}g.stops.forEach(v=>{const[m,S,P]=v,N=ve(S)?S.bind(t.api)(o.id):S;g.defs&&g.defs.append("stop").attr("offset",m).attr("stop-color",N||f).attr("stop-opacity",P)})}})},setOverColor(t,e){const n=this,{config:a,$el:{main:i}}=n,o=a.color_onover;let s=t?o:n.color;Be(s)?s=({id:l})=>l in o?o[l]:n.color(l):ze(s)?s=()=>o:ve(o)&&(s=s.bind(n.api)),i.selectAll(Be(e)?`.${Ve.arc}${n.getTargetSelectorSuffix(e.id)}`:`.${sn.shape}-${e}`).style("fill",s)}},Wv={getYDomainMinMax(t,e){const n=this,{axis:a,config:i}=n,o=e==="min",s=i.data_groups,l=n.mapToIds(t),c=n.getValuesAsIdKeyed(t);if(s.length>0){const f=n[`has${o?"Negative":"Positive"}ValueInTargets`](t);s.forEach(g=>{const v=g.filter(m=>l.indexOf(m)>=0);if(v.length){const m=v[0],S=a.getId(m);f&&c[m]&&(c[m]=c[m].map(P=>(o?P<0:P>0)?P:0)),v.filter((P,N)=>N>0).forEach(P=>{if(c[P]){const N=a.getId(P);c[P].forEach((L,w)=>{const X=+L,W=o?X>0:X<0;N===S&&!(f&&W)&&(c[m][w]+=X)})}})}})}return _n(e,Object.keys(c).map(f=>_n(e,c[f])))},isHiddenTargetWithYDomain(t){const e=this;return e.state.hiddenTargetIds.some(n=>e.axis.getId(n)===t)},getYDomain(t,e,n){const a=this,{axis:i,config:o,scale:s}=a,l=`axis_${e}`;if(a.isStackNormalized())return[0,100];const c=(s==null?void 0:s[e])&&s[e].type==="log",f=t.filter(dt=>i.getId(dt.id)===e),g=n?a.filterByXDomain(f,n):f;if(g.length===0)return a.isHiddenTargetWithYDomain(e)?s[e].domain():e==="y2"?s.y.domain():a.getYDomain(t,"y2",n);const v=o[`${l}_min`],m=o[`${l}_max`],S=o[`${l}_center`],P=o[`${l}_inverted`],N=a.hasDataLabel()&&o.axis_rotated,L=a.hasDataLabel()&&!o.axis_rotated;let w=a.getYDomainMinMax(g,"min"),X=a.getYDomainMinMax(g,"max"),W=[oe.BAR,oe.BUBBLE,oe.SCATTER,...Sr.Line].some(dt=>{const st=dt.indexOf("area")>-1?"area":dt;return a.hasType(dt,g,!0)&&o[`${st}_zerobased`]});w=De(v)?v:De(m)?w<=m?w:m-10:w,X=De(m)?m:De(v)?v<=X?X:v+10:X,isNaN(w)&&(w=0),isNaN(X)&&(X=w),w===X&&(w<0?X=0:w=0);const H=w>=0&&X>=0,k=w<=0&&X<=0;(De(v)&&H||De(m)&&k)&&(W=!1),W&&(H&&(w=0),k&&(X=0));const K=Math.abs(X-w);let at={top:K*.1,bottom:K*.1};if(Qe(S)){const dt=Math.max(Math.abs(w),Math.abs(X));X=S+dt,w=S-dt}if(N){const dt=Dr(s.y.range()),st=a.getDataLabelLength(w,X,"width").map(Vt=>Vt/dt);["bottom","top"].forEach((Vt,vt)=>{at[Vt]+=K*(st[vt]/(1-st[0]-st[1]))})}else if(L){const dt=a.getDataLabelLength(w,X,"height");["bottom","top"].forEach((st,Vt)=>{at[st]+=a.convertPixelToScale("y",dt[Vt],K)})}at=a.getResettedPadding(at);const ht=o[`${l}_padding`];cn(ht)&&["bottom","top"].forEach(dt=>{at[dt]=i.getPadding(ht,dt,at[dt],K)}),W&&(H&&(at.bottom=w),k&&(at.top=-X));const $t=c?[w,X].map(dt=>dt<0?0:dt):[w-at.bottom,X+at.top];return P?$t.reverse():$t},getXDomainMinMax(t,e){var n;const a=this,i=a.config[`axis_x_${e}`],o=_n(e,t.map(l=>_n(e,l.values.map(c=>c.x))));let s=Be(i)?i.value:i;return s=Qe(s)&&((n=a.axis)!=null&&n.isTimeSeries())?Yn.bind(this)(s):s,Be(i)&&i.fit&&(e==="min"&&so)&&(s=void 0),Qe(s)?s:o},getXDomainPadding(t,e){const n=this,{axis:a,config:i}=n,o=i.axis_x_padding,s=a.isTimeSeries()&&e,l=Dr(t);let c;if(a.isCategorized()||s)c=0;else if(n.hasType("bar")){const v=n.getMaxDataCount();c=v>1?l/(v-1)/2:.5}else c=n.getResettedPadding(l*.01);let{left:f=c,right:g=c}=he(o)?{left:o,right:o}:o;if(o.unit==="px"){const v=Math.abs(l+l*.2);f=a.getPadding(o,"left",c,v),g=a.getPadding(o,"right",c,v)}else{const v=l+f+g;if(s&&v){const m=l/e/v;f=f/v/m,g=g/v/m}}return{left:f,right:g}},getXDomain(t){const e=this,{axis:n,config:a,scale:{x:i}}=e,o=a.axis_x_inverted,s=[e.getXDomainMinMax(t,"min"),e.getXDomainMinMax(t,"max")];let[l=0,c=0]=s;if(i.type!=="log"){const f=n.isCategorized(),g=n.isTimeSeries(),v=e.getXDomainPadding(s);let[m,S]=s;m-S===0&&!f&&(g?(m=new Date(m.getTime()*.5),S=new Date(S.getTime()*1.5)):(m=m===0?1:m*.5,S=S===0?-1:S*1.5)),(m||m===0)&&(l=g?new Date(m.getTime()-v.left):m-v.left),(S||S===0)&&(c=g?new Date(S.getTime()+v.right):S+v.right)}return o?[c,l]:[l,c]},updateXDomain(t,e,n,a,i){var o;const s=this,{config:l,org:c,scale:{x:f,subX:g}}=s,v=l.zoom_enabled;if(n&&(f.domain(i||na(s.getXDomain(t),!l.axis_x_inverted)),c.xDomain=f.domain(),g.domain(f.domain()),(o=s.brush)==null||o.scale(g)),e){const m=i||!s.brush||Kl(s)?c.xDomain:Wl(s).map(g.invert);f.domain(m)}return(n||e)&&v&&s.zoom.updateScaleExtent(),a&&f.domain(s.trimXDomain(f.orgDomain())),f.domain()},trimXDomain(t){const e=this,n=e.config.axis_x_inverted,a=e.getZoomDomain(),[i,o]=a;return(n?t[0]>=i:t[0]<=i)&&(t[1]=+t[1]+(i-t[0]),t[0]=i),(n?t[1]<=o:t[1]>=o)&&(t[0]=+t[0]-(t[1]-o),t[1]=o),t},getZoomDomain(t="zoom",e=!1){const n=this,{config:a,scale:i,org:o}=n;let[s,l]=e&&i[t]?i[t].domain():o.xDomain;return t==="zoom"&&(Qe(a.zoom_x_min)&&(s=_n("min",[s,a.zoom_x_min])),Qe(a.zoom_x_max)&&(l=_n("max",[l,a.zoom_x_max]))),[s,l]},getZoomDomainValue(t){const e=this,{config:n,axis:a}=e;if(a.isCategorized()&&Array.isArray(t)){const i=n.axis_x_inverted;return t.map((s,l)=>Number(s)+(l===0?+i:+!i))}return t},convertPixelToScale(t,e,n){const a=this,{config:i,state:o}=a,s=i.axis_rotated;let l;return t==="x"?l=s?"height":"width":l=s?"width":"height",n*(e/o[l])},withinRange(t,e=[0,0],n){const i=this.config.axis_x_inverted,[o,s]=n;if(Array.isArray(t)){const l=[...t];if(i&&l.reverse(),l[0](f===0?i?+c<=o:+c>=o:i?+c>=s:+c<=s)&&!t.every((g,v)=>g===e[v]))}return!1}};function mc(t,e,n){const{config:a}=t,i=`axis_${e}_tick_format`;return(a[i]?a[i]:t.defaultValueFormat).call(t.api,n)}var Kv={yFormat(t){return mc(this,"y",t)},y2Format(t){return mc(this,"y2",t)},getDefaultValueFormat(){const t=this,{defaultArcValueFormat:e,yFormat:n,y2Format:a}=t,i=t.hasArcType(null,["gauge","polar","radar"]);return function(o,s,l){return(i?e:t.axis&&t.axis.getId(l)==="y2"?a:n).call(t,o,s)}},defaultValueFormat(t){return je(t)?t.join("~"):De(t)?+t:""},defaultArcValueFormat(t,e){return`${(e*100).toFixed(1)}%`},defaultPolarValueFormat(t){return`${t}`},dataLabelFormat(t){const e=this,n=e.config.data_labels,a=o=>{const s="~";let l=o;return je(o)?l=o.join(s):Be(o)&&(l=Object.values(o).join(s)),l};let i=a;return ve(n.format)?i=n.format:nr(n.format)&&(n.format[t]?i=n.format[t]===!0?a:n.format[t]:i=()=>""),i.bind(e.api)}};function Ii(t){const e=this,n=e.getDataById(t);return e.levelColor?e.levelColor(n.values[0].value):e.color(n)}function Wo(t,e=!0){var n;const{config:a}=this;let i=(n=a.data_names[t])!=null?n:t;return e&&ve(a.legend_format)&&(i=a.legend_format(i,t!==i?t:void 0)),i}var Zv={initLegend(){const t=this,{config:e,$el:n}=t;t.legendItemTextBox={},t.state.legendHasRendered=!1,e.legend_show?(e.legend_contents_bindto||(n.legend=t.$el.svg.append("g").classed(We.legend,!0).attr("transform",t.getTranslate("legend"))),t.updateLegend()):t.state.hiddenLegendIds=t.mapToIds(t.data.targets)},updateLegend(t,e,n){var a;const i=this,{config:o,state:s,scale:l,$el:c}=i,f=e||{withTransform:!1,withTransitionForTransform:!1,withTransition:!1};f.withTransition=$r(f,"withTransition",!0),f.withTransitionForTransform=$r(f,"withTransitionForTransform",!0),o.legend_contents_bindto&&o.legend_contents_template?i.updateLegendTemplate():s.hasTreemap||i.updateLegendElement(t||i.mapToIds(i.data.targets),f,n),(a=c.legend)==null||a.selectAll(`.${We.legendItem}`).classed(We.legendItemHidden,function(g){const v=!i.isTargetToShow(g);return v&&(this.style.opacity=null),v}),i.updateScales(!1,!l.zoom),i.updateSvgSize(),i.transformAll(f.withTransitionForTransform,n),s.legendHasRendered=!0},updateLegendTemplate(){const t=this,{config:e,$el:n}=t,a=ot(e.legend_contents_bindto),i=e.legend_contents_template;if(!a.empty()){const o=t.mapToIds(t.data.targets),s=[];let l="";o.forEach(f=>{const g=ve(i)?i.bind(t.api)(f,t.color(f),t.api.data(f)[0].values):bi(i,{COLOR:t.color(f),TITLE:f});g&&(s.push(f),l+=g)});const c=a.html(l).selectAll(function(){return this.childNodes}).data(s);t.setLegendItem(c),n.legend=a}},updateSizeForLegend(t){const e=this,{config:n,state:{isLegendTop:a,isLegendLeft:i,isLegendRight:o,isLegendInset:s,current:l}}=e,{width:c,height:f}=t,g={top:a?e.getCurrentPaddingByDirection("top")+n.legend_inset_y+5.5:l.height-f-e.getCurrentPaddingByDirection("bottom")-n.legend_inset_y,left:i?e.getCurrentPaddingByDirection("left")+n.legend_inset_x+.5:l.width-c-e.getCurrentPaddingByDirection("right")-n.legend_inset_x+.5};e.state.margin3={top:o?0:s?g.top:l.height-f,right:NaN,bottom:0,left:o?l.width-c:s?g.left:0}},transformLegend(t){const e=this,{$el:{legend:n},$T:a}=e;a(n,t).attr("transform",e.getTranslate("legend"))},updateLegendStep(t){this.state.legendStep=t},updateLegendItemWidth(t){this.state.legendItemWidth=t},updateLegendItemHeight(t){this.state.legendItemHeight=t},updateLegendItemColor(t,e){const{legend:n}=this.$el;n&&n.select(`.${We.legendItem}-${t} line`).style("stroke",e)},getLegendWidth(){const t=this,{current:{width:e},isLegendRight:n,isLegendInset:a,legendItemWidth:i,legendStep:o}=t.state;return t.config.legend_show?n||a?i*(o+1):e:0},getLegendHeight(){var t;const e=this,{current:n,isLegendRight:a,legendItemHeight:i,legendStep:o}=e.state,s=((t=e.config.padding)==null?void 0:t.mode)==="fit";return e.config.legend_show?a?n.height:Math.max(s?10:20,i)*(o+1):0},opacityForUnfocusedLegend(t){return t.classed(We.legendItemHidden)?null:"0.3"},toggleFocusLegend(t,e){const n=this,{$el:{legend:a},$T:i}=n,o=n.mapToTargetIds(t);a&&i(a.selectAll(`.${We.legendItem}`).filter(s=>o.indexOf(s)>=0).classed(qe.legendItemFocused,e)).style("opacity",function(){return e?null:n.opacityForUnfocusedLegend.call(n,ot(this))})},revertLegend(){const t=this,{$el:{legend:e},$T:n}=t;e&&n(e.selectAll(`.${We.legendItem}`).classed(qe.legendItemFocused,!1)).style("opacity",null)},showLegend(t){const e=this,{config:n,$el:a,$T:i}=e;n.legend_show||(n.legend_show=!0,a.legend?a.legend.style("visibility",null):e.initLegend(),!e.state.legendHasRendered&&e.updateLegend()),e.removeHiddenLegendIds(t),i(a.legend.selectAll(e.selectorLegends(t)).style("visibility",null)).style("opacity",null)},hideLegend(t){const e=this,{config:n,$el:{legend:a}}=e;n.legend_show&&qn(t)&&(n.legend_show=!1,a.style("visibility","hidden")),e.addHiddenLegendIds(t),a.selectAll(e.selectorLegends(t)).style("opacity","0").style("visibility","hidden")},getLegendItemTextBox(t,e){const n=this,{cache:a,state:i}=n;let o;const s=Ln.legendItemTextBox;return t&&(o=!i.redrawing&&a.get(s)||{},o[t]||(o[t]=n.getTextRect(e,We.legendItem),a.add(s,o)),o=o[t]),o},setLegendItem(t){const e=this,{$el:n,api:a,config:i,state:o}=e,s=o.inputType==="touch",l=e.hasType("gauge"),c=i.boost_useCssRule,f=i.legend_item_interaction;t.attr("class",function(g){const v=ot(this);return(!v.empty()&&v.attr("class")||"")+e.generateClass(We.legendItem,g)}).style("visibility",g=>e.isLegendToShow(g)?null:"hidden"),i.interaction_enabled&&(c&&[[`.${We.legendItem}`,"cursor:pointer"],[`.${We.legendItem} text`,"pointer-events:none"],[`.${We.legendItemPoint} text`,"pointer-events:none"],[`.${We.legendItemTile}`,"pointer-events:none"],[`.${We.legendItemEvent}`,"fill-opacity:0"]].forEach(g=>{const[v,m]=g;e.setCssRule(!1,v,[m])(n.legend)}),t.on(f.dblclick?"dblclick":"click",f||ve(i.legend_item_onclick)?function(g,v){if(!_e(i.legend_item_onclick,a,v,!o.hiddenTargetIds.includes(v))){const{altKey:m,target:S,type:P}=g;P==="dblclick"||m?o.hiddenTargetIds.length&&S.parentNode.getAttribute("class").indexOf(We.legendItemHidden)===-1?a.show():(a.hide(),a.show(v)):(a.toggle(v),ot(this).classed(qe.legendItemFocused,!1))}s&&e.hideTooltip()}:null),!s&&t.on("mouseout",f||ve(i.legend_item_onout)?function(g,v){_e(i.legend_item_onout,a,v,!o.hiddenTargetIds.includes(v))||(ot(this).classed(qe.legendItemFocused,!1),l&&e.undoMarkOverlapped(e,`.${Un.gaugeValue}`),e.api.revert())}:null).on("mouseover",f||ve(i.legend_item_onover)?function(g,v){_e(i.legend_item_onover,a,v,!o.hiddenTargetIds.includes(v))||(ot(this).classed(qe.legendItemFocused,!0),l&&e.markOverlapped(v,e,`.${Un.gaugeValue}`),!o.transiting&&e.isTargetToShow(v)&&a.focus(v))}:null),!t.empty()&&t.on("click mouseout mouseover")&&t.style("cursor",e.getStylePropValue("pointer")))},updateLegendElement(t,e){const n=this,{config:a,state:i,$el:{legend:o},$T:s}=n,c=a.legend_item_tile_type!=="circle",f=a.legend_item_tile_r,g={width:c?a.legend_item_tile_width:f*2,height:c?a.legend_item_tile_height:f*2},v={padding:{top:4,right:10},max:{width:0,height:0},posMin:10,step:0,tileWidth:g.width+5,totalLength:0},m={offsets:{},widths:{},heights:{},margins:[0],steps:{}};let S,P,N;const L=t.filter(K=>!Qe(a.data_names[K])||a.data_names[K]!==null),w=e.withTransition,X=n.getUpdateLegendPositions(L,v,m);i.isLegendInset&&(v.step=a.legend_inset_step?a.legend_inset_step:L.length,n.updateLegendStep(v.step)),i.isLegendRight?(S=K=>v.max.width*m.steps[K],P=K=>m.margins[m.steps[K]]+m.offsets[K]):i.isLegendInset?(S=K=>v.max.width*m.steps[K]+10,P=K=>m.margins[m.steps[K]]+m.offsets[K]):(S=K=>m.margins[m.steps[K]]+m.offsets[K],P=K=>v.max.height*m.steps[K]);const W={xText:(K,at)=>S(K,at)+4+g.width,xRect:(K,at)=>S(K,at),x1Tile:(K,at)=>S(K,at)-2,x2Tile:(K,at)=>S(K,at)-2+g.width,yText:(K,at)=>P(K,at)+9,yRect:(K,at)=>P(K,at)-5,yTile:(K,at)=>P(K,at)+4};n.generateLegendItem(L,g,X,W),N=o.select(`.${We.legendBackground} rect`),i.isLegendInset&&v.max.width>0&&N.size()===0&&(N=o.insert("g",`.${We.legendItem}`).attr("class",We.legendBackground).append("rect")),a.legend_tooltip&&o.selectAll("title").data(L).text(K=>Wo.bind(n)(K,!1));const H=o.selectAll("text").data(L).text(K=>Wo.bind(n)(K)).each(function(K,at){X(this,K,at)});s(H,w).attr("x",W.xText).attr("y",W.yText);const k=o.selectAll(`rect.${We.legendItemEvent}`).data(L);s(k,w).attr("width",K=>m.widths[K]).attr("height",K=>m.heights[K]).attr("x",W.xRect).attr("y",W.yRect),n.updateLegendItemPos(L,w,W),N&&s(N,w).attr("height",n.getLegendHeight()-12).attr("width",v.max.width*(v.step+1)+10),n.updateLegendItemWidth(v.max.width),n.updateLegendItemHeight(v.max.height),n.updateLegendStep(v.step)},getUpdateLegendPositions(t,e,n){const a=this,{config:i,state:o}=a,s=o.isLegendRight||o.isLegendInset;return function(l,c,f){const g=f===0,v=f===t.length-1,m=a.getLegendItemTextBox(c,l),S=m.width+e.tileWidth+(v&&!s?0:e.padding.right)+i.legend_padding,P=m.height+e.padding.top,N=s?P:S,L=s?a.getLegendHeight():a.getLegendWidth();let w;const X=function(H,k){k||(w=(L-e.totalLength-N)/2,w=e.max.width)&&(e.max.width=S),(!e.max.height||P>=e.max.height)&&(e.max.height=P);const W=s?e.max.height:e.max.width;i.legend_equally?(Object.keys(n.widths).forEach(H=>n.widths[H]=e.max.width),Object.keys(n.heights).forEach(H=>n.heights[H]=e.max.height),w=(L-W*t.length)/2,wX(H))):X(c,!0)):X(c)}},generateLegendItem(t,e,n,a){const i=this,{config:o,state:s,$el:{legend:l}}=i,c=o.legend_usePoint,f=o.legend_item_tile_r,g=o.legend_item_tile_type,v=g!=="circle",m=s.isLegendRight||s.isLegendInset,S=-200,P=l.selectAll(`.${We.legendItem}`).data(t).enter().append("g");if(i.setLegendItem(P),o.legend_tooltip&&P.append("title").text(N=>N),P.append("text").text(N=>Wo.bind(i)(N)).each(function(N,L){n(this,N,L)}).style("pointer-events",i.getStylePropValue("none")).attr("x",m?a.xText:S).attr("y",m?S:a.yText),P.append("rect").attr("class",We.legendItemEvent).style("fill-opacity",i.getStylePropValue("0")).attr("x",m?a.xRect:S).attr("y",m?S:a.yRect),c){const N=[];P.append(L=>{const w=cn(o.point_pattern)?o.point_pattern:[o.point_type];N.indexOf(L)===-1&&N.push(L);let X=w[N.indexOf(L)%w.length];return X==="rectangle"&&(X="rect"),gn.createElementNS(ae.svg,"hasValidPointType"in i&&i.hasValidPointType(X)?X:"use")}).attr("class",We.legendItemPoint).style("fill",Ii.bind(i)).style("pointer-events",i.getStylePropValue("none")).attr("href",(L,w,X)=>{const H=X[w].nodeName.toLowerCase(),k=i.getTargetSelectorSuffix(L);return H==="use"?`#${s.datetimeId}-point${k}`:void 0})}else P.append(v?"line":g).attr("class",We.legendItemTile).style("stroke",Ii.bind(i)).style("pointer-events",i.getStylePropValue("none")).call(N=>{g==="circle"?N.attr("r",f).style("fill",Ii.bind(i)).attr("cx",m?a.x2Tile:S).attr("cy",m?S:a.yTile):v&&N.attr("stroke-width",e.height).attr("x1",m?a.x1Tile:S).attr("y1",m?S:a.yTile).attr("x2",m?a.x2Tile:S).attr("y2",m?S:a.yTile)})},updateLegendItemPos(t,e,n){const a=this,{config:i,$el:{legend:o},$T:s}=a,l=i.legend_usePoint,c=i.legend_item_tile_type,f=c!=="circle";if(l){const g=o.selectAll(`.${We.legendItemPoint}`).data(t);s(g,e).each(function(){const v=this.nodeName.toLowerCase(),m=i.point_r;let S="x",P="y",N=2,L=2.5,w=null,X=null,W=null;if(v==="circle"){const H=m*.2;S="cx",P="cy",w=m+H,N=m*2,L=-H}else if(v==="rect"){const H=m*2.5;X=H,W=H,L=3}ot(this).attr(S,H=>n.x1Tile(H)+N).attr(P,H=>n.yTile(H)-L).attr("r",w).attr("width",X).attr("height",W)})}else{const g=o.selectAll(`.${We.legendItemTile}`).data(t);s(g,e).style("stroke",Ii.bind(a)).call(v=>{c==="circle"?v.attr("cx",m=>{const S=n.x2Tile(m);return S-(S-n.x1Tile(m))/2}).attr("cy",n.yTile):f&&v.attr("x1",n.x1Tile).attr("y1",n.yTile).attr("x2",n.x2Tile).attr("y2",n.yTile)})}}},Jv={redraw(t={}){var e,n,a,i;const o=this,{config:s,state:l,$el:c}=o,{main:f,treemap:g}=c;l.redrawing=!0;const v=o.filterTargetsToShow(o.data.targets),{flow:m,initializing:S}=t,P=o.getWithOption(t),N=P.Transition?s.transition_duration:0,L=P.TransitionForExit?N:0,w=P.TransitionForAxis?N:0,X=(e=o.axis)==null?void 0:e.generateTransitions(w);o.updateSizes(S),P.Legend&&s.legend_show?(t.withTransition=!!N,!g&&o.updateLegend(o.mapToIds(o.data.targets),t,X)):P.Dimension&&o.updateDimension(!0),s.data_empty_label_text&&f.select(`text.${On.text}.${Se.empty}`).attr("x",l.width/2).attr("y",l.height/2).text(s.data_empty_label_text).style("display",v.length?"none":null),l.hasAxis?(o.axis.redrawAxis(v,P,X,m,S),o.hasGrid()&&o.updateGrid(),s.regions.length&&o.updateRegion(),["bar","candlestick","line","area"].forEach(W=>{const H=Cn(W);(/^(line|area)$/.test(W)&&o.hasTypeOf(H)||o.hasType(W))&&o[`update${H}`](P.TransitionForExit)}),c.text&&f.selectAll(`.${tn.selectedCircles}`).filter(o.isBarType.bind(o)).selectAll("circle").remove(),s.interaction_enabled&&!m&&P.EventRect&&(o.redrawEventRect(),(n=o.bindZoomEvent)==null||n.call(o))):(c.arcs&&o.redrawArc(N,L,P.Transform),c.radar&&o.redrawRadar(),c.polar&&o.redrawPolar(),c.funnel&&o.redrawFunnel(),g&&o.updateTreemap(L)),!l.resizing&&!g&&(o.hasPointType()||l.hasRadar)?o.updateCircle():(a=o.hasLegendDefsPoint)!=null&&a.call(o)&&o.data.targets.forEach(o.point("create",this)),o.hasDataLabel()&&!o.hasArcType(null,["radar"])&&o.updateText(),(i=o.redrawTitle)==null||i.call(o),S&&o.updateTypesElements(),o.generateRedrawList(v,m,N,P.Subchart),o.updateTooltipOnRedraw(),o.callPluginHook("$redraw",t,N)},generateRedrawList(t,e,n,a){const i=this,{config:o,state:s}=i,l=i.getDrawShape();s.hasAxis&&o.subchart_show&&i.redrawSubchart(a,n,l);const c=e&&i.generateFlow({targets:t,flow:e,duration:e.duration,shape:l,xv:i.xv.bind(i)}),f=(n||c)&&Da(),g=i.getRedrawList(l,e,c,f),v=()=>{c&&c(),s.redrawing=!1,_e(o.onrendered,i.api)};if(v)if(f&&g.length){const m=ec();Ml().duration(n).each(()=>{g.reduce((S,P)=>S.concat(P),[]).forEach(S=>m.add(S))}).call(m,v)}else s.transiting||v();i.mapToIds(i.data.targets).forEach(m=>{s.withoutFadeIn[m]=!0})},getRedrawList(t,e,n,a){const i=this,{config:o,state:{hasAxis:s,hasRadar:l,hasTreemap:c},$el:{grid:f}}=i,{cx:g,cy:v,xForText:m,yForText:S}=t.pos,P=[];return s&&((o.grid_x_lines.length||o.grid_y_lines.length)&&P.push(i.redrawGrid(a)),o.regions.length&&P.push(i.redrawRegion(a)),Object.keys(t.type).forEach(N=>{const L=Cn(N),w=t.type[N];(/^(area|line)$/.test(N)&&i.hasTypeOf(L)||i.hasType(N))&&P.push(i[`redraw${L}`](w,a))}),!e&&f.main&&P.push(i.updateGridFocus())),(!i.hasArcType()||l)&&cn(o.data_labels)&&o.data_labels!==!1&&P.push(i.redrawText(m,S,e,a)),(i.hasPointType()||l)&&!i.isPointFocusOnly()&&i.redrawCircle&&P.push(i.redrawCircle(g,v,a,n)),c&&P.push(i.redrawTreemap(a)),P},updateAndRedraw(t={}){const e=this,{config:n,state:a}=e;let i;t.withTransition=$r(t,"withTransition",!0),t.withTransform=$r(t,"withTransform",!1),t.withLegend=$r(t,"withLegend",!1),t.withUpdateXDomain=!0,t.withUpdateOrgXDomain=!0,t.withTransitionForExit=!1,t.withTransitionForTransform=$r(t,"withTransitionForTransform",t.withTransition),t.withLegend&&n.legend_show||(a.hasAxis&&(i=e.axis.generateTransitions(t.withTransitionForAxis?n.transition_duration:0)),e.updateScales(),e.updateSvgSize(),e.transformAll(t.withTransitionForTransform,i)),e.redraw(t,i)}};const Qv=Math.sqrt(50),kv=Math.sqrt(10),qv=Math.sqrt(2);function Oi(t,e,n){const a=(e-t)/Math.max(0,n),i=Math.floor(Math.log10(a)),o=a/Math.pow(10,i),s=o>=Qv?10:o>=kv?5:o>=qv?2:1;let l,c,f;return i<0?(f=Math.pow(10,-i)/s,l=Math.round(t*f),c=Math.round(e*f),l/fe&&--c,f=-f):(f=Math.pow(10,i)*s,l=Math.round(t/f),c=Math.round(e/f),l*fe&&--c),c0))return[];if(t===e)return[t];const a=e=i))return[];const l=o-i+1,c=new Array(l);if(a)if(s<0)for(let f=0;fe?1:t>=e?0:NaN}function _v(t,e){return t==null||e==null?NaN:et?1:e>=t?0:NaN}function Qo(t){let e,n,a;t.length!==2?(e=Ci,n=(l,c)=>Ci(t(l),c),a=(l,c)=>t(l)-c):(e=t===Ci||t===_v?t:tp,n=t,a=t);function i(l,c,f=0,g=l.length){if(f>>1;n(l[v],c)<0?f=v+1:g=v}while(f>>1;n(l[v],c)<=0?f=v+1:g=v}while(ff&&a(l[v-1],c)>-a(l[v],c)?v-1:v}return{left:i,center:s,right:o}}function tp(){return 0}function ep(t){return t===null?NaN:+t}function*h1(t,e){if(e===void 0)for(let n of t)n!=null&&(n=+n)>=n&&(yield n);else{let n=-1;for(let a of t)(a=e(a,++n,t))!=null&&(a=+a)>=a&&(yield a)}}const yc=Qo(Ci),np=yc.right,g1=yc.left,v1=Qo(ep).center;var rp=np;function ap(t,e){return t=+t,e=+e,function(n){return Math.round(t*(1-n)+e*n)}}function ip(t){return function(){return t}}function op(t){return+t}var xc=[0,1];function aa(t){return t}function ko(t,e){return(e-=t=+t)?function(n){return(n-t)/e}:ip(isNaN(e)?NaN:.5)}function sp(t,e){var n;return t>e&&(n=t,t=e,e=n),function(a){return Math.max(t,Math.min(e,a))}}function lp(t,e,n){var a=t[0],i=t[1],o=e[0],s=e[1];return i2?cp:lp,c=f=null,v}function v(m){return m==null||isNaN(m=+m)?o:(c||(c=l(t.map(a),e,n)))(a(s(m)))}return v.invert=function(m){return s(i((f||(f=l(e,t.map(a),Qn)))(m)))},v.domain=function(m){return arguments.length?(t=Array.from(m,op),g()):t.slice()},v.range=function(m){return arguments.length?(e=Array.from(m),g()):e.slice()},v.rangeRound=function(m){return e=Array.from(m),n=ap,g()},v.clamp=function(m){return arguments.length?(s=m?!0:aa,g()):s!==aa},v.interpolate=function(m){return arguments.length?(n=m,g()):n},v.unknown=function(m){return arguments.length?(o=m,v):o},function(m,S){return a=m,i=S,g()}}function Tc(){return qo()(aa,aa)}var up=/^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;function Na(t){if(!(e=up.exec(t)))throw new Error("invalid format: "+t);var e;return new _o({fill:e[1],align:e[2],sign:e[3],symbol:e[4],zero:e[5],width:e[6],comma:e[7],precision:e[8]&&e[8].slice(1),trim:e[9],type:e[10]})}Na.prototype=_o.prototype;function _o(t){this.fill=t.fill===void 0?" ":t.fill+"",this.align=t.align===void 0?">":t.align+"",this.sign=t.sign===void 0?"-":t.sign+"",this.symbol=t.symbol===void 0?"":t.symbol+"",this.zero=!!t.zero,this.width=t.width===void 0?void 0:+t.width,this.comma=!!t.comma,this.precision=t.precision===void 0?void 0:+t.precision,this.trim=!!t.trim,this.type=t.type===void 0?"":t.type+""}_o.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(this.width===void 0?"":Math.max(1,this.width|0))+(this.comma?",":"")+(this.precision===void 0?"":"."+Math.max(0,this.precision|0))+(this.trim?"~":"")+this.type};function fp(t){return Math.abs(t=Math.round(t))>=1e21?t.toLocaleString("en").replace(/,/g,""):t.toString(10)}function wi(t,e){if((n=(t=e?t.toExponential(e-1):t.toExponential()).indexOf("e"))<0)return null;var n,a=t.slice(0,n);return[a.length>1?a[0]+a.slice(2):a,+t.slice(n+1)]}function ia(t){return t=wi(Math.abs(t)),t?t[1]:NaN}function dp(t,e){return Math.max(0,Math.max(-8,Math.min(8,Math.floor(ia(e)/3)))*3-ia(Math.abs(t)))}function hp(t,e){return function(n,a){for(var i=n.length,o=[],s=0,l=t[0],c=0;i>0&&l>0&&(c+l+1>a&&(l=Math.max(1,a-c)),o.push(n.substring(i-=l,i+l)),!((c+=l+1)>a));)l=t[s=(s+1)%t.length];return o.reverse().join(e)}}function gp(t){return function(e){return e.replace(/[0-9]/g,function(n){return t[+n]})}}function vp(t){t:for(var e=t.length,n=1,a=-1,i;n0&&(a=0);break}return a>0?t.slice(0,a)+t.slice(i+1):t}var $c;function pp(t,e){var n=wi(t,e);if(!n)return t+"";var a=n[0],i=n[1],o=i-($c=Math.max(-8,Math.min(8,Math.floor(i/3)))*3)+1,s=a.length;return o===s?a:o>s?a+new Array(o-s+1).join("0"):o>0?a.slice(0,o)+"."+a.slice(o):"0."+new Array(1-o).join("0")+wi(t,Math.max(0,e+o-1))[0]}function Sc(t,e){var n=wi(t,e);if(!n)return t+"";var a=n[0],i=n[1];return i<0?"0."+new Array(-i).join("0")+a:a.length>i+1?a.slice(0,i+1)+"."+a.slice(i+1):a+new Array(i-a.length+2).join("0")}var Ac={"%":(t,e)=>(t*100).toFixed(e),b:t=>Math.round(t).toString(2),c:t=>t+"",d:fp,e:(t,e)=>t.toExponential(e),f:(t,e)=>t.toFixed(e),g:(t,e)=>t.toPrecision(e),o:t=>Math.round(t).toString(8),p:(t,e)=>Sc(t*100,e),r:Sc,s:pp,X:t=>Math.round(t).toString(16).toUpperCase(),x:t=>Math.round(t).toString(16)};function Ec(t){return t}var bc=Array.prototype.map,Rc=["y","z","a","f","p","n","\xB5","m","","k","M","G","T","P","E","Z","Y"];function mp(t){var e=t.grouping===void 0||t.thousands===void 0?Ec:hp(bc.call(t.grouping,Number),t.thousands+""),n=t.currency===void 0?"":t.currency[0]+"",a=t.currency===void 0?"":t.currency[1]+"",i=t.decimal===void 0?".":t.decimal+"",o=t.numerals===void 0?Ec:gp(bc.call(t.numerals,String)),s=t.percent===void 0?"%":t.percent+"",l=t.minus===void 0?"\u2212":t.minus+"",c=t.nan===void 0?"NaN":t.nan+"";function f(v){v=Na(v);var m=v.fill,S=v.align,P=v.sign,N=v.symbol,L=v.zero,w=v.width,X=v.comma,W=v.precision,H=v.trim,k=v.type;k==="n"?(X=!0,k="g"):Ac[k]||(W===void 0&&(W=12),H=!0,k="g"),(L||m==="0"&&S==="=")&&(L=!0,m="0",S="=");var K=N==="$"?n:N==="#"&&/[boxX]/.test(k)?"0"+k.toLowerCase():"",at=N==="$"?a:/[%p]/.test(k)?s:"",ht=Ac[k],$t=/[defgprs%]/.test(k);W=W===void 0?6:/[gprs]/.test(k)?Math.max(1,Math.min(21,W)):Math.max(0,Math.min(20,W));function dt(st){var Vt=K,vt=at,Q,St,ct;if(k==="c")vt=ht(st)+vt,st="";else{st=+st;var At=st<0||1/st<0;if(st=isNaN(st)?c:ht(Math.abs(st),W),H&&(st=vp(st)),At&&+st==0&&P!=="+"&&(At=!1),Vt=(At?P==="("?P:l:P==="-"||P==="("?"":P)+Vt,vt=(k==="s"?Rc[8+$c/3]:"")+vt+(At&&P==="("?")":""),$t){for(Q=-1,St=st.length;++Qct||ct>57){vt=(ct===46?i+st.slice(Q+1):st.slice(Q))+vt,st=st.slice(0,Q);break}}}X&&!L&&(st=e(st,1/0));var Gt=Vt.length+st.length+vt.length,Bt=Gt>1)+Vt+st+vt+Bt.slice(Gt);break;default:st=Bt+Vt+st+vt;break}return o(st)}return dt.toString=function(){return v+""},dt}function g(v,m){var S=f((v=Na(v),v.type="f",v)),P=Math.max(-8,Math.min(8,Math.floor(ia(m)/3)))*3,N=Math.pow(10,-P),L=Rc[8+P/3];return function(w){return S(N*w)+L}}return{format:f,formatPrefix:g}}var Mi,ts,Ic;yp({thousands:",",grouping:[3],currency:["$",""]});function yp(t){return Mi=mp(t),ts=Mi.format,Ic=Mi.formatPrefix,Mi}function xp(t,e){return t=Math.abs(t),e=Math.abs(e)-t,Math.max(0,ia(e)-ia(t))+1}function Tp(t){return Math.max(0,-ia(Math.abs(t)))}function $p(t,e,n,a){var i=Jo(t,e,n),o;switch(a=Na(a==null?",f":a),a.type){case"s":{var s=Math.max(Math.abs(t),Math.abs(e));return a.precision==null&&!isNaN(o=dp(i,s))&&(a.precision=o),Ic(a,s)}case"":case"e":case"g":case"p":case"r":{a.precision==null&&!isNaN(o=xp(i,Math.max(Math.abs(t),Math.abs(e))))&&(a.precision=o-(a.type==="e"));break}case"f":case"%":{a.precision==null&&!isNaN(o=Tp(i))&&(a.precision=o-(a.type==="%")*2);break}}return ts(a)}function Oc(t){var e=t.domain;return t.ticks=function(n){var a=e();return Ko(a[0],a[a.length-1],n==null?10:n)},t.tickFormat=function(n,a){var i=e();return $p(i[0],i[i.length-1],n==null?10:n,a)},t.nice=function(n){n==null&&(n=10);var a=e(),i=0,o=a.length-1,s=a[i],l=a[o],c,f,g=10;for(l0;){if(f=Zo(s,l,n),f===c)return a[i]=s,a[o]=l,e(a);if(f>0)s=Math.floor(s/f)*f,l=Math.ceil(l/f)*f;else if(f<0)s=Math.ceil(s*f)/f,l=Math.floor(l*f)/f;else break;c=f}return t},t}function Di(){var t=Tc();return t.copy=function(){return Pi(t,Di())},ra.apply(t,arguments),Oc(t)}function Cc(t){return function(e){return Math.sign(e)*Math.log1p(Math.abs(e/t))}}function Pc(t){return function(e){return Math.sign(e)*Math.expm1(Math.abs(e))*t}}function Sp(t){var e=1,n=t(Cc(e),Pc(e));return n.constant=function(a){return arguments.length?t(Cc(e=+a),Pc(e)):e},Oc(n)}function wc(){var t=Sp(qo());return t.copy=function(){return Pi(t,wc()).constant(t.constant())},ra.apply(t,arguments)}function Mc(t,e){t=t.slice();var n=0,a=t.length-1,i=t[n],o=t[a],s;return oMath.pow(t,e)}function Ip(t){return t===Math.E?Math.log:t===10&&Math.log10||t===2&&Math.log2||(t=Math.log(t),e=>Math.log(e)/t)}function Nc(t){return(e,n)=>-t(-e,n)}function Op(t){const e=t(Dc,Lc),n=e.domain;let a=10,i,o;function s(){return i=Ip(a),o=Rp(a),n()[0]<0?(i=Nc(i),o=Nc(o),t(Ap,Ep)):t(Dc,Lc),e}return e.base=function(l){return arguments.length?(a=+l,s()):a},e.domain=function(l){return arguments.length?(n(l),s()):n()},e.ticks=l=>{const c=n();let f=c[0],g=c[c.length-1];const v=g0){for(;m<=S;++m)for(P=1;Pg)break;w.push(N)}}else for(;m<=S;++m)for(P=a-1;P>=1;--P)if(N=m>0?P/o(-m):P*o(m),!(Ng)break;w.push(N)}w.length*2{if(l==null&&(l=10),c==null&&(c=a===10?"s":","),typeof c!="function"&&(!(a%1)&&(c=Na(c)).precision==null&&(c.trim=!0),c=ts(c)),l===1/0)return c;const f=Math.max(1,a*l/e.ticks().length);return g=>{let v=g/o(Math.round(i(g)));return v*an(Mc(n(),{floor:l=>o(Math.floor(i(l))),ceil:l=>o(Math.ceil(i(l)))})),e}function Fc(){const t=Op(qo()).domain([1,10]);return t.copy=()=>Pi(t,Fc()).base(t.base()),ra.apply(t,arguments),t}const Li=en(()=>{},(t,e)=>{t.setTime(+t+e)},(t,e)=>e-t);Li.every=t=>(t=Math.floor(t),!isFinite(t)||!(t>0)?null:t>1?en(e=>{e.setTime(Math.floor(e/t)*t)},(e,n)=>{e.setTime(+e+n*t)},(e,n)=>(n-e)/t):Li);const p1=Li.range,Ur=en(t=>{t.setTime(t-t.getMilliseconds())},(t,e)=>{t.setTime(+t+e*Gn)},(t,e)=>(e-t)/Gn,t=>t.getUTCSeconds()),m1=Ur.range,es=en(t=>{t.setTime(t-t.getMilliseconds()-t.getSeconds()*Gn)},(t,e)=>{t.setTime(+t+e*In)},(t,e)=>(e-t)/In,t=>t.getMinutes()),y1=es.range,ns=en(t=>{t.setUTCSeconds(0,0)},(t,e)=>{t.setTime(+t+e*In)},(t,e)=>(e-t)/In,t=>t.getUTCMinutes()),x1=ns.range,rs=en(t=>{t.setTime(t-t.getMilliseconds()-t.getSeconds()*Gn-t.getMinutes()*In)},(t,e)=>{t.setTime(+t+e*Bn)},(t,e)=>(e-t)/Bn,t=>t.getHours()),T1=rs.range,as=en(t=>{t.setUTCMinutes(0,0,0)},(t,e)=>{t.setTime(+t+e*Bn)},(t,e)=>(e-t)/Bn,t=>t.getUTCHours()),$1=as.range,is=en(t=>{t.setDate(1),t.setHours(0,0,0,0)},(t,e)=>{t.setMonth(t.getMonth()+e)},(t,e)=>e.getMonth()-t.getMonth()+(e.getFullYear()-t.getFullYear())*12,t=>t.getMonth()),S1=is.range,os=en(t=>{t.setUTCDate(1),t.setUTCHours(0,0,0,0)},(t,e)=>{t.setUTCMonth(t.getUTCMonth()+e)},(t,e)=>e.getUTCMonth()-t.getUTCMonth()+(e.getUTCFullYear()-t.getUTCFullYear())*12,t=>t.getUTCMonth()),A1=os.range;function Bc(t,e,n,a,i,o){const s=[[Ur,1,Gn],[Ur,5,5*Gn],[Ur,15,15*Gn],[Ur,30,30*Gn],[o,1,In],[o,5,5*In],[o,15,15*In],[o,30,30*In],[i,1,Bn],[i,3,3*Bn],[i,6,6*Bn],[i,12,12*Bn],[a,1,or],[a,2,2*or],[n,1,to],[e,1,Ps],[e,3,3*Ps],[t,1,eo]];function l(f,g,v){const m=gL).right(s,m);if(S===s.length)return t.every(Jo(f/eo,g/eo,v));if(S===0)return Li.every(Math.max(Jo(f,g,v),1));const[P,N]=s[m/s[S-1][2]n.axis.x.tickOffset()),i=n.config.axis_x_inverted,o=function(s){return t(s)+a()};for(const s in t)o[s]=t[s];return o.orgDomain=()=>t.domain(),o.orgScale=()=>t,n.axis.isCategorized()&&(o.domain=function(s){let l=s;return arguments.length?(t.domain(l),o):(l=this.orgDomain(),i?[l[0]+1,l[1]]:[l[0],l[1]+1])}),o},updateScales(t,e=!0){var n,a;const i=this,{axis:o,config:s,format:l,org:c,scale:f,state:{current:g,width:v,height:m,width2:S,height2:P,hasAxis:N,hasTreemap:L}}=i;if(N){const w=s.axis_rotated,X=i.getResettedPadding(1),W={x:w?X:0,y:w?0:m,subX:w?1:0,subY:w?0:P},H={x:w?m:v,y:w?v:X,subX:w?m:v,subY:w?S:1},k=e&&((n=f.x)==null?void 0:n.orgDomain()),K=e&&c.xDomain;f.x=i.getXScale(W.x,H.x,k,()=>o.x.tickOffset()),f.subX=i.getXScale(W.x,H.x,K,at=>{var ht;return at%1?0:((ht=o.subX)!=null?ht:o.x).tickOffset()}),l.xAxisTick=o.getXAxisTickFormat(),l.subXAxisTick=o.getXAxisTickFormat(!0),o.setAxis("x",f.x,s.axis_x_tick_outer,t),s.subchart_show&&o.setAxis("subX",f.subX,s.axis_x_tick_outer,t),f.y=i.getYScale("y",W.y,H.y,f.y?f.y.domain():s.axis_y_default),f.subY=i.getYScale("y",W.subY,H.subY,f.subY?f.subY.domain():s.axis_y_default),o.setAxis("y",f.y,s.axis_y_tick_outer,t),s.axis_y2_show&&(f.y2=i.getYScale("y2",W.y,H.y,f.y2?f.y2.domain():s.axis_y2_default),f.subY2=i.getYScale("y2",W.subY,H.subY,f.subY2?f.subY2.domain():s.axis_y2_default),o.setAxis("y2",f.y2,s.axis_y2_tick_outer,t))}else if(L){const w=i.getCurrentPadding();f.x=Di().rangeRound([w.left,g.width-w.right]),f.y=Di().rangeRound([w.top,g.height-w.bottom])}else(a=i.updateArc)==null||a.call(i)},xx(t){const e=this,{config:n,scale:{x:a,zoom:i}}=e,o=n.zoom_enabled&&i?i:a;return t?o(De(t.x)?t.x:t):null},xv(t){const e=this,{axis:n,config:a,scale:{x:i,zoom:o}}=e,s=a.zoom_enabled&&o?o:i;let l=e.getBaseValue(t);return n.isTimeSeries()?l=Yn.call(e,l):n.isCategorized()&&ze(l)&&(l=a.axis_x_categories.indexOf(l)),s(l)},yv(t){const e=this,{scale:{y:n,y2:a}}=e;return(t.axis&&t.axis==="y2"?a:n)(e.getBaseValue(t))},subxx(t){return t?this.scale.subX(t.x):null}},Up={setContainerSize(){const t=this,{state:e}=t;e.current.width=t.getCurrentWidth(),e.current.height=t.getCurrentHeight()},getCurrentWidth(){const t=this;return t.config.size_width||t.getParentWidth()},getCurrentHeight(){const t=this,{config:e}=t,n=e.size_height||t.getParentHeight();return n>0?n:320/(t.hasType("gauge")&&!e.gauge_fullCircle?2:1)},getParentRectValue(t){const e=`offset${Cn(t)}`;let n=this.$el.chart.node(),a=0;for(;a<30&&n&&n.tagName!=="BODY";){try{a=n.getBoundingClientRect()[t]}catch(o){e in n&&(a=n[e])}n=n.parentNode}const i=gn.body[e];return a>i&&(a=i),a},getParentWidth(){return this.getParentRectValue("width")},getParentHeight(){const t=this.$el.chart.style("height");let e=0;return t&&(e=/px$/.test(t)?parseInt(t,10):this.getParentRectValue("height")),e},getSvgLeft(t){const e=this,{config:n,state:{hasAxis:a},$el:i}=e,o=n.axis_rotated,s=o||!o&&!n.axis_y_inner,l=o?Tn.axisX:Tn.axisY,c=i.main.select(`.${l}`).node(),f=a&&n[`axis_${o?"x":"y"}_label`];let g=0;if(a&&(ze(f)||ze(f.text)||/^inner-/.test(f==null?void 0:f.position))){const N=i.main.select(`.${l}-label`);N.empty()||(g=N.node().getBoundingClientRect().left)}const v=c&&s?c.getBoundingClientRect():{right:0},m=i.chart.node().getBoundingClientRect().left+g,S=e.hasArcType(),P=v.right-m-(S?0:e.getCurrentPaddingByDirection("left",t));return P>0?P:0},updateDimension(t){var e;const n=this,{config:a,state:{hasAxis:i},$el:o}=n;i&&!t&&n.axis.x&&a.axis_rotated&&((e=n.axis.subX)==null||e.create(o.axis.subX)),n.updateScales(t),n.updateSvgSize(),n.transformAll(!1)},updateSvgSize(){const t=this,{config:e,state:{clip:n,current:a,hasAxis:i,width:o,height:s},$el:{svg:l}}=t;if(e.resize_auto==="viewBox"?l.attr("viewBox",`0 0 ${a.width} ${a.height}`):l.attr("width",a.width).attr("height",a.height),i){const c=l.select(`.${ks.brush} .overlay`),f={width:0,height:0};c.size()&&(f.width=+c.attr("width"),f.height=+c.attr("height")),l.selectAll([`#${n.id}`,`#${n.idGrid}`]).select("rect").attr("width",o).attr("height",s),l.select(`#${n.idXAxis}`).select("rect").call(t.setXAxisClipPath.bind(t)),l.select(`#${n.idYAxis}`).select("rect").call(t.setYAxisClipPath.bind(t)),n.idSubchart&&l.select(`#${n.idSubchart}`).select("rect").attr("width",o).attr("height",f.height)}},getCurrentPaddingByDirection(t,e=!1,n=!1){var a;const i=this,{config:o,$el:s,state:{hasAxis:l}}=i,c=o.axis_rotated,f=((a=o.padding)==null?void 0:a.mode)==="fit",g=he(o[`padding_${t}`])?o[`padding_${t}`]:void 0,v=l?{top:c?"y2":null,bottom:c?"y":"x",left:c?"x":"y",right:c?null:"y2"}[t]:null,m=/^(left|right)$/.test(t),S=v&&o[`axis_${v}_inner`],P=v&&o[`axis_${v}_show`],N=v?o[`axis_${v}_axes`].length:0;let L=v?m?i.getAxisWidthByAxisId(v,e):i.getHorizontalAxisHeight(v):0;const w=20;let X=0;!f&&m&&(L=Jg(L));let W=l&&m&&(S||ln(g)&&!P)?0:f?(P?L:0)+(g!=null?g:0):ln(g)?L:g;return m&&l?(v&&(f||S)&&o[`axis_${v}_label`].text&&(W+=i.axis.getAxisLabelPosition(v).isOuter?w:0),t==="right"?(W+=c?!f&&ln(g)?10:2:!P||S?f?2:1:0,W+=n?i.axis.getXAxisTickTextY2Overflow(w):0):t==="left"&&c&&ln(g)&&(W=o.axis_x_show?f?L:Math.max(L,40):1)):t==="top"?(s.title&&s.title.node()&&(W+=i.getTitlePadding()),X=c&&!S?N:0):t==="bottom"&&l&&c&&!P&&(W+=1),W+L*N-X},getCurrentPadding(t=!1){const e=this,[n,a,i,o]=["top","bottom","left","right"].map(s=>e.getCurrentPaddingByDirection(s,null,t));return{top:n,bottom:a,left:i,right:o}},getResettedPadding(t){const e=this,{config:n}=e,a=he(t);let i=a?0:{};return n.padding===!1?!a&&Object.keys(t).forEach(o=>{i[o]=!qn(n.data_labels)&&n.data_labels!==!1&&o==="top"?t[o]:0}):i=t,i},updateSizes(t){var e,n,a,i,o;const s=this,{config:l,state:c,$el:{legend:f}}=s,g=l.axis_rotated,v=s.hasArcType()||c.hasFunnel||c.hasTreemap,m=((e=l.padding)==null?void 0:e.mode)==="fit";!t&&s.setContainerSize();const S={width:f?s.getLegendWidth():0,height:f?s.getLegendHeight():0};!v&&l.axis_x_show&&l.axis_x_tick_autorotate&&s.updateXAxisTickClip();const P={right:l.legend_show&&c.isLegendRight?s.getLegendWidth()+(m?0:20):0,bottom:!l.legend_show||c.isLegendRight||c.isLegendInset?0:S.height},N=g||v?0:s.getHorizontalAxisHeight("x"),L=l.subchart_axis_x_show&&l.subchart_axis_x_tick_text_show?N:30,w=l.subchart_show&&!v?l.subchart_size_height+L:0,X=s.hasType("gauge")&&l.arc_needle_show&&!l.gauge_fullCircle&&!l.gauge_label_show?10:0,W=s.getCurrentPadding(!0);if(c.margin=!v&&g?{top:W.top,right:v?0:W.right+P.right,bottom:P.bottom+W.bottom,left:w+(v?0:W.left)}:{top:(m?0:4)+W.top,right:v?0:W.right+P.right,bottom:X+w+P.bottom+W.bottom,left:v?0:W.left},c.margin=s.getResettedPadding(c.margin),c.margin2=g?{top:c.margin.top,right:NaN,bottom:20+P.bottom,left:s.state.rotatedPadding.left}:{top:c.current.height-w-P.bottom,right:NaN,bottom:L+P.bottom,left:c.margin.left},c.margin3={top:0,right:NaN,bottom:0,left:0},(n=s.updateSizeForLegend)==null||n.call(s,S),c.width=c.current.width-c.margin.left-c.margin.right,c.height=c.current.height-c.margin.top-c.margin.bottom,c.width<0&&(c.width=0),c.height<0&&(c.height=0),c.width2=g?c.margin.left-c.rotatedPadding.left-c.rotatedPadding.right:c.width,c.height2=g?c.height:c.current.height-c.margin2.top-c.margin2.bottom,c.width2<0&&(c.width2=0),c.height2<0&&(c.height2=0),s.hasArcType()){const H=s.hasType("gauge"),k=l.legend_show&&c.isLegendRight,K=(a=c.hasRadar&&s.cache.get(Ln.radarTextWidth))!=null?a:0;c.arcWidth=c.width-(k?S.width+10:0)-K,c.arcHeight=c.height-(k&&!H?0:10),(i=l.arc_rangeText_values)!=null&&i.length&&(H?(c.arcWidth-=25,c.arcHeight-=10,c.margin.left+=10):(c.arcHeight-=20,c.margin.top+=10)),H&&!l.gauge_fullCircle&&(c.arcHeight+=c.height-s.getPaddingBottomForGauge()),(o=s.updateRadius)==null||o.call(s)}c.isLegendRight&&v&&(c.margin3.left=c.arcWidth/2+c.radiusExpanded*1.1)}},zp={setCssRule(t,e,n,a){const i=this,{config:o,state:{cssRule:s,style:l}}=i;return o.boost_useCssRule?c=>{c.each(f=>{const g=a&&(a==null?void 0:a.call(i,f)),v=`${t?`.${sn.shapes+i.getTargetSelectorSuffix(f.id)}`:""}${e}`;e in s&&l.sheet.deleteRule(s[v]),i.state.cssRule[v]=_g(l,v,n.filter(Boolean).map(m=>ze(g)&&m.indexOf(":")===-1?`${m}: ${g}`:m||""))})}:()=>{}},getStylePropValue(t){const{config:{boost_useCssRule:e}}=this;return e?null:ve(t)?t.bind(this):t}};function Uc(t){return typeof t=="string"?new Ie([document.querySelectorAll(t)],[document.documentElement]):new Ie([T(t)],_t)}function jp(t){let e="middle";return t>0&&t<=170?e="end":t>190&&t<=360&&(e="start"),e}function Vp(t,e,n,a,i){var o;const s=this,{value:l}=t,c=s.isCandlestickType(t),f=he(l)&&l<0||c&&!((o=s.getCandlestickData(t))!=null&&o._isUp);let{x:g,y:v}=e;const m=4,S=m*2;return a?n==="start"?(g+=f?0:S,v+=m):n==="middle"?(g+=S,v-=S):n==="end"&&(f&&(g-=S),v+=m):(n==="start"?(g+=m,f&&(v+=S*2)):n==="middle"?v-=S:n==="end"&&(g-=m,f&&(v+=S*2)),i&&(v+=f?-17:c?13:7)),{x:g,y:v}}function zc(t,e){var n;const a=this.config.data_labels_position,{id:i,index:o,value:s}=t;return(n=ve(a)?a.bind(this.api)(e,s,i,o,this.$el.text):(i in a?a[i]:a)[e])!=null?n:0}var Gp={opacityForText(t){const e=this;return e.isBarType(t)&&!e.meetsLabelThreshold(Math.abs(e.getRatio("bar",t)),"bar")?"0":e.hasDataLabel?null:"0"},initText(){const{$el:t}=this;t.main.select(`.${Se.chart}`).append("g").attr("class",On.chartTexts).style("pointer-events",t.funnel||t.treemap?"none":null)},updateTargetsForText(t){const e=this,n=e.getChartClass("Text"),a=e.getClass("texts","id"),i=e.classFocus.bind(e);e.$el.main.select(`.${On.chartTexts}`).selectAll(`.${On.chartText}`).data(t).attr("class",l=>`${n(l)}${i(l)}`.trim()).enter().append("g").style("opacity","0").attr("class",n).call(e.setCssRule(!0,` .${On.text}`,["fill","pointer-events:none"],e.updateTextColor)).append("g").attr("class",a)},updateText(){const t=this,{$el:e,$T:n,config:a,axis:i}=t,o=t.getClass("text","index"),s=a.data_labels.centered,l=e.main.selectAll(`.${On.texts}`).selectAll(`.${On.text}`).data(t.labelishData.bind(t));n(l.exit()).style("fill-opacity","0").remove(),e.text=l.enter().append("text").merge(l).attr("class",o).attr("text-anchor",c=>{let g=a[`axis_${i==null?void 0:i.getId(c.id)}_inverted`]?c.value>0:c.value<0;if(t.isCandlestickType(c)){const v=t.getCandlestickData(c);g=!(v!=null&&v._isUp)}else if(t.isTreemapType(c))return s?"middle":"start";return a.axis_rotated?g?"end":"start":"middle"}).style("fill",t.getStylePropValue(t.updateTextColor)).style("fill-opacity","0").each(function(c,f,g){const v=ot(this);let{value:m}=c;if(t.isBubbleZType(c))m=t.getBubbleZData(m,"z");else if(t.isCandlestickType(c)){const S=t.getCandlestickData(c);S&&(m=S.close)}m=t.isTreemapType(c)?t.treemapDataLabelFormat(c)(v):t.dataLabelFormat(c.id)(m,c.id,c.index,g),he(m)?this.textContent=m:wa(v,m)})},updateTextColor(t){const e=this,{config:n}=e,a=n.data_labels_colors,i=e.isArcType(t)&&!e.isRadarType(t)||e.isFunnelType(t)||e.isTreemapType(t)?null:e.color(t);let o;if(ze(a))o=a;else if(Be(a)){const{id:s}=t.data||t;o=a[s]}else ve(a)&&(o=a.bind(e.api)(i,t));if(e.isCandlestickType(t)&&!ve(a)){const s=e.getCandlestickData(t);if(!(s!=null&&s._isUp)){const l=n.candlestick_color_down;o=Be(l)?l[t.id]:l}}return o||i},updateTextBGColor(t,e){const n=this,{$el:a}=n;let i="";if(ze(e)||Be(e)){const o=ze(e)?"":n.getTargetSelectorSuffix("id"in t?t.id:t.data.id),s=a.defs.select(["filter[id*='labels-bg","']"].join(o));s.size()&&(i=`url(#${s.attr("id")})`)}return i||null},redrawText(t,e,n,a){const i=this,{$T:o,axis:s,config:l,state:{hasTreemap:c}}=i,f=gr(!0),g=l.axis_rotated,v=l.data_labels.rotate,m=jp(v),S=v?`rotate(${v})`:"";return i.$el.text.style("fill",i.getStylePropValue(i.updateTextColor)).attr("filter",P=>i.updateTextBGColor.bind(i)(P,l.data_labels_backgroundColors)).style("fill-opacity",n?0:i.opacityForText.bind(i)).each(function(P,N){const L=o(c&&this.childElementCount?this.parentNode:this,!!(a&&this.getAttribute("x")),f),w=l[`axis_${s==null?void 0:s.getId(P.id)}_inverted`];let X={x:t.bind(this)(P,N),y:e.bind(this)(P,N)};v&&(X=Vp.bind(i)(P,X,m,g,w),L.attr("text-anchor",m)),this.childElementCount||v?L.attr("transform",`translate(${X.x} ${X.y}) ${S}`):L.attr("x",X.x).attr("y",X.y)}),!0},getTextRect(t,e){const n=this;let a=t.node?t.node():t;/text/i.test(a.tagName)||(a=a.querySelector("text"));const i=a.textContent,o=`${Ln.textRect}-${i.replace(/\W/g,"_")}`;let s=n.cache.get(o);return s||(n.$el.svg.append("text").style("visibility","hidden").style("font",ot(a).style("font")).classed(e,!0).text(i).call(l=>{s=Ma(l.node())}).remove(),n.cache.add(o,s)),s},generateXYForText(t,e){const n=this,{state:{hasRadar:a,hasFunnel:i,hasTreemap:o}}=n,s=Object.keys(t),l={},c=e?n.getXForText:n.getYForText;return i&&s.push("funnel"),a&&s.push("radar"),o&&s.push("treemap"),s.forEach(f=>{l[f]=n[`generateGet${Cn(f)}Points`](t[f],!1)}),function(f,g){const v=n.isAreaType(f)&&"area"||n.isBarType(f)&&"bar"||n.isCandlestickType(f)&&"candlestick"||n.isFunnelType(f)&&"funnel"||n.isRadarType(f)&&"radar"||n.isTreemapType(f)&&"treemap"||"line";return c.call(n,l[v](f,g),f,this)}},getCenteredTextPos(t,e,n,a){const i=this,{config:o}=i,s=o.axis_rotated,l=i.isBarType(t),c=i.isTreemapType(t);if(o.data_labels.centered&&(l||c)){const f=Ma(n);if(l){const g=i.getRangedData(t,null,"bar")>=0;if(s){const v=(g?e[1][1]-e[0][1]:e[0][1]-e[1][1])/2+f.width/2;return g?-v-3:v+2}else{const v=(g?e[0][1]-e[1][1]:e[1][1]-e[0][1])/2+f.height/2;return g?v:-v-2}}else if(c)return a==="x"?(e[1][0]-e[0][0])/2:(e[1][1]-e[0][1])/2+f.height/2}return 0},getXForText(t,e,n){var a;const i=this,{config:o}=i,s=o.axis_rotated,l=i.isFunnelType(e),c=i.isTreemapType(e);let f=t?t[0][0]:0;if(i.isCandlestickType(e))s?f=(a=i.getCandlestickData(e))!=null&&a._isUp?t[2][2]+4:t[2][1]-4:f+=(t[1][0]-f)/2;else if(l)f+=i.state.current.width/2;else if(c)f+=o.data_labels.centered?0:5;else if(s){const g=o[`axis_${i.axis.getId(e.id)}_inverted`],v=i.isBarType(e)?4:6,m=e.value;f=t[2][1],g?f-=v*(m>0?1:-1):f+=v*(m<0?-1:1)}else f=i.hasType("bar")?(t[2][0]+t[0][0])/2:f;return(s||c)&&(f+=i.getCenteredTextPos(e,t,n,"x")),f+zc.call(this,e,"x")},getYForText(t,e,n){const a=this,{axis:i,config:o,state:s}=a,l=o.axis_rotated,c=o[`axis_${i==null?void 0:i.getId(e.id)}_inverted`],f=a.isBarType(e),g=a.isFunnelType(e),v=a.isTreemapType(e),m=o.point_r,S=Ma(n);let{value:P}=e,N=3,L;if(a.isCandlestickType(e))P=a.getCandlestickData(e),l?(L=t[0][0],L+=(t[1][0]-L)/2+N):(L=P&&P._isUp?t[2][2]-N:t[2][1]+N*4,c&&(L+=15*(P._isUp?1:-1)));else if(g)L=t?t[0][1]+(t[1][1]-t[0][1])/2+S.height/2-3:0;else if(v)L=t[0][1]+(o.data_labels.centered?0:S.height+5);else if(l)L=(t[0][0]+t[2][0]+S.height*.6)/2;else if(L=t[2][1],he(m)&&m>5&&(a.isLineType(e)||a.isScatterType(e))&&(N+=o.point_r/2.3),P<0||P===0&&!s.hasPositiveValue&&s.hasNegativeValue)L+=c?f?-3:-5:S.height+(f?-N:N);else{let w=-N*2;f?w=-N:a.isBubbleType(e)&&(w=N),c&&(w=f?10:15),L+=w}return(!l||v)&&(L+=a.getCenteredTextPos(e,t,n,"y")),L+zc.call(this,e,"y")},markOverlapped(t,e,n){const a=e.$el.arcs.selectAll(n),i=a.filter(c=>c.data.id!==t),o=a.filter(c=>c.data.id===t),s=Jl(o.node()),l=(c,f)=>Math.sqrt(Math.pow(c,2)+Math.pow(f,2));o.node()&&i.each(function(){const c=Jl(this),f=ot(this),g=l(s.e,s.f)>l(c.e,c.f)?o:f,v=Math.ceil(Math.abs(s.e-c.e))=i}};function jc(t="left",e){const n=he(e);let a;return t.indexOf("center")>-1?a=n?e/2:"middle":t.indexOf("right")>-1?a=n?e:"end":a=n?0:"start",a}var Xp={initTitle(){const t=this,{config:e,$el:n}=t;if(e.title_text){n.title=n.svg.append("g");const a=n.title.append("text").style("text-anchor",jc(e.title_position)).attr("class",On.title);wa(a,e.title_text,[.3,1.5])}},redrawTitle(){const t=this,{config:e,state:{current:n},$el:{title:a}}=t;if(a){const i=jc(e.title_position,n.width),o=(e.title_padding.top||0)+t.getTextRect(t.$el.title,On.title).height;a.attr("transform",`translate(${i}, ${o})`)}},getTitlePadding(){const t=this,{$el:{title:e},config:n}=t;return(n.title_padding.top||0)+(e?t.getTextRect(e,On.title).height:0)+(n.title_padding.bottom||0)}},Hp={initTooltip(){const t=this,{config:e,$el:n}=t;n.tooltip=ot(e.tooltip_contents.bindto),n.tooltip.empty()&&(n.tooltip=n.chart.append("div").attr("class",ei.tooltipContainer).style("position","absolute").style("pointer-events","none").style("display","none")),t.bindTooltipResizePos()},initShowTooltip(){var t;const e=this,{config:n,$el:a,state:{hasAxis:i,hasRadar:o}}=e;if(n.tooltip_init_show){const s=!(i||o);(t=e.axis)!=null&&t.isTimeSeries()&&ze(n.tooltip_init_x)&&(n.tooltip_init_x=Yn.call(e,n.tooltip_init_x)),e.api.tooltip.show({data:{[s?"index":"x"]:n.tooltip_init_x}});const l=n.tooltip_init_position;if(!n.tooltip_contents.bindto&&!qn(l)){const{top:c=0,left:f=50}=l;a.tooltip.style("top",ze(c)?c:`${c}px`).style("left",ze(f)?f:`${f}px`).style("display",null)}}},getTooltipHTML(...t){const e=this,{api:n,config:a}=e;return ve(a.tooltip_contents)?a.tooltip_contents.bind(n)(...t):e.getTooltipContent(...t)},getTooltipContent(t,e,n,a){var i;const o=this,{api:s,config:l,state:c,$el:f}=o,[g,v,m]=["title","name","value"].map(vt=>{const Q=l[`tooltip_format_${vt}`];return ve(Q)?Q.bind(s):Q}),S=(...vt)=>Po((g||e)(...vt)),P=(...vt)=>Po((v||(Q=>Q))(...vt)),N=(...vt)=>{const Q=m||(c.hasTreemap||o.isStackNormalized()?(St,ct)=>`${(ct*100).toFixed(2)}%`:n);return Po(Q(...vt))},L=l.tooltip_order,w=vt=>o.axis&&o.isBubbleZType(vt)?o.getBubbleZData(vt.value,"z"):o.getBaseValue(vt),X=o.levelColor?vt=>o.levelColor(vt.value):vt=>a(vt),W=l.tooltip_contents,H=W.template,k=o.mapToTargetIds();if(L===null&&l.data_groups.length){const vt=o.orderTargets(o.data.targets).map(Q=>Q.id).reverse();t.sort((Q,St)=>{let ct=Q?Q.value:null,At=St?St.value:null;return ct>0&&At>0&&(ct=Q.id?vt.indexOf(Q.id):null,At=St.id?vt.indexOf(St.id):null),ct-At})}else if(/^(asc|desc)$/.test(L)){const vt=L==="asc";t.sort((Q,St)=>{const ct=Q?w(Q):null,At=St?w(St):null;return vt?ct-At:At-ct})}else ve(L)&&t.sort(L.bind(s));const K=o.getTooltipContentTemplate(H),at=t.length;let ht,$t,dt,st,Vt;for(Vt=0;Vt${vt}`:""})}if(!$t.ratio&&f.arcs&&(dt=["arc",o.$el.arcs.select(`path.${Ve.arc}-${$t.id}`).data()[0]],$t.ratio=o.getRatio(...dt)),dt=[$t.ratio,$t.id,$t.index],o.isAreaRangeType($t)){const[vt,Q]=["high","low"].map(ct=>N(o.getRangedData($t,ct),...dt));st=`Mid: ${N(w($t),...dt)} High: ${vt} Low: ${Q}`}else if(o.isCandlestickType($t)){const[vt,Q,St,ct,At]=["open","high","low","close","volume"].map(Gt=>o.getRangedData($t,Gt,"candlestick")?N(o.getRangedData($t,Gt,"candlestick"),...dt):void 0);st=`Open: ${vt} High: ${Q} Low: ${St} Close: ${ct}${At?` Volume: ${At}`:""}`}else if(o.isBarRangeType($t)){const{value:vt,id:Q,index:St}=$t;st=`${N(vt,void 0,Q,St)}`}else st=N(w($t),...dt);if(st!==void 0){if($t.name===null)continue;const vt=P((i=$t.name)!=null?i:$t.id,...dt),Q=X($t),St={CLASS_TOOLTIP_NAME:ei.tooltipName+o.getTargetSelectorSuffix($t.id),COLOR:H||!o.patterns?Q:``,NAME:vt,VALUE:st};if(H&&Be(W.text)){const ct=k.indexOf($t.id);Object.keys(W.text).forEach(At=>{St[At]=W.text[At][ct]})}ht+=bi(K[1],St)}}return`${ht}`},getTooltipContentTemplate(t){return(t||` + {=TITLE} + {{ + + + }} +
${this.patterns?"{=COLOR}":''}{=NAME}{=VALUE}
`).replace(/(\r?\n|\t)/g,"").split(/{{(.*)}}/)},setTooltipPosition(t,e){var n,a;const i=this,{config:o,scale:s,state:l,$el:{eventRect:c,tooltip:f,svg:g}}=i,{bindto:v}=o.tooltip_contents,m=o.axis_rotated,S=f==null?void 0:f.datum();if(!v&&S){const P=t!=null?t:JSON.parse(S.current),[N,L]=Hn(l.event,e!=null?e:c==null?void 0:c.node()),w={x:N,y:L};if(l.hasAxis&&s.x&&S&&"x"in S){const k=(K=0,at,ht="y")=>{var $t;const dt=s[at?($t=i.axis)==null?void 0:$t.getId(at):ht];return dt?dt(K)+(m?l.margin.left:l.margin.top):0};w.xAxis=s.x(S.x)+(o.tooltip_position?m?l.margin.top:l.margin.left:0),P.length===1?w.yAxis=k(P[0].value,P[0].id):w.yAxis=k}const{width:X=0,height:W=0}=S,H=(a=(n=o.tooltip_position)==null?void 0:n.bind(i.api)(P,X,W,c==null?void 0:c.node(),w))!=null?a:Lo(g)?i.getTooltipPositionViewBox.bind(i)(X,W,w):i.getTooltipPosition.bind(i)(X,W,w);["top","left"].forEach(k=>{const K=H[k];f.style(k,`${K}px`),k==="left"&&!S.xPosInPercent&&(S.xPosInPercent=K/l.current.width*100)})}},getTooltipPositionViewBox(t,e,n){var a,i;const o=this,{$el:{eventRect:s,svg:l},config:c,state:f}=o,g=c.axis_rotated,v=o.hasArcType()||f.hasFunnel||f.hasTreemap,m=(i=(a=v?l:s)==null?void 0:a.node())!=null?i:f.event.target;let{x:S,y:P}=n;f.hasAxis&&(S=g?S:n.xAxis,P=g?n.xAxis:P);const N=Ai(m,S,P,!1),L=m.getBoundingClientRect(),w=Ai(m,20,0,!1).x;let X=N.y,W=N.x+t/2+w;return v&&(f.hasFunnel||f.hasTreemap||f.hasRadar?(W-=t/2+w,X+=e):(X+=L.height/2,W+=L.width/2-(t-w))),W+t>L.width&&(W=L.width-t-w),X+e>L.height&&(X-=e*2),{top:X,left:W}},getTooltipPosition(t,e,n){var a,i,o;const s=this,{config:l,scale:c,state:f}=s,{width:g,height:v,current:m,hasFunnel:S,hasRadar:P,hasTreemap:N,isLegendRight:L,inputType:w}=f,X=s.hasType("gauge")&&!l.gauge_fullCircle,W=l.axis_rotated,H=s.hasArcType(),k=s.getSvgLeft(!0);let K=k+m.width-s.getCurrentPaddingByDirection("right");const at=20;let{x:ht,y:$t}=n;if(P)ht+=ht>=g/2?15:-(t+15),$t+=15;else if(H){if(w!=="touch"){let Vt=(i=(a=s.getTitlePadding)==null?void 0:a.call(s))!=null?i:0;Vt&&X&&((o=l.arc_rangeText_values)!=null&&o.length)&&(Vt+=10),ht+=(g-(L?s.getLegendWidth():0))/2,$t+=(X?v:v/2+e)+Vt}}else if(S||N)$t+=e;else{const st={top:s.getCurrentPaddingByDirection("top",!0),left:s.getCurrentPaddingByDirection("left",!0)};W?(ht+=k+st.left+at,$t=st.top+n.xAxis+at,K-=k):(ht=k+st.left+at+(c.zoom?ht:n.xAxis),$t+=st.top-5)}if(ht+t+15>K&&(ht-=t+(S||N||H?0:W?at*2:38)),$t+e>m.height){const st=N?e+10:30;$t-=X?e*1.5:e+st}const dt={top:$t,left:ht};return Object.keys(dt).forEach(st=>{dt[st]<0&&(dt[st]=0)}),dt},showTooltip(t,e){const n=this,{config:a,$el:{tooltip:i}}=n,o=t.filter(c=>c&&De(n.getBaseValue(c)));if(!i||o.length===0||!a.tooltip_show)return;let s=i.datum();const l=JSON.stringify(t);if(!s||s.current!==l){const{index:c,x:f}=t.concat().sort()[0];_e(a.tooltip_onshow,n.api,t),i.html(n.getTooltipHTML(t,n.axis?n.axis.getXAxisTickFormat():n.categoryName.bind(n),n.getDefaultValueFormat(),n.color)).style("display",null).style("visibility",null).datum(s={index:c,x:f,current:l,width:i.property("offsetWidth"),height:i.property("offsetHeight")}),_e(a.tooltip_onshown,n.api,t),n._handleLinkedCharts(!0,c)}n.setTooltipPosition(o,e)},bindTooltipResizePos(){const t=this,{resizeFunction:e,state:n,$el:{tooltip:a}}=t;e.add(()=>{if(a.style("display")==="block"){const{current:i}=n,{width:o,xPosInPercent:s}=a.datum();let l=i.width/100*s;const c=i.width-(l+o);c<0&&(l+=c),a.style("left",`${l}px`)}})},hideTooltip(t){var e;const n=this,{api:a,config:i,$el:{tooltip:o}}=n;if(o&&o.style("display")!=="none"&&(!i.tooltip_doNotHide||t)){const s=JSON.parse((e=o.datum().current)!=null?e:{});_e(i.tooltip_onhide,a,s),o.style("display","none").style("visibility","hidden").datum(null),_e(i.tooltip_onhidden,a,s)}},_handleLinkedCharts(t,e){const n=this,{charts:a,config:i,state:{event:o}}=n;if(o!=null&&o.isTrusted&&i.tooltip_linked&&a.length>1){const s=i.tooltip_linked_name;a.filter(l=>l!==n.api).forEach(l=>{const{config:c,$el:f}=l.internal,g=c.tooltip_linked,v=c.tooltip_linked_name,m=gn.body.contains(f.chart.node());if(g&&s===v&&m){const S=f.tooltip.data()[0],P=e!==(S==null?void 0:S.index);try{l.tooltip[t&&P?"show":"hide"]({index:e})}catch(N){}}})}},updateTooltipOnRedraw(t,e){var n;const a=this,{config:i,$el:{eventRect:o,svg:s,tooltip:l},state:{event:c,hasAxis:f,hasRadar:g,hasTreemap:v}}=a;if((l==null?void 0:l.style("display"))==="block"&&c){const m=t!=null?t:(n=g?s:o)==null?void 0:n.node();if(f||g)if(a.isMultipleX())a.selectRectForMultipleXs(m,!1);else{const S=e!=null?e:a.getDataIndexFromEvent(c);e===-1?a.api.tooltip.hide():(a.selectRectForSingle(m,S),a.setExpand(S,null,!0))}else{const{clientX:S,clientY:P}=c;setTimeout(()=>{let N=[S,P].every(Number.isFinite)&&gn.elementFromPoint(S,P);const L=N&&ot(N).datum();if(L){const w=a.hasArcType()?a.convertToArcData(a.updateAngle(L)):L==null?void 0:L.data;v&&(N=s.node()),w&&a.showTooltip([w],N)}else a.api.tooltip.hide()},i.transition_duration)}}}},Yp={getTranslate(t,e=0){var n;const a=this,{config:i,state:o}=a,s=i.axis_rotated;let l=0,c,f;if(e&&/^(x|y2?)$/.test(t)&&(l=a.getAxisSize(t)*e),t==="main")c=$i(o.margin.left),f=$i(o.margin.top);else if(t==="context")c=$i(o.margin2.left),f=$i(o.margin2.top);else if(t==="legend")c=o.margin3.left,f=o.margin3.top;else if(t==="x")c=s?-l:0,f=s?0:o.height+l;else if(t==="y")c=s?0:-l,f=s?o.height+l:0;else if(t==="y2")c=s?0:o.width+l,f=s?-l-1:0;else if(t==="subX")c=0,f=s?0:o.height2;else if(t==="arc")c=o.arcWidth/2,f=o.arcHeight/2,(n=i.arc_rangeText_values)!=null&&n.length&&(f+=5+(a.hasType("gauge")&&i.title_text?10:0));else if(t==="polar")c=o.arcWidth/2,f=o.arcHeight/2;else if(t==="radar"){const[g,v]=a.getRadarSize();c=o.width/2-g,f=o.height/2-v}return`translate(${c}, ${f})`},transformMain(t,e){const n=this,{$el:{main:a},$T:i}=n,o=e!=null&&e.axisX?e.axisX:i(a.select(`.${Tn.axisX}`),t),s=e!=null&&e.axisY?e.axisY:i(a.select(`.${Tn.axisY}`),t),l=e!=null&&e.axisY2?e.axisY2:i(a.select(`.${Tn.axisY2}`),t);i(a,t).attr("transform",n.getTranslate("main")),o.attr("transform",n.getTranslate("x")),s.attr("transform",n.getTranslate("y")),l.attr("transform",n.getTranslate("y2")),a.select(`.${Ve.chartArcs}`).attr("transform",n.getTranslate("arc"))},transformAll(t,e){const n=this,{config:a,state:{hasAxis:i,hasFunnel:o,hasTreemap:s},$el:l}=n;!o&&!s&&n.transformMain(t,e),i&&a.subchart_show&&n.transformContext(t,e),l.legend&&n.transformLegend(t)}},Wp={isValidChartType(t){return!!(t&&Object.values(oe).indexOf(t)>-1)},setTargetType(t,e){const n=this,{config:a,state:{withoutFadeIn:i}}=n;n.mapToTargetIds(t).forEach(o=>{i[o]=e===a.data_types[o],a.data_types[o]=e}),t||(a.data_type=e)},updateTypesElements(){const t=this,{state:{current:e}}=t;Object.keys(oe).forEach(n=>{const a=oe[n],i=t.hasType(a,null,!0),o=e.types.indexOf(a);o===-1&&i?e.types.push(a):o>-1&&!i&&e.types.splice(o,1)}),t.setChartElements()},hasType(t,e,n=!1){var a;const i=this,{config:o,state:{current:s}}=i,l=o.data_types,c=e||i.data.targets;let f=!1;return!n&&((a=s.types)==null?void 0:a.indexOf(t))>-1?f=!0:c!=null&&c.length?c.forEach(g=>{const v=l[g.id];(v===t||!v&&t==="line")&&(f=!0)}):Object.keys(l).length?Object.keys(l).forEach(g=>{l[g]===t&&(f=!0)}):f=o.data_type===t,f},hasTypeOf(t,e,n=[]){return t in Sr?!Sr[t].filter(a=>n.indexOf(a)===-1).every(a=>!this.hasType(a,e)):!1},isTypeOf(t,e){var n;const a=ze(t)?t:t.id,i=this.config&&(((n=this.config.data_types)==null?void 0:n[a])||this.config.data_type);return je(e)?e.indexOf(i)>=0:i===e},hasPointType(){const t=this;return t.hasTypeOf("Line")||t.hasType("bubble")||t.hasType("scatter")},hasArcType(t,e){return this.hasTypeOf("Arc",t,e)},hasMultiArcGauge(){return this.hasType("gauge")&&this.config.gauge_type==="multi"},isLineType(t){const e=ze(t)?t:t.id;return!this.config.data_types[e]||this.isTypeOf(e,Sr.Line)},isStepType(t){return this.isTypeOf(t,Sr.Step)},isSplineType(t){return this.isTypeOf(t,Sr.Spline)},isAreaType(t){return this.isTypeOf(t,Sr.Area)},isAreaRangeType(t){return this.isTypeOf(t,Sr.AreaRange)},isBarType(t){return this.isTypeOf(t,"bar")},isBubbleType(t){return this.isTypeOf(t,"bubble")},isCandlestickType(t){return this.isTypeOf(t,"candlestick")},isScatterType(t){return this.isTypeOf(t,"scatter")},isTreemapType(t){return this.isTypeOf(t,"treemap")},isPieType(t){return this.isTypeOf(t,"pie")},isFunnelType(t){return this.isTypeOf(t,"funnel")},isGaugeType(t){return this.isTypeOf(t,"gauge")},isDonutType(t){return this.isTypeOf(t,"donut")},isPolarType(t){return this.isTypeOf(t,"polar")},isRadarType(t){return this.isTypeOf(t,"radar")},isArcType(t){return this.isPieType(t)||this.isDonutType(t)||this.isGaugeType(t)||this.isPolarType(t)||this.isRadarType(t)},isCirclePoint(t){const{config:e}=this,n=e.point_pattern;let a=!1;return(t==null?void 0:t.tagName)==="circle"?a=!0:a=e.point_type==="circle"&&(!n||je(n)&&n.length===0),a},lineData(t){return this.isLineType(t)?[t]:[]},arcData(t){return this.isArcType(t.data)?[t]:[]},labelishData(t){return this.isBarType(t)||this.isLineType(t)||this.isScatterType(t)||this.isBubbleType(t)||this.isCandlestickType(t)||this.isFunnelType(t)||this.isRadarType(t)||this.isTreemapType(t)?t.values.filter(e=>he(e.value)||!!e.value):[]},barLineBubbleData(t){return this.isBarType(t)||this.isLineType(t)||this.isBubbleType(t)?t.values:[]},isInterpolationType(t){return["basis","basis-closed","basis-open","bundle","cardinal","cardinal-closed","cardinal-open","catmull-rom","catmull-rom-closed","catmull-rom-open","linear","linear-closed","monotone-x","monotone-y","natural"].indexOf(t)>=0}};function Ni(t,e,n){t._context.bezierCurveTo((2*t._x0+t._x1)/3,(2*t._y0+t._y1)/3,(t._x0+2*t._x1)/3,(t._y0+2*t._y1)/3,(t._x0+4*t._x1+e)/6,(t._y0+4*t._y1+n)/6)}function Fi(t){this._context=t}Fi.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){switch(this._point){case 3:Ni(this,this._x1,this._y1);case 2:this._context.lineTo(this._x1,this._y1);break}(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;break;case 2:this._point=3,this._context.lineTo((5*this._x0+this._x1)/6,(5*this._y0+this._y1)/6);default:Ni(this,t,e);break}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e}};function Kp(t){return new Fi(t)}function Ar(){}function Vc(t){this._context=t}Vc.prototype={areaStart:Ar,areaEnd:Ar,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._y0=this._y1=this._y2=this._y3=this._y4=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:{this._context.moveTo(this._x2,this._y2),this._context.closePath();break}case 2:{this._context.moveTo((this._x2+2*this._x3)/3,(this._y2+2*this._y3)/3),this._context.lineTo((this._x3+2*this._x2)/3,(this._y3+2*this._y2)/3),this._context.closePath();break}case 3:{this.point(this._x2,this._y2),this.point(this._x3,this._y3),this.point(this._x4,this._y4);break}}},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._x2=t,this._y2=e;break;case 1:this._point=2,this._x3=t,this._y3=e;break;case 2:this._point=3,this._x4=t,this._y4=e,this._context.moveTo((this._x0+4*this._x1+t)/6,(this._y0+4*this._y1+e)/6);break;default:Ni(this,t,e);break}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e}};function Zp(t){return new Vc(t)}function Gc(t){this._context=t}Gc.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){(this._line||this._line!==0&&this._point===3)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3;var n=(this._x0+4*this._x1+t)/6,a=(this._y0+4*this._y1+e)/6;this._line?this._context.lineTo(n,a):this._context.moveTo(n,a);break;case 3:this._point=4;default:Ni(this,t,e);break}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e}};function Jp(t){return new Gc(t)}function Xc(t,e){this._basis=new Fi(t),this._beta=e}Xc.prototype={lineStart:function(){this._x=[],this._y=[],this._basis.lineStart()},lineEnd:function(){var t=this._x,e=this._y,n=t.length-1;if(n>0)for(var a=t[0],i=e[0],o=t[n]-a,s=e[n]-i,l=-1,c;++l<=n;)c=l/n,this._basis.point(this._beta*t[l]+(1-this._beta)*(a+c*o),this._beta*e[l]+(1-this._beta)*(i+c*s));this._x=this._y=null,this._basis.lineEnd()},point:function(t,e){this._x.push(+t),this._y.push(+e)}};var Qp=function t(e){function n(a){return e===1?new Fi(a):new Xc(a,e)}return n.beta=function(a){return t(+a)},n}(.85);function Bi(t,e,n){t._context.bezierCurveTo(t._x1+t._k*(t._x2-t._x0),t._y1+t._k*(t._y2-t._y0),t._x2+t._k*(t._x1-e),t._y2+t._k*(t._y1-n),t._x2,t._y2)}function ls(t,e){this._context=t,this._k=(1-e)/6}ls.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:Bi(this,this._x1,this._y1);break}(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2,this._x1=t,this._y1=e;break;case 2:this._point=3;default:Bi(this,t,e);break}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};var kp=function t(e){function n(a){return new ls(a,e)}return n.tension=function(a){return t(+a)},n}(0);function cs(t,e){this._context=t,this._k=(1-e)/6}cs.prototype={areaStart:Ar,areaEnd:Ar,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:{this._context.moveTo(this._x3,this._y3),this._context.closePath();break}case 2:{this._context.lineTo(this._x3,this._y3),this._context.closePath();break}case 3:{this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5);break}}},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._x3=t,this._y3=e;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=e);break;case 2:this._point=3,this._x5=t,this._y5=e;break;default:Bi(this,t,e);break}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};var qp=function t(e){function n(a){return new cs(a,e)}return n.tension=function(a){return t(+a)},n}(0);function us(t,e){this._context=t,this._k=(1-e)/6}us.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){(this._line||this._line!==0&&this._point===3)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:Bi(this,t,e);break}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};var _p=function t(e){function n(a){return new us(a,e)}return n.tension=function(a){return t(+a)},n}(0);const Hc=Math.abs,En=Math.atan2,jr=Math.cos,tm=Math.max,fs=Math.min,rr=Math.sin,oa=Math.sqrt,bn=1e-12,Fa=Math.PI,Ui=Fa/2,zi=2*Fa;function em(t){return t>1?0:t<-1?Fa:Math.acos(t)}function Yc(t){return t>=1?Ui:t<=-1?-Ui:Math.asin(t)}function ds(t,e,n){var a=t._x1,i=t._y1,o=t._x2,s=t._y2;if(t._l01_a>bn){var l=2*t._l01_2a+3*t._l01_a*t._l12_a+t._l12_2a,c=3*t._l01_a*(t._l01_a+t._l12_a);a=(a*l-t._x0*t._l12_2a+t._x2*t._l01_2a)/c,i=(i*l-t._y0*t._l12_2a+t._y2*t._l01_2a)/c}if(t._l23_a>bn){var f=2*t._l23_2a+3*t._l23_a*t._l12_a+t._l12_2a,g=3*t._l23_a*(t._l23_a+t._l12_a);o=(o*f+t._x1*t._l23_2a-e*t._l12_2a)/g,s=(s*f+t._y1*t._l23_2a-n*t._l12_2a)/g}t._context.bezierCurveTo(a,i,o,s,t._x2,t._y2)}function Wc(t,e){this._context=t,this._alpha=e}Wc.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:this.point(this._x2,this._y2);break}(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){if(t=+t,e=+e,this._point){var n=this._x2-t,a=this._y2-e;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(n*n+a*a,this._alpha))}switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;break;case 2:this._point=3;default:ds(this,t,e);break}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};var nm=function t(e){function n(a){return e?new Wc(a,e):new ls(a,0)}return n.alpha=function(a){return t(+a)},n}(.5);function Kc(t,e){this._context=t,this._alpha=e}Kc.prototype={areaStart:Ar,areaEnd:Ar,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 1:{this._context.moveTo(this._x3,this._y3),this._context.closePath();break}case 2:{this._context.lineTo(this._x3,this._y3),this._context.closePath();break}case 3:{this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5);break}}},point:function(t,e){if(t=+t,e=+e,this._point){var n=this._x2-t,a=this._y2-e;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(n*n+a*a,this._alpha))}switch(this._point){case 0:this._point=1,this._x3=t,this._y3=e;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=e);break;case 2:this._point=3,this._x5=t,this._y5=e;break;default:ds(this,t,e);break}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};var rm=function t(e){function n(a){return e?new Kc(a,e):new cs(a,0)}return n.alpha=function(a){return t(+a)},n}(.5);function Zc(t,e){this._context=t,this._alpha=e}Zc.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){(this._line||this._line!==0&&this._point===3)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){if(t=+t,e=+e,this._point){var n=this._x2-t,a=this._y2-e;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(n*n+a*a,this._alpha))}switch(this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:ds(this,t,e);break}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};var am=function t(e){function n(a){return e?new Zc(a,e):new us(a,0)}return n.alpha=function(a){return t(+a)},n}(.5);function Jc(t){return t<0?-1:1}function Qc(t,e,n){var a=t._x1-t._x0,i=e-t._x1,o=(t._y1-t._y0)/(a||i<0&&-0),s=(n-t._y1)/(i||a<0&&-0),l=(o*i+s*a)/(a+i);return(Jc(o)+Jc(s))*Math.min(Math.abs(o),Math.abs(s),.5*Math.abs(l))||0}function kc(t,e){var n=t._x1-t._x0;return n?(3*(t._y1-t._y0)/n-e)/2:e}function hs(t,e,n){var a=t._x0,i=t._y0,o=t._x1,s=t._y1,l=(o-a)/3;t._context.bezierCurveTo(a+l,i+l*e,o-l,s-l*n,o,s)}function ji(t){this._context=t}ji.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=this._t0=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x1,this._y1);break;case 3:hs(this,this._t0,kc(this,this._t0));break}(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){var n=NaN;if(t=+t,e=+e,!(t===this._x1&&e===this._y1)){switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;break;case 2:this._point=3,hs(this,kc(this,n=Qc(this,t,e)),n);break;default:hs(this,this._t0,n=Qc(this,t,e));break}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e,this._t0=n}}};function qc(t){this._context=new _c(t)}(qc.prototype=Object.create(ji.prototype)).point=function(t,e){ji.prototype.point.call(this,e,t)};function _c(t){this._context=t}_c.prototype={moveTo:function(t,e){this._context.moveTo(e,t)},closePath:function(){this._context.closePath()},lineTo:function(t,e){this._context.lineTo(e,t)},bezierCurveTo:function(t,e,n,a,i,o){this._context.bezierCurveTo(e,t,a,n,o,i)}};function im(t){return new ji(t)}function om(t){return new qc(t)}function tu(t){this._context=t}tu.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x=[],this._y=[]},lineEnd:function(){var t=this._x,e=this._y,n=t.length;if(n)if(this._line?this._context.lineTo(t[0],e[0]):this._context.moveTo(t[0],e[0]),n===2)this._context.lineTo(t[1],e[1]);else for(var a=eu(t),i=eu(e),o=0,s=1;s=0;--e)i[e]=(s[e]-i[e+1])/o[e];for(o[n-1]=(t[n]+i[n-1])/2,e=0;e=0&&(this._t=1-this._t,this._line=1-this._line)},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;default:{if(this._t<=0)this._context.lineTo(this._x,e),this._context.lineTo(t,e);else{var n=this._x*(1-this._t)+t*this._t;this._context.lineTo(n,this._y),this._context.lineTo(n,e)}break}}this._x=t,this._y=e}};function cm(t){return new Vi(t,.5)}function um(t){return new Vi(t,0)}function fm(t){return new Vi(t,1)}function dm(t){const e=this;let n;return e.isLineType(t)?n=e.generateGetLinePoints(e.getShapeIndices(e.isLineType)):e.isBarType(t)&&(n=e.generateGetBarPoints(e.getShapeIndices(e.isBarType))),n}var hm={getDrawShape(){const t=this,e=t.config.axis_rotated,{hasRadar:n,hasTreemap:a}=t.state,i={type:{},indices:{},pos:{}};if(!a&&["bar","candlestick","line","area"].forEach(o=>{const s=Cn(/^(bubble|scatter)$/.test(o)?"line":o);if(t.hasType(o)||t.hasTypeOf(s)||o==="line"&&(t.hasType("bubble")||t.hasType("scatter"))){const l=t.getShapeIndices(t[`is${s}Type`]),c=t[`generateDraw${s}`];i.indices[o]=l,i.type[o]=c?c.bind(t)(l,!1):void 0}}),!t.hasArcType()||n||a){let o,s;a||(o=n?t.radarCircleX:e?t.circleY:t.circleX,s=n?t.radarCircleY:e?t.circleX:t.circleY),i.pos={xForText:t.generateXYForText(i.indices,!0),yForText:t.generateXYForText(i.indices,!1),cx:(o||function(){}).bind(t),cy:(s||function(){}).bind(t)}}return i},getShapeIndices(t){const e=this,{config:n}=e,a=n.data_xs,i=cn(a),o={};let s=i?{}:0;return i&&Mo(Object.keys(a).map(l=>a[l])).forEach(l=>{s[l]=0,o[l]={}}),e.filterTargetsToShow(e.data.targets.filter(t,e)).forEach(l=>{var c;const f=l.id in a?a[l.id]:"",g=f?o[f]:o;for(let v=0,m;m=n.data_groups[v];v++)if(!(m.indexOf(l.id)<0))for(let S=0,P;P=m[S];S++){if(P in g){g[l.id]=g[P];break}l.id!==P&&f&&(g[P]=(c=g[l.id])!=null?c:s[f])}ln(g[l.id])&&(g[l.id]=f?s[f]++:s++,g.__max__=(f?s[f]:s)-1)}),o},getIndices(t,e,n){const a=this,{data_xs:i,bar_indices_removeNull:o}=a.config,{id:s,index:l}=e;if(a.isBarType(s)&&o){const c={};return a.getAllValuesOnIndex(l,!0).forEach((f,g)=>{c[f.id]=g,c.__max__=g}),c}return cn(i)?t[i[s]]:t},getIndicesMax(t){return cn(this.config.data_xs)?Object.keys(t).map(e=>t[e].__max__||0).reduce((e,n)=>e+n):t.__max__},getShapeX(t,e,n){const a=this,{config:i,scale:o}=a,s=n?o.subX:o.zoom||o.x,l=i.bar_overlap,c=i.bar_padding,f=(v,m)=>v+m,g=nr(t)&&(t._$total.length?t._$total.reduce(f)/2:0);return v=>{const m=a.getIndices(e,v,"getShapeX"),S=v.id in m?m[v.id]:0,P=(m.__max__||0)+1;let N=0;if(cn(v.x)){const L=s(v.x,!0);if(g){const w=t[v.id]||t._$width;N=l?L-w/2:L-w+t._$total.slice(0,S+1).reduce(f)-g}else N=L-(he(t)?t:t._$width)*(P/2-(l?1:S))}return t&&N&&P>1&&c&&(S&&(N+=c*S),P>2?N-=(P-1)*c/2:P===2&&(N-=c/2)),N}},getShapeY(t){const e=this,n=e.isStackNormalized();return a=>{let{value:i}=a;return he(a)?i=a:e.isAreaRangeType(a)?i=e.getBaseValue(a,"mid"):n?i=e.getRatio("index",a,!0):e.isBubbleZType(a)?i=e.getBubbleZData(a.value,"y"):e.isBarRangeType(a)&&(i=i[1]),e.getYScaleById(a.id,t)(i)}},getShapeYMin(t){const e=this,n=e.axis.getId(t),a=e.scale[n],[i]=a.domain(),o=e.config[`axis_${n}_inverted`];return!e.isGrouped(t)&&!o&&i>0?i:0},getShapeOffsetData(t){const e=this,n=e.orderTargets(e.filterTargetsToShow(e.data.targets.filter(t,e))),a=e.isStackNormalized(),i=n.map(s=>{let l=s.values;const c={};e.isStepType(s)&&(l=e.convertValuesToStep(l));const f=l.reduce((g,v)=>{const m=Number(v.x);return g[m]=v,c[m]=a?e.getRatio("index",v,!0):v.value,g},{});return{id:s.id,rowValues:l,rowValueMapByXValue:f,values:c}});return{indexMapByTargetId:n.reduce((s,{id:l},c)=>(s[l]=c,s),{}),shapeOffsetTargets:i}},getShapeOffset(t,e,n){const a=this,{shapeOffsetTargets:i,indexMapByTargetId:o}=a.getShapeOffsetData(t),s=a.config.data_groupsZeroAs;return(l,c)=>{const{id:f,value:g,x:v}=l,m=a.getIndices(e,l),S=a.getYScaleById(f,n);if(a.isBarRangeType(l))return S(g[0]);const P=Number(v),N=S(s==="zero"?0:a.getShapeYMin(f));let L=N;return i.filter(w=>w.id!==f&&m[w.id]===m[f]).forEach(w=>{const{id:X,rowValueMapByXValue:W,rowValues:H,values:k}=w;if(o[X]=0&&he(K)&&(g!==0||s==="positive"&&K>0||s==="negative"&&K<0)&&(L+=S(K)-N)}}),L}},circleY(t,e){const n=this,a=t.id;let i;return n.isGrouped(a)&&(i=dm.bind(n)(t)),i?i(t,e)[0][1]:n.getYScaleById(a)(n.getBaseValue(t))},getBarW(t,e,n){var a,i,o,s,l;const c=this,{config:f,org:g,scale:v,state:m}=c,S=c.getMaxDataCount(),P=t==="bar"&&((a=f.data_groups)==null?void 0:a.length),N=`${t}_width`,{k:L}=(o=(i=c.getZoomTransform)==null?void 0:i.call(c))!=null?o:{k:1},w=[(s=f.axis_x_min)!=null?s:g.xDomain[0],(l=f.axis_x_max)!=null?l:g.xDomain[1]].map(c.axis.isTimeSeries()?Yn.bind(c):Number);let X=e.tickInterval(S);if(v.zoom&&!c.axis.isCategorized()&&L>1){const k=w.every((K,at)=>K===g.xDomain[at]);X=g.xDomain.map((K,at)=>{const ht=k?K:K-Math.abs(w[at]);return v.zoom(ht)}).reduce((K,at)=>Math.abs(K)+at)/S}const W=k=>{const K=k?f[N][k]:f[N],at=k?K.ratio:f[`${N}_ratio`],ht=k?K.max:f[`${N}_max`],$t=he(K)?K:ve(K)?K.call(c,m.width,n,S):n?X*at/n:0;return ht&&$t>ht?ht:$t};let H=W();return!P&&nr(f[N])&&(H={_$width:H,_$total:[]},c.filterTargetsToShow(c.data.targets).forEach(k=>{f[N][k.id]&&(H[k.id]=W(k.id),H._$total.push(H[k.id]||H._$width))})),H},getShapeByIndex(t,e,n){const a=this,{$el:i}=a,o=De(e)?`-${e}`:"";let s=i[t];return s&&!s.empty()?s=s.filter(l=>n?l.id===n:!0).filter(l=>De(e)?l.index===e:!0):s=(n?i.main.selectAll(`.${Ue[`${t}s`]}${a.getTargetSelectorSuffix(n)}`):i.main).selectAll(`.${Ue[t]}${o}`),s},isWithinShape(t,e){var n;const a=this,i=ot(t);let o;return a.isTargetToShow(e.id)?(n=a.hasValidPointType)!=null&&n.call(a,t.nodeName)?o=a.isStepType(e)?a.isWithinStep(t,a.getYScaleById(e.id)(a.getBaseValue(e))):a.isWithinCircle(t,a.isBubbleType(e)?a.pointSelectR(e)*1.5:0):t.nodeName==="path"&&(o=i.classed(Ue.bar)?a.isWithinBar(t):!0):o=!1,o},getInterpolate(t){const n=this.getInterpolateType(t);return{basis:Kp,"basis-closed":Zp,"basis-open":Jp,bundle:Qp,cardinal:kp,"cardinal-closed":qp,"cardinal-open":_p,"catmull-rom":nm,"catmull-rom-closed":rm,"catmull-rom-open":am,"monotone-x":im,"monotone-y":om,natural:sm,"linear-closed":lm,linear:gs,step:cm,"step-after":fm,"step-before":um}[n]},getInterpolateType(t){const e=this,{config:n}=e,a=n.spline_interpolation_type,i=e.isInterpolationType(a)?a:"cardinal";return e.isSplineType(t)?i:e.isStepType(t)?n.line_step_type:"linear"},isWithinBar(t){const e=Hn(this.state.event,t),n=Hl(t),[a,i]=n,o=Math.min(a.x,i.x),s=Math.min(a.y,i.y),l=this.config.bar_sensitivity,{width:c,height:f}=t.getBBox(),g=o-l,v=o+c+l,m=s+f+l,S=s-l;return ge in t?gm(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,Rn=(t,e,n)=>vm(t,typeof e!="symbol"?e+"":e,n);class Vr{constructor(e){Rn(this,"api"),Rn(this,"config"),Rn(this,"cache"),Rn(this,"$el"),Rn(this,"state"),Rn(this,"charts"),Rn(this,"data",{xs:{},targets:[]}),Rn(this,"axis"),Rn(this,"scale",{x:null,y:null,y2:null,subX:null,subY:null,subY2:null,zoom:null}),Rn(this,"org",{xScale:null,xDomain:null}),Rn(this,"color"),Rn(this,"patterns"),Rn(this,"levelColor"),Rn(this,"point"),Rn(this,"brush"),Rn(this,"format",{extraLineClasses:null,xAxisTick:null,dataTime:null,defaultAxisTime:null,axisTime:null});const n=this;n.api=e,n.config=new Nr,n.cache=new gv;const a=new uv;n.$el=a.getStore("element"),n.state=a.getStore("state"),n.$T=n.$T.bind(n)}$T(e,n,a){const{config:i,state:o}=this,s=i.transition_duration,l=i.subchart_show;let c=e;return c&&("tagName"in c&&(c=ot(c)),c=(n!==!1&&s||n)&&(!o.zooming||o.dragging)&&!o.resizing&&o.rendered&&!l?c.transition(a).duration(s):c),c}beforeInit(){const e=this;e.callPluginHook("$beforeInit"),_e(e.config.onbeforeinit,e.api)}afterInit(){const e=this;e.callPluginHook("$afterInit"),_e(e.config.onafterinit,e.api)}init(){const e=this,{config:n,state:a,$el:i}=e,o=n.boost_useCssRule;if(vv(e),a.hasRadar=!a.hasAxis&&e.hasType("radar"),a.hasFunnel=!a.hasAxis&&e.hasType("funnel"),a.hasTreemap=!a.hasAxis&&e.hasType("treemap"),a.hasAxis=!e.hasArcType()&&!a.hasFunnel&&!a.hasTreemap,a.datetimeId=`bb-${+new Date*gr()}`,o){const l=gn.createElement("style");l.type="text/css",gn.head.appendChild(l),a.style={rootSelctor:`.${a.datetimeId}`,sheet:l.sheet},i.style=l}const s={element:n.bindto,classname:"bb"};Be(n.bindto)&&(s.element=n.bindto.element||"#chart",s.classname=n.bindto.classname||s.classname),i.chart=ve(s.element.node)?n.bindto.element:ot(s.element||[]),i.chart.empty()&&(i.chart=ot(gn.body.appendChild(gn.createElement("div")))),i.chart.html("").classed(s.classname,!0).classed(a.datetimeId,o).style("position","relative"),e.initParams(),e.initToRender()}initToRender(e){const n=this,{config:a,state:i,$el:{chart:o}}=n,s=()=>nv(o,{display:"none",visibility:"hidden"}),l=a.render.lazy===!1?!1:a.render.lazy||s(),c=Ke.MutationObserver;l&&c&&a.render.observe!==!1&&!e&&new c((f,g)=>{s()||(g.disconnect(),!i.rendered&&n.initToRender(!0))}).observe(o.node(),{attributes:!0,attributeFilter:["class","style"]}),(!l||e)&&n.convertData(a,f=>{n.initWithData(f),n.afterInit()})}initParams(){var e;const n=this,{config:a,format:i,state:o}=n,s=a.axis_rotated;if(n.color=n.generateColor(),n.levelColor=n.generateLevelColor(),a.padding===!1&&(a.axis_x_show=!1,a.axis_y_show=!1,a.axis_y2_show=!1,a.subchart_show=!1),(n.hasPointType()||(e=n.hasLegendDefsPoint)!=null&&e.call(n))&&(n.point=n.generatePoint()),o.hasAxis){n.initClip(),i.extraLineClasses=n.generateExtraLineClass(),i.dataTime=a.data_xLocaltime?Ws:Ks,i.axisTime=a.axis_x_localtime?ao:io;const l=n.config.zoom_enabled&&n.config.zoom_type==="drag";i.defaultAxisTime=c=>{const{x:f,zoom:g}=n.scale,v=l?g:g&&f.orgDomain().toString()!==g.domain().toString(),m=c.getMilliseconds()&&".%L"||c.getSeconds()&&".:%S"||c.getMinutes()&&"%I:%M"||c.getHours()&&"%I %p"||c.getDate()!==1&&"%b %d"||v&&c.getDate()===1&&"%b'%y"||c.getMonth()&&"%-m/%-d"||"%Y";return i.axisTime(m)(c)}}o.isLegendRight=a.legend_position==="right",o.isLegendInset=a.legend_position==="inset",o.isLegendTop=a.legend_inset_anchor==="top-left"||a.legend_inset_anchor==="top-right",o.isLegendLeft=a.legend_inset_anchor==="top-left"||a.legend_inset_anchor==="bottom-left",o.rotatedPadding.top=n.getResettedPadding(o.rotatedPadding.top),o.rotatedPadding.right=s&&!a.axis_x_show?0:30,o.inputType=rv(a.interaction_inputType_mouse,a.interaction_inputType_touch)}initWithData(e){var n,a,i;const o=this,{config:s,scale:l,state:c,$el:f,org:g}=o,{hasAxis:v,hasFunnel:m,hasTreemap:S}=c,P=s.interaction_enabled,N=o.hasType("polar"),L=s.data_labels_backgroundColors;if(v&&(o.axis=o.getAxisInstance(),s.zoom_enabled&&o.initZoom()),o.data.xs={},o.data.targets=o.convertDataToTargets(e),s.data_filter&&(o.data.targets=o.data.targets.filter(s.data_filter.bind(o.api))),s.data_hide&&o.addHiddenTargetIds(s.data_hide===!0?o.mapToIds(o.data.targets):s.data_hide),s.legend_hide&&o.addHiddenLegendIds(s.legend_hide===!0?o.mapToIds(o.data.targets):s.legend_hide),o.updateSizes(),o.updateScales(!0),v){const{x:W,y:H,y2:k,subX:K,subY:at,subY2:ht}=l;W&&(W.domain(na(o.getXDomain(o.data.targets),!s.axis_x_inverted)),K.domain(W.domain()),g.xDomain=W.domain()),H&&(H.domain(o.getYDomain(o.data.targets,"y")),at.domain(H.domain())),k&&(k.domain(o.getYDomain(o.data.targets,"y2")),ht&&ht.domain(k.domain()))}if(f.svg=f.chart.append("svg").style("overflow","hidden").style("display","block"),P&&c.inputType){const W=c.inputType==="touch",{onclick:H,onover:k,onout:K}=s;f.svg.on("click",(H==null?void 0:H.bind(o.api))||null).on(W?"touchstart":"mouseenter",(k==null?void 0:k.bind(o.api))||null).on(W?"touchend":"mouseleave",(K==null?void 0:K.bind(o.api))||null)}s.svg_classname&&f.svg.attr("class",s.svg_classname);const w=ve(s.color_tiles)&&o.patterns;(v||w||N||S||L||(n=o.hasLegendDefsPoint)!=null&&n.call(o))&&(f.defs=f.svg.append("defs"),v&&["id","idXAxis","idYAxis","idGrid"].forEach(W=>{o.appendClip(f.defs,c.clip[W])}),o.generateTextBGColorFilter(L),w&&o.patterns.forEach(W=>f.defs.append(()=>W.node))),o.updateSvgSize(),o.bindResize();const X=f.svg.append("g").classed(Se.main,!0).attr("transform",m||S?null:o.getTranslate("main"));if(f.main=X,s.subchart_show&&o.initSubchart(),s.tooltip_show&&o.initTooltip(),s.title_text&&o.initTitle(),!S&&s.legend_show&&o.initLegend(),s.data_empty_label_text&&X.append("text").attr("class",`${On.text} ${Se.empty}`).attr("text-anchor","middle").attr("dominant-baseline","middle"),v&&(s.regions.length&&o.initRegion(),!s.clipPath&&o.axis.init()),X.append("g").classed(Se.chart,!0).attr("clip-path",v?c.clip.path:null),o.callPluginHook("$init"),o.initChartElements(),v&&(P&&((a=o.initEventRect)==null||a.call(o)),o.initGrid(),s.clipPath&&((i=o.axis)==null||i.init())),o.updateTargets(o.data.targets),o.updateDimension(),_e(s.oninit,o.api),o.setBackground(),o.redraw({withTransition:!1,withTransform:!0,withUpdateXDomain:!0,withUpdateOrgXDomain:!0,withTransitionForAxis:!1,initializing:!0}),s.data_onmin||s.data_onmax){const W=o.getMinMaxData();_e(s.data_onmin,o.api,W.min),_e(s.data_onmax,o.api,W.max)}s.tooltip_show&&o.initShowTooltip(),c.rendered=!0}initChartElements(){const e=this,{hasAxis:n,hasRadar:a,hasTreemap:i}=e.state,o=[];if(n){const s=["bar","bubble","candlestick","line"];e.config.bar_front&&s.push(s.shift()),s.forEach(l=>{const c=Cn(l);(l==="line"&&e.hasTypeOf(c)||e.hasType(l))&&o.push(c)})}else if(i)o.push("Treemap");else if(e.hasType("funnel"))o.push("Funnel");else{const s=e.hasType("polar");a||o.push("Arc","Pie"),e.hasType("gauge")?o.push("Gauge"):a?o.push("Radar"):s&&o.push("Polar")}o.forEach(s=>{e[`init${s}`]()}),cn(e.config.data_labels)&&!e.hasArcType(null,["radar"])&&e.initText()}setChartElements(){const e=this,{$el:{chart:n,svg:a,defs:i,main:o,tooltip:s,legend:l,title:c,grid:f,needle:g,arcs:v,circle:m,bar:S,candlestick:P,line:N,area:L,text:w}}=e;e.api.$={chart:n,svg:a,defs:i,main:o,tooltip:s,legend:l,title:c,grid:f,arc:v,circles:m,bar:{bars:S},candlestick:P,line:{lines:N,areas:L},needle:g,text:{texts:w}}}setBackground(){const e=this,{config:{background:n},state:a,$el:{svg:i}}=e;if(cn(n)){const o=i.select("g").insert(n.imgUrl?"image":"rect",":first-child");n.imgUrl?o.attr("href",n.imgUrl):n.color&&o.style("fill",n.color).attr("clip-path",a.clip.path),o.attr("class",n.class||null).attr("width","100%").attr("height","100%")}}updateTargets(e){var n;const a=this,{hasAxis:i,hasFunnel:o,hasRadar:s,hasTreemap:l}=a.state,c=g=>a[`updateTargetsFor${g}`](e.filter(a[`is${g}Type`].bind(a)));if(a.updateTargetsForText(e),i)["bar","candlestick","line"].forEach(g=>{const v=Cn(g);(g==="line"&&a.hasTypeOf(v)||a.hasType(g))&&c(v)}),a.updateTargetsForSubchart&&a.updateTargetsForSubchart(e);else if(a.hasArcType(e)){let g="Arc";s?g="Radar":a.hasType("polar")&&(g="Polar"),c(g)}else o?c("Funnel"):l&&c("Treemap");const f=a.hasType("bubble")||a.hasType("scatter");f&&((n=a.updateTargetForCircle)==null||n.call(a)),a.filterTargetsToShowAtInit(f)}filterTargetsToShowAtInit(e=!1){const n=this,{$el:{svg:a},$T:i}=n;let o=`.${Se.target}`;e&&(o+=`, .${$n.chartCircles} > .${$n.circles}`),i(a.selectAll(o).filter(s=>n.isTargetToShow(s.id))).style("opacity",null)}getWithOption(e){const n={Dimension:!0,EventRect:!0,Legend:!1,Subchart:!0,Transform:!1,Transition:!0,TrimXDomain:!0,UpdateXAxis:"UpdateXDomain",UpdateXDomain:!1,UpdateOrgXDomain:!1,TransitionForExit:"Transition",TransitionForAxis:"Transition",Y:!0};return Object.keys(n).forEach(a=>{let i=n[a];ze(i)&&(i=n[i]),n[a]=$r(e,`with${a}`,i)}),n}initialOpacity(e){const n=this,{withoutFadeIn:a}=n.state;return n.getBaseValue(e)!==null&&a[e.id]?null:"0"}bindResize(){const e=this,{$el:n,config:a,state:i}=e,o=xv(a.resize_timer),s=[];s.push(()=>_e(a.onresize,e.api)),/^(true|parent)$/.test(a.resize_auto)&&s.push(()=>{i.resizing=!0,a.legend_show&&(e.updateSizes(),e.updateLegend()),e.api.flush(!1)}),s.push(()=>{_e(a.onresized,e.api),i.resizing=!1}),s.forEach(l=>o.add(l)),e.resizeFunction=o,a.resize_auto==="parent"?(e.resizeFunction.resizeObserver=new ResizeObserver(e.resizeFunction.bind(e))).observe(n.chart.node().parentNode):Ke.addEventListener("resize",e.resizeFunction)}callPluginHook(e,...n){this.config.plugins.forEach(a=>{e==="$beforeInit"&&(a.$$=this,this.api.plugins.push(a)),a[e](...n)})}}yn(Vr.prototype,[Mv,Dv,Lv,jv,Vv,Yv,Wv,zv,Kv,Zv,Jv,Bp,hm,Up,zp,Gp,Xp,Hp,Yp,Wp]);function pm(t){const e=this.config;let n,a,i;const o=()=>{const s=a.shift();if(s&&n&&nr(n)&&s in n)return n=n[s],o();if(!s)return n};Object.keys(e).forEach(s=>{n=t,a=s.split("_"),i=o(),Qe(i)&&(e[s]=i)}),this.api&&(this.state.orgConfig=t)}var mm={resize(t){const e=this.internal,{config:n,state:a}=e;a.rendered&&(n.size_width=t?t.width:null,n.size_height=t?t.height:null,a.resizing=!0,this.flush(!1),e.resizeFunction())},flush(t){var e,n;const a=this.internal,{state:i,$el:{zoomResetBtn:o}}=a;i.rendered?(i.resizing?(e=a.brush)==null||e.updateResize():(n=a.axis)==null||n.setOrient(),o==null||o.style("display","none"),a.scale.zoom=null,t?a.redraw({withTransform:!0,withUpdateXDomain:!0,withUpdateOrgXDomain:!0,withLegend:!0}):a.updateAndRedraw({withLegend:!0,withTransition:!1,withTransitionForTransform:!1}),!i.resizing&&a.brush&&(a.brush.getSelection().call(a.brush.move),a.unselectRect())):a.initToRender(!0)},destroy(){var t;const e=this.internal,{$el:{chart:n,style:a,svg:i}}=e;if(cn(e)){e.callPluginHook("$willDestroy"),e.charts.splice(e.charts.indexOf(this),1),e.unbindAllEvents(),i.select("*").interrupt(),e.resizeFunction.clear(),(t=e.resizeFunction.resizeObserver)==null||t.disconnect(),Ke.removeEventListener("resize",e.resizeFunction),n.classed("bb",!1).style("position",null).selectChildren().remove(),a&&a.parentNode.removeChild(a),Object.keys(this).forEach(o=>{o==="internal"&&Object.keys(e).forEach(s=>{e[s]=null}),this[o]=null,delete this[o]});for(const o in this)this[o]=()=>{}}return null},config(t,e,n){const a=this.internal,{config:i,state:o}=a,s=t==null?void 0:t.replace(/\./g,"_");let l;return t&&s in i?Qe(e)?(i[s]=e,l=e,n&&this.flush()):l=i[s]:(arguments.length===0||qn(t))&&(l=o.orgConfig),l}},ym={color(t){return this.internal.color(t)}};const au=function(t){const{targets:e}=this.internal.data;if(!ln(t)){const n=je(t)?t:[t];return e.filter(a=>n.some(i=>i===a.id))}return e};yn(au,{shown:function(t){return this.internal.filterTargetsToShow(this.data(t))},values:function(t,e=!0){let n=null;if(t){const a=this.data(t);je(a)&&(n=[],a.forEach(i=>{const o=i.values.map(s=>s.value);e?n=n.concat(o):n.push(o)}))}return n},names:function(t){return this.internal.updateDataAttributes("names",t)},colors:function(t){return this.internal.updateDataAttributes("colors",t)},axes:function(t){return this.internal.updateDataAttributes("axes",t)},min:function(){return this.internal.getMinMaxData().min},max:function(){return this.internal.getMinMaxData().max}});var xm={data:au};const Tm=t=>{var e,n;return(n=(e=Ke).btoa)==null?void 0:n.call(e,encodeURIComponent(t).replace(/%([0-9A-F]{2})/g,(a,i)=>String.fromCharCode(+`0x${i}`)))};function $m(t,e,n){const{width:a,height:i}=e||n,o=new XMLSerializer,s=t.cloneNode(!0),l=tv(Lr(gn.styleSheets)).filter(m=>m.cssText).map(m=>m.cssText);s.setAttribute("xmlns",ae.xhtml),s.style.margin="0",s.style.padding="0",e.preserveFontStyle&&s.querySelectorAll("text").forEach(m=>{m.innerHTML=""});const c=o.serializeToString(s),f=gn.createElement("style");f.appendChild(gn.createTextNode(l.join(` +`)));const g=o.serializeToString(f),v=` + + ${g} + ${c.replace(/(url\()[^#]+/g,"$1")} + `;return`data:image/svg+xml;base64,${Tm(v)}`}function Sm(t,e){const{top:n,left:a}=e,{x:i,y:o}=t.getBBox(),{a:s,b:l,c,d:f,e:g,f:v}=t.getScreenCTM(),{width:m,height:S}=t.getBoundingClientRect();return{x:s*i+c*o+g-a,y:l*i+f*o+v-n+(S-Math.round(S/4)),width:m,height:S}}function Am(t){const{left:e,top:n}=t.getBoundingClientRect(),a=o=>o.textContent||o.childElementCount,i=[];return Lr(t.querySelectorAll("text")).filter(a).forEach(o=>{const s=l=>{const{fill:c,fontFamily:f,fontSize:g,textAnchor:v,transform:m}=Ke.getComputedStyle(l),{x:S,y:P,width:N,height:L}=Sm(l,{left:e,top:n});return{[l.textContent]:{x:S,y:P,width:N,height:L,fill:c,fontFamily:f,fontSize:g,textAnchor:v,transform:m}}};if(o.childElementCount>1){const l=[];return Lr(o.querySelectorAll("tspan")).filter(a).forEach(c=>{i.push(s(c))}),l}else i.push(s(o))}),i}function Em(t,e){e.forEach(n=>{Object.keys(n).forEach(a=>{const{x:i,y:o,width:s,height:l,fill:c,fontFamily:f,fontSize:g,transform:v}=n[a];if(t.save(),t.font=`${g} ${f}`,t.fillStyle=c,v==="none")t.fillText(a,i,o);else{const m=v.replace(/(matrix|\(|\))/g,"").split(",");m.splice(4).every(S=>+S==0)?(m.push(i+s-s/4),m.push(o-l+l/3)):(m.push(i),m.push(o)),t.transform(...m),t.fillText(a,0,0)}t.restore()})})}var bm={export(t,e){const n=this.internal,{state:a,$el:{chart:i,svg:o}}=n,{width:s,height:l}=a.current,c=ea(Object.create(null),{width:s,height:l,preserveAspectRatio:!0,preserveFontStyle:!1,mimeType:"image/png"},t),f=$m(i.node(),c,{width:s,height:l}),g=c.preserveFontStyle?Am(o.node()):[];if(e&&ve(e)){const v=new Image;v.crossOrigin="Anonymous",v.onload=()=>{const m=gn.createElement("canvas"),S=m.getContext("2d");m.width=c.width||s,m.height=c.height||l,S.drawImage(v,0,0),g.length&&(Em(S,g),g.length=0),e.bind(this)(m.toDataURL(c.mimeType))},v.src=f}return f}},Rm={focus(t){const e=this.internal,{state:n}=e,a=e.mapToTargetIds(t),i=e.$el.svg.selectAll(e.selectorTargets(a.filter(e.isTargetToShow,e)));this.revert(),this.defocus(),i.classed(qe.focused,!0).classed(qe.defocused,!1),e.hasArcType()&&!n.hasRadar&&(e.expandArc(a),e.hasType("gauge")&&e.markOverlapped(t,e,`.${Un.gaugeValue}`)),e.toggleFocusLegend(a,!0),n.focusedTargetIds=a,n.defocusedTargetIds=n.defocusedTargetIds.filter(o=>a.indexOf(o)<0)},defocus(t){const e=this.internal,{state:n}=e,a=e.mapToTargetIds(t);e.$el.svg.selectAll(e.selectorTargets(a.filter(e.isTargetToShow,e))).classed(qe.focused,!1).classed(qe.defocused,!0),e.hasArcType(null,["polar"])&&(e.unexpandArc(a),e.hasType("gauge")&&e.undoMarkOverlapped(e,`.${Un.gaugeValue}`)),e.toggleFocusLegend(a,!1),n.focusedTargetIds=n.focusedTargetIds.filter(o=>a.indexOf(o)<0),n.defocusedTargetIds=a},revert(t){const e=this.internal,{config:n,state:a,$el:i}=e,o=e.mapToTargetIds(t);i.svg.selectAll(e.selectorTargets(o)).classed(qe.focused,!1).classed(qe.defocused,!1),e.hasArcType(null,["polar"])&&e.unexpandArc(o),n.legend_show&&(e.showLegend(o.filter(e.isLegendToShow.bind(e))),i.legend.selectAll(e.selectorLegends(o)).filter(function(){return ot(this).classed(qe.legendItemFocused)}).classed(qe.legendItemFocused,!1)),a.focusedTargetIds=[],a.defocusedTargetIds=[]}},Im={legend:{show:function(t){const e=this.internal;e.showLegend(e.mapToTargetIds(t)),e.updateAndRedraw({withLegend:!0})},hide:function(t){const e=this.internal;e.hideLegend(e.mapToTargetIds(t)),e.updateAndRedraw({withLegend:!0})}}},Om={load(t){const e=this.internal,{config:n}=e;t.xs&&e.addXs(t.xs),"names"in t&&this.data.names(t.names),"classes"in t&&Object.keys(t.classes).forEach(a=>{n.data_classes[a]=t.classes[a]}),"categories"in t&&e.axis.isCategorized()&&(n.axis_x_categories=t.categories),"axes"in t&&Object.keys(t.axes).forEach(a=>{n.data_axes[a]=t.axes[a]}),"colors"in t&&Object.keys(t.colors).forEach(a=>{n.data_colors[a]=t.colors[a]}),"unload"in t&&t.unload!==!1?e.unload(e.mapToTargetIds(t.unload===!0?null:t.unload),()=>{jl(()=>e.loadFromArgs(t))}):e.loadFromArgs(t)},unload(t){const e=this.internal;let n=t||{};qn(n)&&this.tooltip.hide(),je(n)?n={ids:n}:ze(n)&&(n={ids:[n]});const a=e.mapToTargetIds(n.ids);e.unload(a,()=>{e.redraw({withUpdateOrgXDomain:!0,withUpdateXDomain:!0,withLegend:!0}),e.cache.remove(a),cc.call(e,n.done,n.resizeAfter)})}};function iu(t,e,n){const a=this.internal,i=a.mapToTargetIds(e),o=a.state.hiddenTargetIds.map(c=>i.indexOf(c)>-1&&c).filter(Boolean);a.state.toggling=!0,a[`${t?"remove":"add"}HiddenTargetIds`](i);const s=a.$el.svg.selectAll(a.selectorTargets(i)),l=t?null:"0";t&&o.length&&(s.style("display",null),_e(a.config.data_onshown,this,o)),a.$T(s).style("opacity",l,"important").call(Si,()=>{var c;!t&&o.length===0&&(s.style("display","none"),_e((c=a.config)==null?void 0:c.data_onhidden,this,i)),s.style("opacity",l)}),n.withLegend&&a[`${t?"show":"hide"}Legend`](i),a.redraw({withUpdateOrgXDomain:!0,withUpdateXDomain:!0,withLegend:!0}),a.state.toggling=!1}var Cm={show(t,e={}){iu.call(this,!0,t,e)},hide(t,e={}){iu.call(this,!1,t,e)},toggle(t,e={}){const n=this.internal,a={show:[],hide:[]};n.mapToTargetIds(t).forEach(i=>a[n.isTargetToShow(i)?"hide":"show"].push(i)),a.show.length&&this.show(a.show,e),a.hide.length&&setTimeout(()=>this.hide(a.hide,e),0)}},Pm={tooltip:{show:function(t){var e,n,a;const i=this.internal,{$el:o,config:s,state:{eventReceiver:l,hasFunnel:c,hasTreemap:f,inputType:g}}=i;let v,m;if(t.mouse&&(m=t.mouse),t.data){const{data:S}=t,P=(e=i.getYScaleById(S.id))==null?void 0:e(S.value);if((c||f)&&S.id){const N=i.selectorTarget(S.id,void 0,`.${sn.shape}`);l.rect=o.main.select(N)}else i.isMultipleX()?m=[i.xx(S),P]:(s.tooltip_grouped||(m=[0,P]),v=(a=S.index)!=null?a:i.hasArcType()&&S.id?(n=i.getArcElementByIdOrIndex(S.id))==null?void 0:n.datum().index:i.getIndexByX(S.x))}else Qe(t.x)?v=i.getIndexByX(t.x):Qe(t.index)&&(v=t.index);(g==="mouse"?["mouseover","mousemove"]:["touchstart"]).forEach(S=>{i.dispatchEvent(S,v,m)})},hide:function(){var t,e,n;const a=this.internal,{state:{inputType:i},$el:{tooltip:o}}=a,s=o==null?void 0:o.datum();if(s){const{index:l}=JSON.parse(s.current)[0];(i==="mouse"?["mouseout"]:["touchend"]).forEach(c=>{a.dispatchEvent(c,l)})}i==="touch"&&a.callOverOutForTouch(),a.hideTooltip(!0),(t=a.hideGridFocus)==null||t.call(a),(e=a.unexpandCircles)==null||e.call(a),(n=a.expandBarTypeShapes)==null||n.call(a,!1)}}},wm=Object.defineProperty,Mm=(t,e,n)=>e in t?wm(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,ou=(t,e,n)=>Mm(t,typeof e!="symbol"?e+"":e,n);class Er{constructor(e){ou(this,"plugins",[]),ou(this,"internal");const n=new Vr(this);this.internal=n,function a(i,o,s){Object.keys(i).forEach(l=>{const c=ve(i[l]),f=o!==s,g=cn(i[l]),v=g&&Object.keys(i[l]).length>0;c&&(!f&&v||f)?o[l]=i[l].bind(s):g&&!c?o[l]={}:o[l]=i[l],v&&a(i[l],o[l],s)})}(Er.prototype,this,this),pm.call(n,e),n.beforeInit(),n.init()}}yn(Er.prototype,[mm,ym,xm,bm,Rm,Im,Om,Cm,Pm]);function su(t=!1,e,n,a){const i=this,{config:o,$el:{main:s}}=i,l=o.data_selection_grouped,c=o.data_selection_isselectable.bind(i.api);o.data_selection_enabled&&s.selectAll(`.${sn.shapes}`).selectAll(`.${sn.shape}`).each(function(f){const g=ot(this),{id:v,index:m}=f.data?f.data:f,S=i.getToggle(this,f).bind(i),P=l||!e||e.indexOf(v)>=0,N=!n||n.indexOf(m)>=0,L=g.classed(tn.SELECTED);g.classed(ur.line)||g.classed(ti.area)||(t?P&&N&&c(f)&&!L?S(!0,g.classed(tn.SELECTED,!0),f,m):Qe(a)&&a&&L&&S(!1,g.classed(tn.SELECTED,!1),f,m):P&&N&&c(f)&&L&&S(!1,g.classed(tn.SELECTED,!1),f,m))})}var Dm={selected(t){const e=this.internal,n=[];return e.$el.main.selectAll(`.${sn.shapes+e.getTargetSelectorSuffix(t)}`).selectAll(`.${sn.shape}`).filter(function(){return ot(this).classed(tn.SELECTED)}).each(a=>n.push(a)),n},select(t,e,n){const a=this.internal;su.bind(a)(!0,t,e,n)},unselect(t,e){const n=this.internal;su.bind(n)(!1,t,e)}};const lu=function(t){var e;const n=this.internal,{axis:a,brush:i,config:o,scale:{x:s,subX:l},state:c}=n;let f;return o.subchart_show&&(f=t,Array.isArray(f)?(a.isTimeSeries()&&(f=f.map(v=>Yn.bind(n)(v))),n.withinRange(f,n.getZoomDomain("subX",!0),n.getZoomDomain("subX"))&&(c.domain=f,i.move(i.getSelection(),f.map(l)))):f=(e=c.domain)!=null?e:s.orgDomain()),f};yn(lu,{show(){var t,e;const n=this.internal,{$el:{subchart:a},config:i}=n,o=i.subchart_show;if(!o){n.unbindZoomEvent(),i.subchart_show=!o,!a.main&&n.initSubchart();let s=a.main.selectAll(`.${Se.target}`);n.data.targets.length!==s.size()&&(n.updateSizes(),n.updateTargetsForSubchart(n.data.targets),s=(t=a.main)==null?void 0:t.selectAll(`.${Se.target}`)),s==null||s.style("opacity",null),(e=a.main)==null||e.style("display",null),this.resize()}},hide(){const t=this.internal,{$el:{subchart:{main:e}},config:n}=t;n.subchart_show&&(e==null?void 0:e.style("display"))!=="none"&&(n.subchart_show=!1,e.style("display","none"),this.resize())},toggle(){const t=this.internal,{config:e}=t;this.subchart[e.subchart_show?"hide":"show"]()},reset(){const t=this.internal,{brush:e}=t;e.clear(e.getSelection())}});var Lm={subchart:lu},Nm=1e-12;function cu(t){return((t=Math.exp(t))+1/t)/2}function Fm(t){return((t=Math.exp(t))-1/t)/2}function Bm(t){return((t=Math.exp(2*t))-1)/(t+1)}var Um=function t(e,n,a){function i(o,s){var l=o[0],c=o[1],f=o[2],g=s[0],v=s[1],m=s[2],S=g-l,P=v-c,N=S*S+P*P,L,w;if(N()=>t;function zm(t,{sourceEvent:e,target:n,transform:a,dispatch:i}){Object.defineProperties(this,{type:{value:t,enumerable:!0,configurable:!0},sourceEvent:{value:e,enumerable:!0,configurable:!0},target:{value:n,enumerable:!0,configurable:!0},transform:{value:a,enumerable:!0,configurable:!0},_:{value:i}})}function vr(t,e,n){this.k=t,this.x=e,this.y=n}vr.prototype={constructor:vr,scale:function(t){return t===1?this:new vr(this.k*t,this.x,this.y)},translate:function(t,e){return t===0&e===0?this:new vr(this.k,this.x+this.k*t,this.y+this.k*e)},apply:function(t){return[t[0]*this.k+this.x,t[1]*this.k+this.y]},applyX:function(t){return t*this.k+this.x},applyY:function(t){return t*this.k+this.y},invert:function(t){return[(t[0]-this.x)/this.k,(t[1]-this.y)/this.k]},invertX:function(t){return(t-this.x)/this.k},invertY:function(t){return(t-this.y)/this.k},rescaleX:function(t){return t.copy().domain(t.range().map(this.invertX,this).map(t.invert,t))},rescaleY:function(t){return t.copy().domain(t.range().map(this.invertY,this).map(t.invert,t))},toString:function(){return"translate("+this.x+","+this.y+") scale("+this.k+")"}};var ar=new vr(1,0,0);vs.prototype=vr.prototype;function vs(t){for(;!t.__zoom;)if(!(t=t.parentNode))return ar;return t.__zoom}function ps(t){t.stopImmediatePropagation()}function Ba(t){t.preventDefault(),t.stopImmediatePropagation()}function jm(t){return(!t.ctrlKey||t.type==="wheel")&&!t.button}function Vm(){var t=this;return t instanceof SVGElement?(t=t.ownerSVGElement||t,t.hasAttribute("viewBox")?(t=t.viewBox.baseVal,[[t.x,t.y],[t.x+t.width,t.y+t.height]]):[[0,0],[t.width.baseVal.value,t.height.baseVal.value]]):[[0,0],[t.clientWidth,t.clientHeight]]}function uu(){return this.__zoom||ar}function Gm(t){return-t.deltaY*(t.deltaMode===1?.05:t.deltaMode?1:.002)*(t.ctrlKey?10:1)}function Xm(){return navigator.maxTouchPoints||"ontouchstart"in this}function Hm(t,e,n){var a=t.invertX(e[0][0])-n[0][0],i=t.invertX(e[1][0])-n[1][0],o=t.invertY(e[0][1])-n[0][1],s=t.invertY(e[1][1])-n[1][1];return t.translate(i>a?(a+i)/2:Math.min(0,a)||Math.max(0,i),s>o?(o+s)/2:Math.min(0,o)||Math.max(0,s))}function Ym(){var t=jm,e=Vm,n=Hm,a=Gm,i=Xm,o=[0,1/0],s=[[-1/0,-1/0],[1/0,1/0]],l=250,c=Um,f=ri("start","zoom","end"),g,v,m,S=500,P=150,N=0,L=10;function w(Q){Q.property("__zoom",uu).on("wheel.zoom",ht,{passive:!1}).on("mousedown.zoom",$t).on("dblclick.zoom",dt).filter(i).on("touchstart.zoom",st).on("touchmove.zoom",Vt).on("touchend.zoom touchcancel.zoom",vt).style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}w.transform=function(Q,St,ct,At){var Gt=Q.selection?Q.selection():Q;Gt.property("__zoom",uu),Q!==Gt?k(Q,St,ct,At):Gt.interrupt().each(function(){K(this,arguments).event(At).start().zoom(null,typeof St=="function"?St.apply(this,arguments):St).end()})},w.scaleBy=function(Q,St,ct,At){w.scaleTo(Q,function(){var Gt=this.__zoom.k,Bt=typeof St=="function"?St.apply(this,arguments):St;return Gt*Bt},ct,At)},w.scaleTo=function(Q,St,ct,At){w.transform(Q,function(){var Gt=e.apply(this,arguments),Bt=this.__zoom,Kt=ct==null?H(Gt):typeof ct=="function"?ct.apply(this,arguments):ct,ne=Bt.invert(Kt),le=typeof St=="function"?St.apply(this,arguments):St;return n(W(X(Bt,le),Kt,ne),Gt,s)},ct,At)},w.translateBy=function(Q,St,ct,At){w.transform(Q,function(){return n(this.__zoom.translate(typeof St=="function"?St.apply(this,arguments):St,typeof ct=="function"?ct.apply(this,arguments):ct),e.apply(this,arguments),s)},null,At)},w.translateTo=function(Q,St,ct,At,Gt){w.transform(Q,function(){var Bt=e.apply(this,arguments),Kt=this.__zoom,ne=At==null?H(Bt):typeof At=="function"?At.apply(this,arguments):At;return n(ar.translate(ne[0],ne[1]).scale(Kt.k).translate(typeof St=="function"?-St.apply(this,arguments):-St,typeof ct=="function"?-ct.apply(this,arguments):-ct),Bt,s)},At,Gt)};function X(Q,St){return St=Math.max(o[0],Math.min(o[1],St)),St===Q.k?Q:new vr(St,Q.x,Q.y)}function W(Q,St,ct){var At=St[0]-ct[0]*Q.k,Gt=St[1]-ct[1]*Q.k;return At===Q.x&&Gt===Q.y?Q:new vr(Q.k,At,Gt)}function H(Q){return[(+Q[0][0]+ +Q[1][0])/2,(+Q[0][1]+ +Q[1][1])/2]}function k(Q,St,ct,At){Q.on("start.zoom",function(){K(this,arguments).event(At).start()}).on("interrupt.zoom end.zoom",function(){K(this,arguments).event(At).end()}).tween("zoom",function(){var Gt=this,Bt=arguments,Kt=K(Gt,Bt).event(At),ne=e.apply(Gt,Bt),le=ct==null?H(ne):typeof ct=="function"?ct.apply(Gt,Bt):ct,be=Math.max(ne[1][0]-ne[0][0],ne[1][1]-ne[0][1]),Oe=Gt.__zoom,Ce=typeof St=="function"?St.apply(Gt,Bt):St,He=c(Oe.invert(le).concat(be/Oe.k),Ce.invert(le).concat(be/Ce.k));return function(Fe){if(Fe===1)Fe=Ce;else{var dn=He(Fe),Jt=be/dn[2];Fe=new vr(Jt,le[0]-dn[0]*Jt,le[1]-dn[1]*Jt)}Kt.zoom(null,Fe)}})}function K(Q,St,ct){return!ct&&Q.__zooming||new at(Q,St)}function at(Q,St){this.that=Q,this.args=St,this.active=0,this.sourceEvent=null,this.extent=e.apply(Q,St),this.taps=0}at.prototype={event:function(Q){return Q&&(this.sourceEvent=Q),this},start:function(){return++this.active===1&&(this.that.__zooming=this,this.emit("start")),this},zoom:function(Q,St){return this.mouse&&Q!=="mouse"&&(this.mouse[1]=St.invert(this.mouse[0])),this.touch0&&Q!=="touch"&&(this.touch0[1]=St.invert(this.touch0[0])),this.touch1&&Q!=="touch"&&(this.touch1[1]=St.invert(this.touch1[0])),this.that.__zoom=St,this.emit("zoom"),this},end:function(){return--this.active===0&&(delete this.that.__zooming,this.emit("end")),this},emit:function(Q){var St=ot(this.that).datum();f.call(Q,this.that,new zm(Q,{sourceEvent:this.sourceEvent,target:w,type:Q,transform:this.that.__zoom,dispatch:f}),St)}};function ht(Q,...St){if(!t.apply(this,arguments))return;var ct=K(this,St).event(Q),At=this.__zoom,Gt=Math.max(o[0],Math.min(o[1],At.k*Math.pow(2,a.apply(this,arguments)))),Bt=Xn(Q);if(ct.wheel)(ct.mouse[0][0]!==Bt[0]||ct.mouse[0][1]!==Bt[1])&&(ct.mouse[1]=At.invert(ct.mouse[0]=Bt)),clearTimeout(ct.wheel);else{if(At.k===Gt)return;ct.mouse=[Bt,At.invert(Bt)],qr(this),ct.start()}Ba(Q),ct.wheel=setTimeout(Kt,P),ct.zoom("mouse",n(W(X(At,Gt),ct.mouse[0],ct.mouse[1]),ct.extent,s));function Kt(){ct.wheel=null,ct.end()}}function $t(Q,...St){if(m||!t.apply(this,arguments))return;var ct=Q.currentTarget,At=K(this,St,!0).event(Q),Gt=ot(Q.view).on("mousemove.zoom",le,!0).on("mouseup.zoom",be,!0),Bt=Xn(Q,ct),Kt=Q.clientX,ne=Q.clientY;co(Q.view),ps(Q),At.mouse=[Bt,this.__zoom.invert(Bt)],qr(this),At.start();function le(Oe){if(Ba(Oe),!At.moved){var Ce=Oe.clientX-Kt,He=Oe.clientY-ne;At.moved=Ce*Ce+He*He>N}At.event(Oe).zoom("mouse",n(W(At.that.__zoom,At.mouse[0]=Xn(Oe,ct),At.mouse[1]),At.extent,s))}function be(Oe){Gt.on("mousemove.zoom mouseup.zoom",null),uo(Oe.view,At.moved),Ba(Oe),At.event(Oe).end()}}function dt(Q,...St){if(t.apply(this,arguments)){var ct=this.__zoom,At=Xn(Q.changedTouches?Q.changedTouches[0]:Q,this),Gt=ct.invert(At),Bt=ct.k*(Q.shiftKey?.5:2),Kt=n(W(X(ct,Bt),At,Gt),e.apply(this,St),s);Ba(Q),l>0?ot(this).transition().duration(l).call(k,Kt,At,Q):ot(this).call(w.transform,Kt,At,Q)}}function st(Q,...St){if(t.apply(this,arguments)){var ct=Q.touches,At=ct.length,Gt=K(this,St,Q.changedTouches.length===At).event(Q),Bt,Kt,ne,le;for(ps(Q),Kt=0;KtYn.bind(n)(v))),n.withinRange(f,n.getZoomDomain("zoom",!0),n.getZoomDomain("zoom"))){if(l.domain=f,f=n.getZoomDomainValue(f),n.api.tooltip.hide(),i.subchart_show){const v=s.zoom||s.x;n.brush.getSelection().call(n.brush.move,f.map(v))}else{const v=c?s.x.orgScale():o.xScale||s.x;n.updateCurrentZoomTransform(v,f)}n.setZoomResetButton()}}else f=n.zoom.getDomain();return(e=l.domain)!=null?e:f};yn(fu,{enable(t){const e=this.internal,{config:n}=e;/^(drag|wheel)$/.test(t)&&(n.zoom_type=t),n.zoom_enabled=!!t,e.zoom?t===!1&&e.bindZoomEvent(!1):(e.initZoom(),e.bindZoomEvent()),e.updateAndRedraw()},max(t){const e=this.internal,{config:n,org:{xDomain:a}}=e;return(t===0||t)&&(n.zoom_x_max=_n("max",[a[1],t])),n.zoom_x_max},min(t){const e=this.internal,{config:n,org:{xDomain:a}}=e;return(t===0||t)&&(n.zoom_x_min=_n("min",[a[0],t])),n.zoom_x_min},range(t){const e=this.zoom;if(Be(t)){const{min:n,max:a}=t;Qe(n)&&e.min(n),Qe(a)&&e.max(a)}return{min:e.min(),max:e.max()}}});var Wm={zoom:fu,unzoom(){const t=this.internal,{config:e,$el:{eventRect:n,zoomResetBtn:a},scale:{zoom:i},state:o}=t;i&&(e.subchart_show?t.brush.getSelection().call(t.brush.move,null):t.zoom.updateTransformScale(ar),t.updateZoom(!0),a==null||a.style("display","none"),vs(n.node())!==ar&&t.zoom.transform(n,ar),o.domain=void 0)}},Km={initBrush(){const t=this,{config:e,scale:n,$el:{subchart:a},state:i}=t,o=e.axis_rotated,s=e.subchart_size_height;let l,c,f;t.brush=(o?Gg():Vg()).handleSize(5),t.brush.on("start brush end",g=>{const{selection:v,sourceEvent:m,target:S,type:P}=g;P==="start"&&(t.state.inputType==="touch"&&t.hideTooltip(),c=m?v:null),/(start|brush)/.test(P)&&(P==="brush"&&m&&i.domain&&(c==null||c.forEach((N,L)=>{N!==v[L]&&(i.domain[L]=n.x.orgDomain()[L])})),t.redrawForBrush(P!=="start")),P==="end"&&(l=n.x.orgDomain()),S!=null&&S.handle&&(v===null?t.brush.handle.attr("display","none"):t.brush.handle.attr("display",null).attr("transform",(N,L)=>{const w=[v[L],s/2];return`translate(${o?w.reverse():w})`}))}),t.brush.updateResize=function(){f&&clearTimeout(f),f=setTimeout(()=>{const g=this.getSelection();l&&zl(g.node())&&this.move(g,l.map(n.subX.orgScale()))},0)},t.brush.update=function(){var g;return this.extent()()[1].filter(m=>isNaN(m)).length===0&&((g=a.main)==null||g.select(`.${Ue.brush}`).call(this)),this},t.brush.scale=function(g){const v=e.subchart_size_height;let m=t.axis.getExtent();!m&&g.range?m=[[0,0],[g.range()[1],v]]:je(m)&&(m=m.map((S,P)=>[S,P>0?v:P])),o&&m[1].reverse(),this.extent(m),this.update()},t.brush.getSelection=()=>a.main?a.main.select(`.${Ue.brush}`):ot([])},initSubchart(){const t=this,{config:e,state:{clip:n,hasAxis:a},$el:{defs:i,svg:o,subchart:s,axis:l}}=t;if(!a)return;const c=e.subchart_show?null:"hidden",f=`${n.id}-subchart`,g=t.getClipPath(f);n.idSubchart=f,t.appendClip(i,f),t.initBrush(),s.main=o.append("g").classed(Ue.subchart,!0).attr("transform",t.getTranslate("context"));const{main:v}=s;v.style("visibility",c),v.append("g").attr("clip-path",g).attr("class",Ue.chart),["bar","line","bubble","candlestick","scatter"].forEach(S=>{const P=Cn(/^(bubble|scatter)$/.test(S)?"circle":S);if(t.hasType(S)||t.hasTypeOf(P)){const N=v.select(`.${Ue.chart}`),L=Ue[`chart${P}s`];N.select(`.${L}`).empty()&&N.append("g").attr("class",L)}});const m=v.append("g").attr("clip-path",g).attr("class",Ue.brush).call(t.brush);e.subchart_showHandle&&t.addBrushHandle(m),l.subX=v.append("g").attr("class",Ue.axisX).attr("transform",t.getTranslate("subX")).attr("clip-path",e.axis_rotated?"":n.pathXAxis).style("visibility",e.subchart_axis_x_show?c:"hidden")},addBrushHandle(t){const e=this,{config:n}=e,a=n.axis_rotated,i=n.subchart_init_range,o="handle--custom",s=a?["M8.5 0 a6 6 0 0 0 -6 -6.5 H-2.5 a 6 6 0 0 0 -6 6.5 z m-5 -2 H-3.5 m7 -2 H-3.5z","M8.5 0 a6 -6 0 0 1 -6 6.5 H-2.5 a 6 -6 0 0 1 -6 -6.5z m-5 2 H-3.5 m7 2 H-3.5z"]:["M0 -8.5 A6 6 0 0 0 -6.5 -3.5 V2.5 A6 6 0 0 0 0 8.5 Z M-2 -3.5 V3.5 M-4 -3.5 V3.5z","M0 -8.5 A6 6 0 0 1 6.5 -3.5 V2.5 A6 6 0 0 1 0 8.5 Z M2 -3.5 V3.5 M4 -3.5 V3.5z"];e.brush.handle=t.selectAll(`.${o}`).data(a?[{type:"n"},{type:"s"}]:[{type:"w"},{type:"e"}]).enter().append("path").attr("class",o).attr("cursor",`${a?"ns":"ew"}-resize`).attr("d",l=>s[+/[se]/.test(l.type)]).attr("display",i?null:"none")},updateTargetsForSubchart(t){const e=this,{config:n,state:a,$el:{subchart:{main:i}}}=e;n.subchart_show&&(["bar","line","bubble","candlestick","scatter"].filter(o=>e.hasType(o)||e.hasTypeOf(Cn(o))).forEach(o=>{const s=/^(bubble|scatter)$/.test(o),l=Cn(s?"circle":o),c=e.getChartClass(l,!0),f=e.getClass(s?"circles":`${o}s`,!0),g=i.select(`.${Ue[`chart${`${l}s`}`]}`);if(s){const v=g.selectAll(`.${Ue.circles}`).data(t.filter(e[`is${Cn(o)}Type`].bind(e))).attr("class",f);v.exit().remove(),v.enter().append("g").attr("class",f)}else{const v=g.selectAll(`.${Ue[`chart${l}`]}`).attr("class",c).data(t.filter(e[`is${l}Type`].bind(e))),m=v.enter().append("g").style("opacity","0").attr("class",c).append("g").attr("class",f);v.exit().remove(),o==="line"&&e.hasTypeOf("Area")&&m.append("g").attr("class",e.getClass("areas",!0))}}),i.selectAll(`.${Ue.brush} rect`).attr(n.axis_rotated?"width":"height",n.axis_rotated?a.width2:a.height2))},redrawSubchart(t,e,n){var a;const i=this,{config:o,$el:{subchart:{main:s}},state:l}=i,c=!!e;if(s.style("visibility",o.subchart_show?null:"hidden"),o.subchart_show&&(((a=l.event)==null?void 0:a.type)==="zoom"&&i.brush.update(),t)){const f=o.subchart_init_range;if(!Kl(i)&&i.brush.update(),Object.keys(n.type).forEach(g=>{const v=Cn(g),m=i[`generateDraw${v}`](n.indices[g],!0);i[`update${v}`](c,!0),i[`redraw${v}`](m,c,!0)}),i.hasType("bubble")||i.hasType("scatter")){const{cx:g}=n.pos,v=i.updateCircleY(!0);i.updateCircle(!0),i.redrawCircle(g,v,c,void 0,!0)}!l.rendered&&f&&(l.domain=f,i.brush.move(i.brush.getSelection(),f.map(i.scale.x)))}},redrawForBrush(t=!0){var e;const n=this,{config:{subchart_onbrush:a,zoom_rescale:i},scale:o,state:s}=n;n.redraw({withTransition:!1,withY:i,withSubchart:!1,withUpdateXDomain:!0,withDimension:!1}),t&&s.rendered&&a.bind(n.api)((e=s.domain)!=null?e:o.x.orgDomain())},transformContext(t,e){const n=this,{$el:{subchart:a},$T:i}=n,o=e!=null&&e.axisSubX?e.axisSubX:i(a.main.select(`.${Ue.axisX}`),t);a.main.attr("transform",n.getTranslate("context")),o.attr("transform",n.getTranslate("subX"))}},Zm={initZoom(){const t=this;t.scale.zoom=null,t.generateZoom(),t.config.zoom_type==="drag"&&t.initZoomBehaviour()},bindZoomEvent(t=!0){const e=this,{config:n}=e;n.zoom_enabled&&t?!n.subchart_show&&e.bindZoomOnEventRect():t===!1&&(e.api.unzoom(),e.unbindZoomEvent())},generateZoom(){const t=this,{config:e,org:n,scale:a}=t,i=Ym().duration(0).on("start",t.onZoomStart.bind(t)).on("zoom",t.onZoom.bind(t)).on("end",t.onZoomEnd.bind(t));i.orgScaleExtent=()=>{const o=e.zoom_extent||[1,10];return[o[0],Math.max(t.getMaxDataCount()/o[1],o[1])]},i.updateScaleExtent=function(){const o=Dr(t.scale.x.orgDomain())/Dr(t.getZoomDomain()),s=this.orgScaleExtent();return this.scaleExtent([s[0]*o,s[1]*o]),this},i.updateTransformScale=(o,s)=>{var l;const c=e.axis_rotated;(l=n.xScale)==null||l.range(a.x.range());const f=o[c?"rescaleY":"rescaleX"](n.xScale||a.x);if(f.domain().some(m=>/(Invalid Date|NaN)/.test(m.toString())))return;const g=t.trimXDomain(f.domain()),v=e.zoom_rescale;if(f.domain(g,n.xDomain),s){const m=f(a.x.domain()[0]),S=c?o.x:m,P=c?m:o.y;t.$el.eventRect.property("__zoom",ar.translate(S,P).scale(o.k))}t.state.xTickOffset||(t.state.xTickOffset=t.axis.x.tickOffset()),a.zoom=t.getCustomizedXScale(f),t.axis.x.scale(a.zoom),v?(!n.xScale&&(n.xScale=a.x.copy()),a.x.domain(g)):n.xScale&&(a.x.domain(n.xScale.domain()),n.xScale=null)},i.getDomain=()=>{const o=a[a.zoom?"zoom":"subX"].domain();return t.axis.isCategorized()&&(o[1]-=2),o},t.zoom=i},onZoomStart(t){const e=this,{sourceEvent:n}=t;n&&(e.zoom.startEvent=n,e.state.zooming=!0,_e(e.config.zoom_onzoomstart,e.api,t))},onZoom(t){var e;const n=this,{config:a,scale:i,state:o,org:s}=n,{sourceEvent:l}=t,c=(t==null?void 0:t.transform)===ar;if(!a.zoom_enabled||n.filterTargetsToShow(n.data.targets).length===0||!i.zoom&&(l==null?void 0:l.type.indexOf("touch"))>-1&&(l==null?void 0:l.touches.length)===1)return;t.sourceEvent&&(o.zooming=!0,o.domain=void 0);const f=(l==null?void 0:l.type)==="mousemove",g=(l==null?void 0:l.wheelDelta)<0,{transform:v}=t;!f&&g&&i.x.domain().every((S,P)=>S!==s.xDomain[P])&&i.x.domain(s.xDomain),n.zoom.updateTransformScale(v,a.zoom_type==="wheel"&&l);const m=a.transition_duration>0&&!a.subchart_show&&(o.dragging||c||!t.sourceEvent);n.redraw({withTransition:m,withY:a.zoom_rescale,withSubchart:!1,withEventRect:!1,withDimension:!1}),n.state.cancelClick=f,!c&&_e(a.zoom_onzoom,n.api,(e=n.state.domain)!=null?e:n.zoom.getDomain())},onZoomEnd(t){var e,n;const a=this,{config:i,state:o}=a;let{startEvent:s}=a.zoom,l=t==null?void 0:t.sourceEvent;const c=(t==null?void 0:t.transform)===ar;(s==null?void 0:s.type.indexOf("touch"))>-1&&(s=s.changedTouches[0],l=(e=l==null?void 0:l.changedTouches)==null?void 0:e[0]),!(i.zoom_type==="drag"&&l&&s.clientX===l.clientX&&s.clientY===l.clientY)&&(o.zooming=!1,a.redrawEventRect(),a.updateZoom(),!c&&(l||o.dragging)&&_e(i.zoom_onzoomend,a.api,(n=a.state.domain)!=null?n:a.zoom.getDomain()))},updateZoom(t){const e=this,{subX:n,x:a,zoom:i}=e.scale;if(i){const o=i.domain(),s=n.domain(),l=.015,c=e.config.axis_x_inverted?(o[0]>=s[0]||o[0]+l>=s[0])&&(s[1]>=o[1]||s[1]>=o[1]+l):(o[0]<=s[0]||o[0]-l<=s[0])&&(s[1]<=o[1]||s[1]<=o[1]-l);(t||c)&&(e.axis.x.scale(n),a.domain(n.orgDomain()),e.scale.zoom=null)}},updateCurrentZoomTransform(t,e){const n=this,{$el:{eventRect:a},config:i}=n,o=i.axis_rotated,s=[-t(e[0]),0],l=ar.scale(t.range()[1]/(t(e[1])-t(e[0]))).translate(...o?s.reverse():s);a.call(n.zoom.transform,l)},bindZoomOnEventRect(){var t;const e=this,{config:n,$el:{eventRect:a,svg:i}}=e,o=n.zoom_type==="drag"?e.zoomBehaviour:e.zoom;Ke.GestureEvent&&/^((?!chrome|android|mobile).)*safari/i.test((t=Ke.navigator)==null?void 0:t.userAgent)&&i.on("wheel",()=>{}),a==null||a.call(o).on("dblclick.zoom",null)},initZoomBehaviour(){const t=this,{config:e,state:n}=t,a=e.axis_rotated;let i=0,o=0,s,l;const c={axis:a?"y":"x",attr:a?"height":"width",index:a?1:0};t.zoomBehaviour=uc().clickDistance(4).on("start",function(f){l=t.scale.zoom?null:t.axis.getExtent(),n.event=f,t.setDragStatus(!0),t.unselectRect(),s||(s=t.$el.main.append("rect").attr("clip-path",n.clip.path).attr("class",so.zoomBrush).attr("width",a?n.width:0).attr("height",a?0:n.height)),i=Hn(f,this)[c.index],l&&(il[1]&&(i=l[1])),o=i,s.attr(c.axis,i).attr(c.attr,0),t.onZoomStart(f)}).on("drag",function(f){o=Hn(f,this)[c.index],l&&(o>l[1]?o=l[1]:o{const g=t.scale.zoom||t.scale.x;n.event=f,s.attr(c.axis,0).attr(c.attr,0),i>o&&([i,o]=[o,i]),i<0&&(o+=Math.abs(i),i=0),i!==o&&t.api.zoom([i,o].map(v=>g.invert(v))),t.setDragStatus(!1)})},setZoomResetButton(){const t=this,{config:e,$el:n}=t,a=e.zoom_resetButton;a&&e.zoom_type==="drag"&&(n.zoomResetBtn?n.zoomResetBtn.style("display",null):n.zoomResetBtn=t.$el.chart.append("div").classed(Se.button,!0).append("span").on("click",function(){ve(a.onclick)&&a.onclick.bind(t.api)(this),t.api.unzoom()}).classed(so.buttonZoomReset,!0).text(a.text||"Reset Zoom"))},getZoomTransform(){const t=this,{$el:{eventRect:e}}=t;return e!=null&&e.node()?vs(e.node()):{k:1}}},Jm={drag(t){const e=this,{config:n,state:a,$el:{main:i}}=e,o=n.data_selection_grouped,s=n.interaction_enabled&&n.data_selection_isselectable;if(e.hasArcType()||!n.data_selection_enabled||n.zoom_enabled&&!e.zoom.altDomain||!n.data_selection_multiple)return;const[l,c]=a.dragStart||[0,0],[f,g]=t,v=Math.min(l,f),m=Math.max(l,f),S=o?a.margin.top:Math.min(c,g),P=o?a.height:Math.max(c,g);i.select(`.${Or.dragarea}`).attr("x",v).attr("y",S).attr("width",m-v).attr("height",P-S),i.selectAll(`.${sn.shapes}`).selectAll(`.${sn.shape}`).filter(N=>s==null?void 0:s.bind(e.api)(N)).each(function(N,L){const w=ot(this),X=w.classed(tn.SELECTED),W=w.classed(Or.INCLUDED);let H=!1,k;if(w.classed($n.circle)){const K=+w.attr("cx")*1,at=+w.attr("cy")*1;k=e.togglePoint,H=ve in t?Qm(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,ey=(t,e)=>{for(var n in e||(e={}))_m.call(e,n)&&hu(t,n,e[n]);if(du)for(var n of du(e))ty.call(e,n)&&hu(t,n,e[n]);return t},ny=(t,e)=>km(t,qm(e)),ry=ny(ey({},Jm),{selectPoint(t,e,n){const a=this,{config:i,$el:{main:o},$T:s}=a,l=i.axis_rotated,c=(l?a.circleY:a.circleX).bind(a),f=(l?a.circleX:a.circleY).bind(a),g=a.pointSelectR.bind(a);_e(i.data_onselected,a.api,e,t.node()),s(o.select(`.${tn.selectedCircles}${a.getTargetSelectorSuffix(e.id)}`).selectAll(`.${tn.selectedCircle}-${n}`).data([e]).enter().append("circle").attr("class",()=>a.generateClass(tn.selectedCircle,n)).attr("cx",c).attr("cy",f).attr("stroke",a.color).attr("r",v=>a.pointSelectR(v)*1.4)).attr("r",g)},unselectPoint(t,e,n){const a=this,{config:i,$el:{main:o},$T:s}=a;_e(i.data_onunselected,a.api,e,t==null?void 0:t.node()),s(o.select(`.${tn.selectedCircles}${a.getTargetSelectorSuffix(e.id)}`).selectAll(`.${tn.selectedCircle}-${n}`)).attr("r",0).remove()},togglePoint(t,e,n,a){this[`${t?"":"un"}selectPoint`](e,n,a)},selectPath(t,e){const n=this,{config:a}=n;_e(a.data_onselected,n.api,e,t.node()),a.interaction_brighten&&t.style("filter","brightness(1.25)")},unselectPath(t,e){const n=this,{config:a}=n;_e(a.data_onunselected,n.api,e,t.node()),a.interaction_brighten&&t.style("filter",null)},togglePath(t,e,n,a){this[`${t?"":"un"}selectPath`](e,n,a)},getToggle(t,e){const n=this;return t.nodeName==="path"?n.togglePath:n.isStepType(e)?()=>{}:n.togglePoint},toggleShape(t,e,n){var a;const i=this,{config:o,$el:{main:s}}=i;if(o.data_selection_enabled&&o.data_selection_isselectable.bind(i.api)(e)){const l=ot(t),c=l.classed(tn.SELECTED),f=i.getToggle(t,e).bind(i);let g;if(!o.data_selection_multiple){const v=(a=i.isPointFocusOnly)==null?void 0:a.call(i);let m=`.${v?tn.selectedCircles:sn.shapes}`;o.data_selection_grouped&&(m+=i.getTargetSelectorSuffix(e.id)),s.selectAll(m).selectAll(v?`.${tn.selectedCircle}`:`.${sn.shape}.${tn.SELECTED}`).classed(tn.SELECTED,!1).each(function(S){const P=ot(this);g=P,f(!1,P,S,S.index)})}(!g||g.node()!==l.node())&&(l.classed(tn.SELECTED,!c),f(!c,l,e,n))}}}),ay={data_selection_enabled:!1,data_selection_grouped:!1,data_selection_isselectable:()=>!0,data_selection_multiple:!0,data_selection_draggable:!1,data_onselected:()=>{},data_onunselected:()=>{}},iy={subchart_show:!1,subchart_showHandle:!1,subchart_size_height:60,subchart_axis_x_show:!0,subchart_axis_x_tick_show:!0,subchart_axis_x_tick_format:void 0,subchart_axis_x_tick_text_show:!0,subchart_init_range:void 0,subchart_onbrush:()=>{}},oy={zoom_enabled:!1,zoom_type:"wheel",zoom_extent:void 0,zoom_privileged:!1,zoom_rescale:!1,zoom_onzoom:void 0,zoom_onzoomstart:void 0,zoom_onzoomend:void 0,zoom_resetButton:!0,zoom_x_min:void 0,zoom_x_max:void 0};let gu=()=>(yn(Vr.prototype,ry),yn(Er.prototype,Dm),Nr.setOptions([ay]),(gu=()=>!0)()),vu=()=>(yn(Vr.prototype,Km),yn(Er.prototype,Lm),Nr.setOptions([iy]),(vu=()=>!0)()),pu=()=>(yn(Vr.prototype,Zm),yn(Er.prototype,Wm),Nr.setOptions([oy]),(pu=()=>!0)());function mu(t,e,n){const{config:a}=t,i=(o,s)=>{const l=he(s)?s:s===!1?void 0:null;l!==null&&(a[`axis_${o}_${e}`]=l)};Qe(n)&&(nr(n)?Object.keys(n).forEach(o=>{i(o,n[o])}):(he(n)||n===!1)&&["y","y2"].forEach(o=>{i(o,n)}),t.redraw({withUpdateOrgXDomain:!0,withUpdateXDomain:!0}))}function yu(t,e){const{config:n}=t;return{x:n[`axis_x_${e}`],y:n[`axis_y_${e}`],y2:n[`axis_y2_${e}`]}}var sy={axis:{labels:function(t){const e=this.internal;let n;return t&&(Object.keys(t).forEach(a=>{e.axis.setLabelText(a,t[a])}),e.axis.updateLabels()),["x","y","y2"].forEach(a=>{const i=e.axis.getLabelText(a);i&&(!n&&(n={}),n[a]=i)}),n},min:function(t){const e=this.internal;return De(t)||t===!1?mu(e,"min",t):yu(e,"min")},max:function(t){const e=this.internal;return De(t)||t===!1?mu(e,"max",t):yu(e,"max")},range:function(t){const{axis:e}=this;if(arguments.length){const{min:n,max:a}=t;Qe(a)&&e.max(a),Qe(n)&&e.min(n)}else return{max:e.max(),min:e.min()}}}},ly={category(t,e){const n=this.internal,{config:a}=n;return arguments.length>1&&(a.axis_x_categories[t]=e,n.redraw()),a.axis_x_categories[t]},categories(t){const e=this.internal,{config:n}=e;if(!t||!Array.isArray(t)){const a=n.axis_x_categories;return qn(a)?Object.values(e.data.xs)[0]:a}return n.axis_x_categories=t,e.redraw(),n.axis_x_categories}},cy={flow(t){const e=this.internal;let n;(t.json||t.rows||t.columns)&&e.convertData(t,i=>{n=i,a()});function a(){let i,o=0,s=0,l,c;if(e.state.redrawing||!n||!Da())return;const f=[],g=e.getMaxDataCount(),v=e.convertDataToTargets(n,!0),m=e.axis.isTimeSeries();e.data.targets.forEach(N=>{let L=!1;for(let w=0;w{for(let L=0;L{const L=[];for(let w=e.data.targets[0].values[0].index;w{w.index+=s,m||(w.x+=s)}),N.values=L.concat(N.values)}),e.data.targets=e.data.targets.concat(v);const S=e.data.targets[0],P=S.values[0];Qe(t.to)?(o=0,c=m?Yn.call(e,t.to):t.to,S.values.forEach(N=>{N.x1?S.values[S.values.length-1].x-P.x:P.x-e.getXDomain(e.data.targets)[0]:l=1,i=[P.x-l,P.x]),i&&e.updateXDomain(null,!0,!0,!1,i),e.updateTargets(e.data.targets),e.redraw({flow:{index:P.index,length:o,duration:De(t.duration)?t.duration:e.config.transition_duration,done:t.done,orgDataCount:g},withLegend:!0,withTransition:g>1,withTrimXDomain:!1,withUpdateXAxis:!0})}}};function ms(t,e){const n=this.internal,{config:a}=n,i=a.transition_duration&&Da(),o=`grid_${e}_lines`;return t&&(a[o]=t,n.updateGrid(),n.redrawGrid(i)),a[o]}function xu(t,e){const n=`grid_${e}_lines`;return ms.bind(this)(this.internal.config[n].concat(t||[]),e)}function Tu(t,e){this.internal.removeGridLines(t,e)}const $u=function(t){return ms.bind(this)(t,"x")};yn($u,{add(t){return xu.bind(this)(t,"x")},remove(t){return Tu.bind(this)(t,!0)}});const Su=function(t){return ms.bind(this)(t,"y")};yn(Su,{add(t){return xu.bind(this)(t,"y")},remove(t){return Tu.bind(this)(t,!1)}});var uy={xgrids:$u,ygrids:Su},fy={groups(t){const e=this.internal,{config:n}=e;return ln(t)||(n.data_groups=t,e.redraw()),n.data_groups}};function Au(t,e=!1){const n=this.internal,{config:a}=n,i=a.transition_duration&&Da();return t?(a.regions=e?a.regions.concat(t):t,n.updateRegion(),n.redrawRegion(i),e?a.regions:t):a.regions}const Eu=function(t){return Au.bind(this)(t)};yn(Eu,{add:function(t){return Au.bind(this)(t,!0)},remove:function(t){const e=this.internal,{config:n,$T:a}=e,i=t||{},o=$r(i,"classes",[$a.region]);let s=e.$el.main.select(`.${$a.regions}`).selectAll(o.map(l=>`.${l}`));return a(s).style("opacity","0").remove(),s=n.regions,Object.keys(i).length?(s=s.filter(l=>{let c=!1;return l.class?(l.class.split(" ").forEach(f=>{o.indexOf(f)>=0&&(c=!0)}),!c):!0}),n.regions=s):n.regions=[],s}});var dy={regions:Eu},hy={x(t){const e=this.internal,{axis:n,data:a}=e,i=n.isCustomX()&&n.isCategorized();return je(t)&&(i?this.categories(t):(e.updateTargetX(a.targets,t),e.redraw({withUpdateOrgXDomain:!0,withUpdateXDomain:!0}))),i?this.categories():a.xs},xs(t){const e=this.internal;return Be(t)&&(e.updateTargetXs(e.data.targets,t),e.redraw({withUpdateOrgXDomain:!0,withUpdateXDomain:!0})),e.data.xs}};function gy(t){return t}var Xi=1,Hi=2,ys=3,Ua=4,bu=1e-6;function vy(t){return"translate("+t+",0)"}function py(t){return"translate(0,"+t+")"}function my(t){return e=>+t(e)}function yy(t,e){return e=Math.max(0,t.bandwidth()-e*2)/2,t.round()&&(e=Math.round(e)),n=>+t(n)+e}function xy(){return!this.__axis}function Yi(t,e){var n=[],a=null,i=null,o=6,s=6,l=3,c=typeof window!="undefined"&&window.devicePixelRatio>1?0:.5,f=t===Xi||t===Ua?-1:1,g=t===Ua||t===Hi?"x":"y",v=t===Xi||t===ys?vy:py;function m(S){var P=a==null?e.ticks?e.ticks.apply(e,n):e.domain():a,N=i==null?e.tickFormat?e.tickFormat.apply(e,n):gy:i,L=Math.max(o,0)+l,w=e.range(),X=+w[0]+c,W=+w[w.length-1]+c,H=(e.bandwidth?yy:my)(e.copy(),c),k=S.selection?S.selection():S,K=k.selectAll(".domain").data([null]),at=k.selectAll(".tick").data(P,e).order(),ht=at.exit(),$t=at.enter().append("g").attr("class","tick"),dt=at.select("line"),st=at.select("text");K=K.merge(K.enter().insert("path",".tick").attr("class","domain").attr("stroke","currentColor")),at=at.merge($t),dt=dt.merge($t.append("line").attr("stroke","currentColor").attr(g+"2",f*o)),st=st.merge($t.append("text").attr("fill","currentColor").attr(g,f*L).attr("dy",t===Xi?"0em":t===ys?"0.71em":"0.32em")),S!==k&&(K=K.transition(S),at=at.transition(S),dt=dt.transition(S),st=st.transition(S),ht=ht.transition(S).attr("opacity",bu).attr("transform",function(Vt){return isFinite(Vt=H(Vt))?v(Vt+c):this.getAttribute("transform")}),$t.attr("opacity",bu).attr("transform",function(Vt){var vt=this.parentNode.__axis;return v((vt&&isFinite(vt=vt(Vt))?vt:H(Vt))+c)})),ht.remove(),K.attr("d",t===Ua||t===Hi?s?"M"+f*s+","+X+"H"+c+"V"+W+"H"+f*s:"M"+c+","+X+"V"+W:s?"M"+X+","+f*s+"V"+c+"H"+W+"V"+f*s:"M"+X+","+c+"H"+W),at.attr("opacity",1).attr("transform",function(Vt){return v(H(Vt)+c)}),dt.attr(g+"2",f*o),st.attr(g,f*L).text(N),k.filter(xy).attr("fill","none").attr("font-size",10).attr("font-family","sans-serif").attr("text-anchor",t===Hi?"start":t===Ua?"end":"middle"),k.each(function(){this.__axis=H})}return m.scale=function(S){return arguments.length?(e=S,m):e},m.ticks=function(){return n=Array.from(arguments),m},m.tickArguments=function(S){return arguments.length?(n=S==null?[]:Array.from(S),m):n.slice()},m.tickValues=function(S){return arguments.length?(a=S==null?null:Array.from(S),m):a&&a.slice()},m.tickFormat=function(S){return arguments.length?(i=S,m):i},m.tickSize=function(S){return arguments.length?(o=s=+S,m):o},m.tickSizeInner=function(S){return arguments.length?(o=+S,m):o},m.tickSizeOuter=function(S){return arguments.length?(s=+S,m):s},m.tickPadding=function(S){return arguments.length?(l=+S,m):l},m.offset=function(S){return arguments.length?(c=+S,m):c},m}function Ty(t){return Yi(Xi,t)}function $y(t){return Yi(Hi,t)}function Ru(t){return Yi(ys,t)}function Iu(t){return Yi(Ua,t)}var Sy=Object.defineProperty,Ay=(t,e,n)=>e in t?Sy(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,xs=(t,e,n)=>Ay(t,typeof e!="symbol"?e+"":e,n);class Ou{constructor(e){xs(this,"owner"),xs(this,"config"),xs(this,"scale");const n=zr(),{config:a,params:i}=e;this.owner=e,this.config=a,this.scale=n,(a.noTransition||!i.config.transition_duration)&&(a.withoutTransition=!0),a.range=this.scaleExtent((i.orgXScale||n).range())}static getSizeFor1Char(e,n=!0){const a={w:5.5,h:11.5};return!e.empty()&&e.text("0").call(i=>{try{const{width:o,height:s}=i.node().getBBox();o&&s&&(a.w=o,a.h=s)}finally{i.text("")}}),n&&(this.getSizeFor1Char=()=>a),a}getTickTransformSetter(e){const{config:n}=this,a=e==="x"?i=>`translate(${i+n.tickOffset},0)`:i=>`translate(0,${i})`;return(i,o)=>{i.attr("transform",s=>{const l=o(s);return De(s)?a(l):null})}}scaleExtent(e){const n=e[0],a=e[e.length-1];return n0?i:1,o]).range(e.range());s=c.ticks();for(let f=o.toFixed().length;s.length>15;f--)s=c.ticks(f);s.splice(0,1,i),s.splice(s.length-1,1,o)}else s=e.ticks(...this.config.tickArguments||[]);s=s.map(c=>ze(c)&&he(c)&&!isNaN(c)&&Math.round(c*10)/10||c)}return s}copyScale(){const e=this.scale.copy();return e.domain().length||e.domain(this.scale.domain()),e.type=this.scale.type,e}textFormatted(e){const n=this.config.tickFormat,a=/\d+\.\d+0{5,}\d$/.test(e)?+String(e).replace(/0+\d$/,""):e,i=n?n(a):a;return Qe(i)?i:""}transitionise(e){const{config:n}=this;let a=e;if(n.withoutTransition)a=e.interrupt();else if(n.transition||!this.owner.params.noTransition)try{a=e.transition(n.transition)}catch(i){}return a}}var Ey=Object.defineProperty,by=(t,e,n)=>e in t?Ey(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,za=(t,e,n)=>by(t,typeof e!="symbol"?e+"":e,n);class Ry{constructor(e={}){za(this,"helper"),za(this,"config"),za(this,"params"),za(this,"g"),za(this,"generatedTicks");const n={innerTickSize:6,outerTickSize:e.outerTick?6:0,orient:"bottom",range:[],tickArguments:null,tickCentered:null,tickCulling:!0,tickFormat:null,tickLength:9,tickOffset:0,tickPadding:3,tickValues:null,transition:null,noTransition:e.noTransition};n.tickLength=Math.max(n.innerTickSize,0)+n.tickPadding,this.config=n,this.params=e,this.helper=new Ou(this)}create(e){const n=this,{config:a,helper:i,params:o}=n,{scale:s}=i,{orient:l}=a,c=this.splitTickText.bind(n),f=/^(left|right)$/.test(l),g=/^(top|bottom)$/.test(l),v=i.getTickTransformSetter(g?"x":"y"),m=v===i.axisX?"y":"x",S=/^(top|left)$/.test(l)?-1:1,P=o.tickTextRotate;this.config.range=s.rangeExtent?s.rangeExtent():i.scaleExtent((o.orgXScale||s).range());const{innerTickSize:N,tickLength:L,range:w}=a,X=o.id,W=X&&/^(x|y|y2)$/.test(X)?o.config[`axis_${X}_tick_text_position`]:{x:0,y:0},H=X==="subX"?"subchart_axis_x":`axis_${X}`,k=o.config[`${H}_show`],K={tick:k?o.config[`${H}_tick_show`]:!1,text:k?o.config[`${H}_tick_text_show`]:!1},at=o.config.axis_evalTextSize;let ht;e.each(function(){const $t=ot(this);let dt=this.__chart__||s,st=i.copyScale();ht=$t,this.__chart__=st,a.tickOffset=o.isCategory?(st(1)-st(0))/2:0;const Vt=$t.selectAll(".domain").data([0]);if(Vt.enter().append("path").attr("class","domain").merge(Vt).attr("d",()=>{const vt=a.outerTickSize*S;return g?`M${w[0]},${vt}V0H${w[1]}V${vt}`:`M${vt},${w[0]}H0V${w[1]}H${vt}`}),K.tick||K.text){const vt=a.tickValues||i.generateTicks(st,f);n.generatedTicks=vt;let Q=$t.selectAll(".tick").data(vt,st);const St=Q.enter().insert("g",".domain").attr("class","tick"),ct=Q.exit().remove();Q=St.merge(Q),K.tick&&St.append("line"),K.text&&St.append("text");const At=Q.select("text"),Gt=ve(at)?at.bind(n.params.owner.api)(At.node()):Ou.getSizeFor1Char(At,at),Bt=[];let Kt=At.selectAll("tspan").data((be,Oe)=>{const Ce=o.tickMultiline?c(be,st,vt,f,Gt.w):je(i.textFormatted(be))?i.textFormatted(be).concat():[i.textFormatted(be)];return Bt[Oe]=Ce.length,Ce.map(He=>({index:Oe,splitted:He}))});Kt.exit().remove(),Kt=Kt.enter().append("tspan").merge(Kt).text(be=>be.splitted),Kt.attr("x",g?0:L*S).attr("dx",(()=>{let be=0;return/(top|bottom)/.test(l)&&P&&(be=8*Math.sin(Math.PI*(P/180))*(l==="top"?-1:1)),be+(W.x||0)})()).attr("dy",(be,Oe)=>{const Ce=".71em";let He=0;return l!=="top"&&(He=Gt.h,Oe===0&&(He=f?-((Bt[be.index]-1)*(Gt.h/2)-3):W.y===0?Ce:0)),he(He)&&W.y?He+W.y:He||Ce});const ne=Q.select("line"),le=Q.select("text");if(St.select("line").attr(`${m}2`,N*S),St.select("text").attr(m,L*S),n.setTickLineTextPosition(ne,le),o.tickTitle){const be=le.select("title");(be.empty()?le.append("title"):be).text(Oe=>o.tickTitle[Oe])}if(st.bandwidth){const be=st,Oe=be.bandwidth()/2;dt=Ce=>be(Ce)+Oe,st=dt}else dt.bandwidth?dt=st:v(ct,st);Q=o.owner.state.flowing?i.transitionise(Q):o.owner.$T(Q),v(St,dt),v(Q.style("opacity",null),st)}}),this.g=ht}getGeneratedTicks(e){var n;const a=((n=this.generatedTicks)==null?void 0:n.length)-1;let i=this.generatedTicks;if(a>e){const o=Math.round(a/e+.1);i=this.generatedTicks.map((s,l)=>l%o===0?s:null).filter(s=>s!==null).splice(0,e)}return i}getTickXY(){const{config:e}=this,n={x:0,y:0};return this.params.isCategory&&(n.x=e.tickCentered?0:e.tickOffset,n.y=e.tickCentered?e.tickOffset:0),n}getTickSize(e){const{scale:n}=this.helper,{config:a}=this,{innerTickSize:i,range:o}=a,s=n(e)+(a.tickCentered?0:a.tickOffset);return o[0]{const N=["start","end"];return o==="top"&&N.reverse(),P?N[P>0?0:1]:"middle"},g=P=>P?`rotate(${P})`:null,v=P=>{const N=P/(o==="bottom"?15:23);return P?11.5-2.5*N*(P>0?1:-1):s},{config:{axis_rotated:m,axis_x_tick_text_inner:S}}=this.params.owner;switch(o){case"bottom":e.attr("x1",a.x).attr("x2",a.x).attr("y2",this.getTickSize.bind(this)),n.attr("x",0).attr("y",v(c)).style("text-anchor",f(c)).style("text-anchor",(P,N,{length:L})=>!m&&N===0&&(S===!0||S.first)?"start":!m&&N===L-1&&(S===!0||S.last)?"end":f(c)).attr("transform",g(c));break;case"top":e.attr("x2",0).attr("y2",-i),n.attr("x",0).attr("y",-v(c)*2).style("text-anchor",f(c)).attr("transform",g(c));break;case"left":e.attr("x2",-i).attr("y1",a.y).attr("y2",a.y),n.attr("x",-s).attr("y",l).style("text-anchor","end");break;case"right":e.attr("x2",i).attr("y2",0),n.attr("x",s).attr("y",0).style("text-anchor","start")}}splitTickText(e,n,a,i,o){const{params:s}=this,l=this.helper.textFormatted(e),c=ze(l)&&l.indexOf(` +`)>-1?l.split(` +`):[];if(c.length)return c;if(je(l))return l;let f=s.tickWidth;(!f||f<=0)&&(f=i?95:s.isCategory?(s.isInverted?n(a[0])-n(a[1]):n(a[1])-n(a[0]))-12:110);function g(v,m){let S,P,N;for(let L=1;L{const S=v+1;return Se(this.helper.scale.domain());else{if(!arguments.length)return n.tickValues;n.tickValues=e}return this}setTransition(e){return this.config.transition=e,this}}var Iy=Object.defineProperty,Oy=(t,e,n)=>e in t?Iy(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,pr=(t,e,n)=>Oy(t,typeof e!="symbol"?e+"":e,n),Cy={getAxisInstance:function(){return this.axis||new Py(this)}};class Py{constructor(e){pr(this,"owner"),pr(this,"x"),pr(this,"subX"),pr(this,"y"),pr(this,"y2"),pr(this,"axesList",{}),pr(this,"tick",{x:null,y:null,y2:null}),pr(this,"xs",[]),pr(this,"orient",{x:"bottom",y:"left",y2:"right",subX:"bottom"}),this.owner=e,this.setOrient()}getAxisClassName(e){return`${Tn.axis} ${Tn[`axis${Cn(e)}`]}`}isHorizontal(e,n){const a=e.config.axis_rotated;return n?a:!a}isCategorized(){const{config:e,state:n}=this.owner;return e.axis_x_type.indexOf("category")>=0||n.hasRadar}isCustomX(){const{config:e}=this.owner;return!this.isTimeSeries()&&(e.data_x||cn(e.data_xs))}isTimeSeries(e="x"){return this.owner.config[`axis_${e}_type`]==="timeseries"}isLog(e="x"){return this.owner.config[`axis_${e}_type`]==="log"}isTimeSeriesY(){return this.isTimeSeries("y")}getAxisType(e="x"){let n="linear";return this.isTimeSeries(e)?n=this.owner.config.axis_x_localtime?"time":"utc":this.isLog(e)&&(n="log"),n}getExtent(){const e=this.owner,{config:n,scale:a}=e;let i=n.axis_x_extent;if(i){if(ve(i))i=i.bind(e.api)(e.getXDomain(e.data.targets),a.subX);else if(this.isTimeSeries()&&i.every(isNaN)){const o=Yn.bind(e);i=i.map(s=>a.subX(o(s)))}}return i}init(){const e=this.owner,{config:n,$el:{main:a,axis:i},state:{clip:o}}=e,s=["x","y"];n.axis_y2_show&&s.push("y2"),s.forEach(l=>{const c=this.getAxisClassName(l);i[l]=a.append("g").attr("class",c).attr("clip-path",()=>{let f=null;return l==="x"?f=o.pathXAxis:l==="y"&&(f=o.pathYAxis),f}).attr("transform",e.getTranslate(l)).style("visibility",n[`axis_${l}_show`]?null:"hidden"),this.generateAxes(l)})}setOrient(){const e=this.owner,{axis_rotated:n,axis_y_inner:a,axis_y2_inner:i}=e.config;this.orient={x:n?"left":"bottom",y:n?a?"top":"bottom":a?"right":"left",y2:n?i?"bottom":"top":i?"left":"right",subX:n?"left":"bottom"}}generateAxes(e){const n=this.owner,{config:a}=n,i=[],o=a[`axis_${e}_axes`],s=a.axis_rotated;let l;e==="x"?l=s?Iu:Ru:e==="y"?l=s?Ru:Iu:e==="y2"&&(l=s?Ty:$y),o.length&&o.forEach(c=>{const f=c.tick||{},g=n.scale[e].copy();c.domain&&g.domain(c.domain),i.push(l(g).ticks(f.count).tickFormat(ve(f.format)?f.format.bind(n.api):v=>v).tickValues(f.values).tickSizeOuter(f.outer===!1?0:6))}),this.axesList[e]=i}updateAxes(){const e=this.owner,{config:n,$el:{main:a},$T:i}=e;Object.keys(this.axesList).forEach(o=>{const s=n[`axis_${o}_axes`],l=e.scale[o].copy(),c=l.range();this.axesList[o].forEach((f,g)=>{const v=f.scale().range();c.every((P,N)=>P===v[N])||f.scale().range(c);const m=`${this.getAxisClassName(o)}-${g+1}`;let S=a.select(`.${m.replace(/\s/,".")}`);S.empty()?S=a.append("g").attr("class",m).style("visibility",n[`axis_${o}_show`]?null:"hidden").call(f):(s[g].domain&&l.domain(s[g].domain),i(S).call(f.scale(l))),S.attr("transform",e.getTranslate(o,g+1))})})}setAxis(e,n,a,i){const o=this.owner;e!=="subX"&&(this.tick[e]=this.getTickValues(e)),this[e]=this.getAxis(e,n,a,e==="x"&&(o.scale.zoom||o.config.subchart_show||o.state.resizing)?!0:i)}getAxis(e,n,a,i,o){const s=this.owner,{config:l}=s,c=/^(x|subX)$/.test(e),f=c?"x":e,g=c&&this.isCategorized(),v=this.orient[e],m=o?0:s.getAxisTickRotate(f);let S;if(c)S=e==="subX"?s.format.subXAxisTick:s.format.xAxisTick;else{const X=l[`axis_${e}_tick_format`];ve(X)&&(S=X.bind(s.api))}let P=this.tick[f];const N=ea({outerTick:a,noTransition:i,config:l,id:e,tickTextRotate:m,owner:s},c&&{isCategory:g,isInverted:l.axis_x_inverted,tickMultiline:l.axis_x_tick_multiline,tickWidth:l.axis_x_tick_width,tickTitle:g&&l.axis_x_tick_tooltip&&s.api.categories(),orgXScale:s.scale.x});c||(N.tickStepSize=l[`axis_${f}_tick_stepSize`]);const L=new Ry(N).scale(c&&s.scale.zoom||n).orient(v);if(c&&this.isTimeSeries()&&P&&!ve(P)){const X=Yn.bind(s);P=P.map(W=>X(W))}else!c&&this.isTimeSeriesY()&&(L.ticks(l.axis_y_tick_time_value),P=null);P&&L.tickValues(P),L.tickFormat(S||!c&&s.isStackNormalized()&&(X=>`${X}%`)),g&&(L.tickCentered(l.axis_x_tick_centered),qn(l.axis_x_tick_culling)&&(l.axis_x_tick_culling=!1));const w=l[`axis_${f}_tick_count`];return w&&L.ticks(w),L}updateXAxisTickValues(e,n){var a;const i=this.owner,{config:o}=i,s=o.axis_x_tick_fit;let l=o.axis_x_tick_count,c;return(s||l&&s)&&(c=i.mapTargetsToUniqueXs(e),this.isCategorized()&&l>c.length&&(l=c.length),c=this.generateTickValues(c,l,this.isTimeSeries())),n?n.tickValues(c):this.x&&(this.x.tickValues(c),(a=this.subX)==null||a.tickValues(c)),c}getId(e){const{config:n,scale:a}=this.owner;let i=n.data_axes[e];return(!i||!a[i])&&(i="y"),i}getXAxisTickFormat(e){const n=this.owner,{config:a,format:i}=n,o=e&&a.subchart_axis_x_tick_format||a.axis_x_tick_format,s=this.isTimeSeries(),l=this.isCategorized();let c;return o?ve(o)?c=o.bind(n.api):s&&(c=f=>f?i.axisTime(o)(f):""):c=s?i.defaultAxisTime:l?n.categoryName:f=>f<0?f.toFixed(0):f,ve(c)?f=>c.apply(n,l?[f,n.categoryName(f)]:[f]):c}getTickValues(e){const n=this.owner,a=n.config[`axis_${e}_tick_values`],i=n[`${e}Axis`];return(ve(a)?a.call(n.api):a)||(i?i.tickValues():void 0)}getLabelOptionByAxisId(e){return this.owner.config[`axis_${e}_label`]}getLabelText(e){const n=this.getLabelOptionByAxisId(e);return ze(n)?n:n?n.text:null}setLabelText(e,n){const a=this.owner,{config:i}=a,o=this.getLabelOptionByAxisId(e);ze(o)?i[`axis_${e}_label`]=n:o&&(o.text=n)}getLabelPosition(e,n){const a=this.owner.config.axis_rotated,i=this.getLabelOptionByAxisId(e),o=nr(i)&&i.position?i.position:n[+!a],s=l=>!!~o.indexOf(l);return{isInner:s("inner"),isOuter:s("outer"),isLeft:s("left"),isCenter:s("center"),isRight:s("right"),isTop:s("top"),isMiddle:s("middle"),isBottom:s("bottom")}}getAxisLabelPosition(e){return this.getLabelPosition(e,e==="x"?["inner-top","inner-right"]:["inner-right","inner-top"])}getLabelPositionById(e){return this.getAxisLabelPosition(e)}xForAxisLabel(e){const n=this.owner,{state:{width:a,height:i}}=n,o=this.getAxisLabelPosition(e);let s=o.isMiddle?-i/2:0;return this.isHorizontal(n,e!=="x")?s=o.isLeft?0:o.isCenter?a/2:a:o.isBottom&&(s=-i),s}textAnchorForAxisLabel(e){const n=this.owner,a=this.getAxisLabelPosition(e);let i=a.isMiddle?"middle":"end";return this.isHorizontal(n,e!=="x")?i=a.isLeft?"start":a.isCenter?"middle":"end":a.isBottom&&(i="start"),i}dxForAxisLabel(e){const n=this.owner,a=this.getAxisLabelPosition(e);let i=a.isBottom?"0.5em":"0";return this.isHorizontal(n,e!=="x")?i=a.isLeft?"0.5em":a.isRight?"-0.5em":"0":a.isTop&&(i="-0.5em"),i}dyForAxisLabel(e){const n=this.owner,{config:a}=n,i=a.axis_rotated,o=this.getAxisLabelPosition(e).isInner,s=a[`axis_${e}_tick_rotate`]?n.getHorizontalAxisHeight(e):0,{width:l}=this.getMaxTickSize(e);let c;if(e==="x"){const f=a.axis_x_height;i?c=o?"1.2em":-25-l:o?c="-0.5em":f?c=f-10:s?c=s-10:c="3em"}else c={y:["-0.5em",10,"3em","1.2em",10],y2:["1.2em",-20,"-2.2em","-0.5em",15]}[e],i?o?c=c[0]:s?c=s*(e==="y2"?-1:1)-c[1]:c=c[2]:c=o?c[3]:(c[4]+(a[`axis_${e}_inner`]?0:l+c[4]))*(e==="y"?-1:1);return c}getMaxTickSize(e,n){const a=this.owner,{config:i,state:{current:o},$el:{svg:s,chart:l}}=a,c=o.maxTickSize[e],f=`axis_${e}`,g={width:0,height:0};if(n||!i[`${f}_show`]||c.width>0&&a.filterTargetsToShow().length===0)return c;if(s){const v=/^y2?$/.test(e),m=a.filterTargetsToShow(a.data.targets),S=a.scale[e].copy().domain(a[`get${v?"Y":"X"}Domain`](m,e)),P=S.domain(),N=P[0]===P[1]&&P.every(K=>K>0),L=je(c.domain)&&c.domain[0]===c.domain[1]&&c.domain.every(K=>K>0);if(N||L)return c.size;c.domain=P,v||c.ticks.splice(0);const w=this.getAxis(e,S,!1,!1,!0),X=i[`${f}_tick_rotate`],W=i[`${f}_tick_count`];!i[`${f}_tick_values`]&&W&&w.tickValues(this.generateTickValues(P,W,v?this.isTimeSeriesY():this.isTimeSeries())),!v&&this.updateXAxisTickValues(m,w);const k=l.append("svg").style("visibility","hidden").style("position","fixed").style("top","0").style("left","0");w.create(k),k.selectAll("text").attr("transform",he(X)?`rotate(${X})`:null).each(function(K,at){const{width:ht,height:$t}=this.getBoundingClientRect();g.width=Math.max(g.width,ht),g.height=Math.max(g.height,$t),v||(c.ticks[at]=ht)}),k.remove()}return Object.keys(g).forEach(v=>{g[v]>0&&(c[v]=g[v])}),c}getXAxisTickTextY2Overflow(e){const n=this.owner,{axis:a,config:i,state:{current:o,isLegendRight:s,legendItemWidth:l}}=n,c=n.getAxisTickRotate("x"),f=c>0&&c<90;if((a.isCategorized()||a.isTimeSeries())&&i.axis_x_tick_fit&&(!i.axis_x_tick_culling||qn(i.axis_x_tick_culling))&&!i.axis_x_tick_multiline&&f){const g=i.axis_y2_show&&o.maxTickSize.y2.width||0,v=s&&l||0,m=o.width-n.getCurrentPaddingByDirection("left"),S=this.getXAxisTickMaxOverflow(c,m-e)-g-v,P=Math.max(0,S)+e;return Math.min(P,m/2)}return 0}getXAxisTickMaxOverflow(e,n){const a=this.owner,{axis:i,config:o,state:s}=a,l=i.isTimeSeries(),c=s.current.maxTickSize.x.ticks,f=c.length,{left:g,right:v}=s.axis.x.padding;let m=0;const S=f-(l&&o.axis_x_tick_fit?.5:0);for(let L=0;L{const c=this.getLabelText(l),f=`axis${Cn(l)}`,g=Tn[`${f}Label`];if(c){let v=i.select(`text.${g}`);v.empty()&&(v=i.select(`g.${Tn[f]}`).insert("text",":first-child").attr("class",g).attr("transform",["rotate(-90)",null][l==="x"?+!s:+s]).style("text-anchor",()=>this.textAnchorForAxisLabel(l))),o(v,e).attr("x",()=>this.xForAxisLabel(l)).attr("dx",()=>this.dxForAxisLabel(l)).attr("dy",()=>this.dyForAxisLabel(l)).text(c)}})}getPadding(e,n,a,i){const o=he(e)?e:e[n];return De(o)?this.owner.convertPixelToScale(/(bottom|top)/.test(n)?"y":"x",o,i):a}generateTickValues(e,n,a){let i=e;if(n){const o=ve(n)?n():n;if(o===1)i=[e[0]];else if(o===2)i=[e[0],e[e.length-1]];else if(o>2){const s=this.isCategorized(),l=o-2,c=e[0],f=e[e.length-1],g=(f-c)/(l+1);let v;i=[c];for(let m=0;mo-s)),i}generateTransitions(e){const n=this.owner,{$el:{axis:a},$T:i}=n,[o,s,l,c]=["x","y","y2","subX"].map(f=>i(a[f],e));return{axisX:o,axisY:s,axisY2:l,axisSubX:c}}redraw(e,n,a){const i=this.owner,{config:o,state:s,$el:l}=i,c=n?"0":null;["x","y","y2","subX"].forEach(f=>{const g=this[f],v=l.axis[f];g&&v&&(!a&&!o.transition_duration&&(g.config.withoutTransition=!0),v.style("opacity",c),g.create(e[`axis${Cn(f)}`]))}),this.updateAxes(),!s.rendered&&o.axis_tooltip&&this.setAxisTooltip()}redrawAxis(e,n,a,i,o){var s,l,c;const f=this.owner,{config:g,scale:v,$el:m}=f,S=!!v.zoom;let P;!S&&this.isCategorized()&&e.length===0&&v.x.domain([0,m.axis.x.selectAll(".tick").size()]),v.x&&e.length?(!S&&f.updateXDomain(e,n.UpdateXDomain,n.UpdateOrgXDomain,n.TrimXDomain),g.axis_x_tick_values||this.updateXAxisTickValues(e)):this.x&&(this.x.tickValues([]),(s=this.subX)==null||s.tickValues([])),g.zoom_rescale&&!i&&(P=v.x.orgDomain()),["y","y2"].forEach(N=>{const L=`axis_${N}_`,w=v[N];if(w){const X=g[`${L}tick_values`],W=g[`${L}tick_count`];if(w.domain(f.getYDomain(e,N,P)),!X&&W){const H=f.axis[N],k=w.domain();H.tickValues(this.generateTickValues(k,k.every(K=>K===0)?1:W,this.isTimeSeriesY()))}}}),this.redraw(a,f.hasArcType(),o),this.updateLabels(n.Transition),(n.UpdateXDomain||n.UpdateXAxis||n.Y)&&e.length&&this.setCulling(),n.Y&&((l=v.subY)==null||l.domain(f.getYDomain(e,"y")),(c=v.subY2)==null||c.domain(f.getYDomain(e,"y2")))}setCulling(){const e=this.owner,{config:n,state:{clip:a,current:i},$el:o}=e;["subX","x","y","y2"].forEach(s=>{const l=o.axis[s],f=`axis_${s==="subX"?"x":s}_tick_culling`,g=n[f];if(l&&g){const v=l.selectAll(".tick"),m=na(v.data()),S=m.length,P=n[`${f}_max`],N=n[`${f}_lines`];let L;if(S){for(let w=1;w{var f,g,v;if(ze(l)||l[c])if(s[c]=(f=o[c])==null?void 0:f.append("text").classed(Tn[`axis${c.toUpperCase()}Tooltip`],!0).attr("filter",n.updateTextBGColor({id:c},l)),a){const m=c==="x"?"x":"y",S=c==="y"?"1.15em":c==="x"?"-0.3em":"-0.4em";(g=s[c])==null||g.attr(m,S).attr(`d${c==="x"?"y":"x"}`,c==="x"?"0.4em":"-1.3em").style("text-anchor",c==="x"?"end":null)}else{const m=c==="x"?"y":"x",S=c==="x"?"1.15em":`${c==="y"?"-":""}0.4em`;(v=s[c])==null||v.attr(m,S).attr(`d${c==="x"?"x":"y"}`,c==="x"?"-1em":"0.3em").style("text-anchor",c==="y"?"end":null)}})}}var wy={initEventRect(){this.$el.main.select(`.${Se.chart}`).append("g").attr("class",Zn.eventRects).style("fill-opacity","0")},redrawEventRect(){var t;const e=this,{config:n,state:a,$el:i}=e,o=e.isMultipleX(),s=n.axis_x_inverted;if(i.eventRect)e.updateEventRect(i.eventRect,!0);else if(e.data.targets.length){const c=e.$el.main.select(`.${Zn.eventRects}`).style("cursor",n.zoom_enabled&&n.zoom_type!=="drag"?n.axis_rotated?"ns-resize":"ew-resize":null).classed(Zn.eventRectsMultiple,o).classed(Zn.eventRectsSingle,!o).selectAll(`.${Zn.eventRect}`).data([0]).enter().append("rect");e.updateEventRect(c),e.updateEventType(c),c.call(e.getDraggableSelection()),i.eventRect=c,e.state.inputType==="touch"&&!i.svg.on("touchstart.eventRect")&&!e.hasArcType()&&e.bindTouchOnEventRect(),a.rendered&&e.updateEventRect(i.eventRect,!0)}if(!o){const l=e.getMaxDataCountTarget();(!n.data_xSort||s)&&l.sort((c,f)=>s?f.x-c.x:c.x-f.x),e.updateDataIndexByX(l),e.updateXs(l),(t=e.updatePointClass)==null||t.call(e,!0),a.eventReceiver.data=l}e.updateEventRectData()},bindTouchOnEventRect(){const t=this,{config:e,state:n,$el:{eventRect:a,svg:i}}=t,o=m=>{if(t.isMultipleX())t.selectRectForMultipleXs(m);else{const S=t.getDataIndexFromEvent(n.event);t.callOverOutForTouch(S),S===-1?t.unselectRect():t.selectRectForSingle(m,S)}},s=()=>{t.unselectRect(),t.callOverOutForTouch()},l=e.interaction_inputType_touch.preventDefault,c=Co(l)&&l||!1,f=!isNaN(l)&&l||null;let g;const v=m=>{const S=m.type,N=m.changedTouches[0][`client${e.axis_rotated?"Y":"X"}`];S==="touchstart"?c?m.preventDefault():f!==null&&(g=N):S==="touchmove"&&(c||g===!0||f!==null&&Math.abs(g-N)>=f)&&(g=!0,m.preventDefault())};a.on("touchstart",m=>{n.event=m,t.updateEventRect()}).on("touchstart.eventRect touchmove.eventRect",m=>{if(n.event=m,!a.empty()&&a.classed(Zn.eventRect)){if(n.dragging||n.flowing||t.hasArcType()||m.touches.length>1)return;v(m),o(a.node())}else s()},!0).on("touchend.eventRect",m=>{n.event=m,!a.empty()&&a.classed(Zn.eventRect)&&(t.hasArcType()||!t.toggleShape||n.cancelClick)&&n.cancelClick&&(n.cancelClick=!1)},!0),i.on("touchstart",m=>{n.event=m;const{target:S}=m;S&&S!==a.node()&&s()})},updateEventRect(t,e=!1){const n=this,{state:a,$el:i}=n,{eventReceiver:o,width:s,height:l,rendered:c,resizing:f}=a,g=t||i.eventRect,v=()=>{if(o){const m=Zl(i.chart.node());o.rect=g.node().getBoundingClientRect().toJSON(),o.rect.top+=m.y,o.rect.left+=m.x}};(!c||f||e)&&(g.attr("x",0).attr("y",0).attr("width",s).attr("height",l),(!c||e)&&g.classed(Zn.eventRect,!0)),v()},updateEventType(t){const e=this,n=Co(t),a=n?e.$el.eventRect:t,i=n?t!==(a==null?void 0:a.datum().multipleX):!1;a&&(i&&(a==null||a.on("mouseover mousemove mouseout click",null)),e.isMultipleX()?e.generateEventRectsForMultipleXs(a):e.generateEventRectsForSingleX(a))},updateEventRectData(){const t=this,{config:e,scale:n,state:a}=t,i=n.zoom||n.x,o=e.axis_rotated,s=t.isMultipleX();let l,c,f,g;if(t.updateEventType(s),s)l=0,c=0,f=a.width,g=a.height;else{let S,P;if(t.axis.isCategorized())S=t.getEventRectWidth(),P=N=>i(N.x)-S/2;else{const N=({index:L})=>({prev:t.getPrevX(L),next:t.getNextX(L)});S=L=>{const w=N(L),X=i.domain();let W;return w.prev===null&&w.next===null?W=o?a.height:a.width:w.prev===null?W=(i(w.next)+i(L.x))/2:w.next===null?W=i(X[1])-(i(w.prev)+i(L.x))/2:(Object.keys(w).forEach((H,k)=>{var K;w[H]=(K=w[H])!=null?K:X[k]}),W=Math.max(0,(i(w.next)-i(w.prev))/2)),W},P=L=>{const w=N(L);let X;return w.prev===null&&w.next===null?X=0:w.prev===null?X=i(i.domain()[0]):X=(i(L.x)+i(w.prev))/2,X}}l=o?0:P,c=o?P:0,f=o?a.width:S,g=o?S:a.height}const{eventReceiver:v}=a,m=(S,P)=>ve(S)?S(P):S;v.coords.splice(v.data.length),v.data.forEach((S,P)=>{v.coords[P]={x:m(l,S),y:m(c,S),w:m(f,S),h:m(g,S)}})},selectRectForSingle(t,e){var n,a;const i=this,{config:o,$el:{main:s,circle:l}}=i,c=o.data_selection_enabled,f=o.data_selection_grouped,g=o.data_selection_isselectable,v=o.tooltip_grouped,m=i.getAllValuesOnIndex(e);if(v&&(i.showTooltip(m,t),(n=i.showGridFocus)==null||n.call(i,m),!c||f))return;!l&&s.selectAll(`.${Se.EXPANDED}:not(.${sn.shape}-${e})`).classed(Se.EXPANDED,!1);const S=s.selectAll(`.${sn.shape}-${e}`).classed(Se.EXPANDED,!0).style("cursor",g?"pointer":null).filter(function(P){return i.isWithinShape(this,P)});S.empty()&&!v&&o.interaction_onout&&((a=i.hideGridFocus)==null||a.call(i),i.hideTooltip(),!f&&i.setExpand(e)),S.call(P=>{var N,L;const w=P.data();c&&(f||g!=null&&g.bind(i.api)(w))&&(t.style.cursor="pointer"),v||(i.showTooltip(w,t),(N=i.showGridFocus)==null||N.call(i,w),(L=i.unexpandCircles)==null||L.call(i),P.each(X=>i.setExpand(e,X.id)))})},selectRectForMultipleXs(t,e=!0){const n=this,{config:a,state:i}=n,o=n.filterTargetsToShow(n.data.targets);if(i.dragging||n.hasArcType(o))return;const s=Hn(i.event,t),l=n.findClosestFromTargets(o,s);if(e&&i.mouseover&&(!l||l.id!==i.mouseover.id)&&(a.data_onout.call(n.api,i.mouseover),i.mouseover=void 0),!l){n.unselectRect();return}const f=(n.isBubbleType(l)||n.isScatterType(l)||!a.tooltip_grouped?[l]:n.filterByX(o,l.x)).map(v=>n.addName(v));n.showTooltip(f,t),n.setExpand(l.index,l.id,!0),n.showGridFocus(f);const g=n.dist(l,s);(n.isBarType(l.id)||g{const c=l?e.getDataIndexFromEvent(l):i.currentIdx;return c>-1?i.data[c]:null};o.on("mouseover",l=>{a.event=l,e.updateEventRect(),Object.values(e.$el.axisTooltip).forEach(c=>c==null?void 0:c.style("display",null))}).on("mousemove",function(l){const c=s(l);if(a.event=l,!c)return;let{index:f}=c;const g=n.line_step_type;if(n.line_step_tooltipMatch&&e.hasType("step")&&/^step\-(before|after)$/.test(g)){const m=e.scale.zoom||e.scale.x,S=e.axis.xs[f],P=m.invert(Hn(l,this)[0]);g==="step-after"&&PS&&(f+=1)}e.showAxisGridFocus();const v=n.tooltip_grouped&&f===i.currentIdx;if(a.dragging||a.flowing||e.hasArcType()||v){n.tooltip_show&&v&&e.setTooltipPosition();return}f!==i.currentIdx&&(e.setOverOut(!1,i.currentIdx),i.currentIdx=f),f===-1?e.unselectRect():e.selectRectForSingle(this,f),e.setOverOut(f!==-1,f)}).on("mouseout",l=>{a.event=l,!(!n||e.hasArcType()||i.currentIdx===-1||!n.interaction_onout)&&(e.hideAxisGridFocus(),e.unselectRect(),e.setOverOut(!1,i.currentIdx),i.currentIdx=-1)})}return o},clickHandlerForSingleX(t,e){const n=e,{config:a,state:i,$el:{main:o}}=n;if(!t||n.hasArcType()||i.cancelClick){i.cancelClick&&(i.cancelClick=!1);return}const{index:s}=t;o.selectAll(`.${sn.shape}-${s}`).each(function(l){var c;(a.data_selection_grouped||n.isWithinShape(this,l))&&((c=n.toggleShape)==null||c.call(n,this,l,s),a.data_onclick.bind(n.api)(l,this))})},generateEventRectsForMultipleXs(t){const e=this,{config:n,state:a}=e;t.on("click",function(i){a.event=i,e.clickHandlerForMultipleXS.bind(this)(e)}).datum({multipleX:!0}),a.inputType==="mouse"&&t.on("mouseover mousemove",function(i){a.event=i,e.selectRectForMultipleXs(this)}).on("mouseout",i=>{a.event=i,!(!e.config||e.hasArcType()||!n.interaction_onout)&&e.unselectRect()})},clickHandlerForMultipleXS(t){const e=t,{config:n,state:a}=e,i=e.filterTargetsToShow(e.data.targets);if(e.hasArcType(i))return;const o=Hn(a.event,this),s=e.findClosestFromTargets(i,o),l=e.getPointSensitivity(s);s&&(e.isBarType(s.id)||e.dist(s,o)+t;var Dy={generateFlow(t){const e=this,{data:n,state:a,$el:i}=e;return function(){const o=t.flow.length;a.flowing=!0,n.targets.forEach(l=>{l.values.splice(0,o)}),e.updateXGrid&&e.updateXGrid(!0);const s={};["axis.x","grid.x","gridLines.x","region.list","text","bar","line","area","circle"].forEach(l=>{const c=l.split(".");let f=i[c[0]];f&&c.length>1&&(f=f[c[1]]),f!=null&&f.size()&&(s[l]=f)}),e.hideGridFocus(),e.setFlowList(s,t)}},setFlowList(t,e){const n=this,{flow:a,targets:i}=e,{duration:o=e.duration,index:s,length:l,orgDataCount:c}=a,f=n.getFlowTransform(i,c,s,l),g=ec();let v;g.add(Object.keys(t).map(m=>(v=t[m].transition().ease(My).duration(o),m==="axis.x"?v=v.call(S=>{n.axis.x.setTransition(S).create(S)}):m==="region.list"?v=v.filter(n.isRegionOnX).attr("transform",f):v=v.attr("transform",f),v))),v.call(g,()=>{n.cleanUpFlow(t,e)})},cleanUpFlow(t,e){const n=this,{config:a,state:i,$el:{svg:o}}=n,s=a.axis_rotated,{flow:l,shape:c,xv:f}=e,{cx:g,cy:v,xForText:m,yForText:S}=c.pos,{done:P=()=>{},length:N}=l;N&&(["circle","text","shape","eventRect"].forEach(L=>{const w=[];for(let X=0;X{const w=t[L];if(L!=="axis.x"&&w.attr("transform",null),L==="grid.x")w.attr(i.xgridAttr);else if(L==="gridLines.x")w.attr("x1",s?0:f).attr("x2",s?i.width:f),w.select("text").attr("x",s?i.width:0).attr("y",f);else if(/^(area|bar|line)$/.test(L))w.attr("d",c.type[L]);else if(L==="text")w.attr("x",m).attr("y",S).style("fill-opacity",n.opacityForText.bind(n));else if(L==="circle")if(n.isCirclePoint())w.attr("cx",g).attr("cy",v);else{const X=H=>g(H)-a.point_r,W=H=>v(H)-a.point_r;w.attr("x",X).attr("y",W)}else L==="region.list"&&w.select("rect").filter(n.isRegionOnX).attr("x",n.regionX.bind(n)).attr("width",n.regionWidth.bind(n))}),a.interaction_enabled&&n.redrawEventRect(),P.call(n.api),i.flowing=!1},getFlowTransform(t,e,n,a){const i=this,{data:o,scale:{x:s}}=i,l=o.targets[0].values;let c=i.getValueOnIndex(l,n),f=i.getValueOnIndex(l,n+a),g;const v=s.domain(),m=i.updateXDomain(t,!0,!0);e?e===1||(c==null?void 0:c.x)===(f==null?void 0:f.x)?g=s(v[0])-s(m[0]):g=i.axis.isTimeSeries()?s(v[0])-s(m[0]):s((c==null?void 0:c.x)||0)-s(f.x):l.length!==1?g=s(v[0])-s(m[0]):i.axis.isTimeSeries()?(c=i.getValueOnIndex(l,0),f=i.getValueOnIndex(l,l.length-1),g=s(c.x)-s(f.x)):g=Dr(m)/2;const S=Dr(v)/Dr(m);return`translate(${g},0) scale(${S},1)`}},Ly={initClip(){const t=this,{clip:e,datetimeId:n}=t.state;e.id=`${n}-clip`,e.idXAxis=`${e.id}-xaxis`,e.idYAxis=`${e.id}-yaxis`,e.idGrid=`${e.id}-grid`,e.path=t.getClipPath(e.id),e.pathXAxis=t.getClipPath(e.idXAxis),e.pathYAxis=t.getClipPath(e.idYAxis),e.pathGrid=t.getClipPath(e.idGrid)},getClipPath(t){const e=this,{config:n}=e;return!n.clipPath&&/-clip$/.test(t)||!n.axis_x_clipPath&&/-clip-xaxis$/.test(t)||!n.axis_y_clipPath&&/-clip-yaxis$/.test(t)?null:`url(#${t})`},appendClip(t,e){e&&t.append("clipPath").attr("id",e).append("rect")},setXAxisClipPath(t){const e=this,{config:n,state:{margin:a,width:i,height:o}}=e,s=n.axis_rotated,l=Math.max(30,a.left)-(s?0:20),c=(s?a.top+o+10:a.bottom)+20,f=s?-(1+l):-(l-1),g=-15,v=s?a.left+20:i+10+l;t.attr("x",f).attr("y",g).attr("width",v).attr("height",c)},setYAxisClipPath(t){const e=this,{config:n,state:{margin:a,width:i,height:o}}=e,s=n.axis_rotated,l=Math.max(30,a.left)-(s?20:0),c=n.axis_y_inner,f=c&&!s?n.axis_y_label.text?-20:-1:s?-(1+l):-(l-1),g=-(s?20:a.top),v=(s?i+15+l:a.left+20)+(c?20:0),m=(s?a.bottom+10:a.top+o)+10;t.attr("x",f).attr("y",g).attr("width",v).attr("height",m)},updateXAxisTickClip(){const t=this,{config:e,state:{clip:n,xAxisHeight:a},$el:{defs:i}}=t,o=t.getHorizontalAxisHeight("x");if(i&&!n.idXAxisTickTexts){const s=`${n.id}-xaxisticktexts`;t.appendClip(i,s),n.pathXAxisTickTexts=t.getClipPath(n.idXAxisTickTexts),n.idXAxisTickTexts=s}!e.axis_x_tick_multiline&&t.getAxisTickRotate("x")&&o!==a&&(t.setXAxisTickClipWidth(),t.setXAxisTickTextClipPathWidth()),t.state.xAxisHeight=o},setXAxisTickClipWidth(){const t=this,{config:e,state:{current:{maxTickSize:n}}}=t,a=t.getAxisTickRotate("x");if(!e.axis_x_tick_multiline&&a){const i=Math.sin(Math.PI/180*Math.abs(a));n.x.clipPath=(t.getHorizontalAxisHeight("x")-20)/i}else n.x.clipPath=null},setXAxisTickTextClipPathWidth(){const t=this,{state:{clip:e,current:n},$el:{svg:a}}=t;a&&a.select(`#${e.idXAxisTickTexts} rect`).attr("width",n.maxTickSize.x.clipPath).attr("height",30)}};const Ny=t=>De(t.position)||"end",Fy=t=>t.position==="start"?4:t.position==="middle"?0:-4;function Cu(t,e,n){return a=>{let i=t?0:e;return a.position==="start"?i=t?-n:0:a.position==="middle"&&(i=(t?-n:e)/2),i}}function Pu(t,e){e==="grid"&&t.each(function(){const n=ot(this);["x1","x2","y1","y2"].forEach(a=>n.attr(a,+n.attr(a)))})}var By={hasGrid(){const{config:t}=this;return["x","y"].some(e=>t[`grid_${e}_show`]||t[`grid_${e}_lines`].length)},initGrid(){const t=this;t.hasGrid()&&t.initGridLines(),t.initFocusGrid()},initGridLines(){const t=this,{config:e,state:{clip:n},$el:a}=t;(e.grid_x_lines.length||e.grid_y_lines.length)&&(a.gridLines.main=a.main.insert("g",`.${Se.chart}${e.grid_lines_front?" + *":""}`).attr("clip-path",n.pathGrid).attr("class",`${on.grid} ${on.gridLines}`),a.gridLines.main.append("g").attr("class",on.xgridLines),a.gridLines.main.append("g").attr("class",on.ygridLines),a.gridLines.x=Uc([]))},updateXGrid(t){const e=this,{config:n,scale:a,state:i,$el:{main:o,grid:s}}=e,l=n.axis_rotated,c=e.generateGridData(n.grid_x_type,a.x),f=e.axis.isCategorized()?e.axis.x.tickOffset():0,g=v=>(a.zoom||a.x)(v)+f*(l?-1:1);i.xgridAttr=l?{x1:0,x2:i.width,y1:g,y2:g}:{x1:g,x2:g,y1:0,y2:i.height},s.x=o.select(`.${on.xgrids}`).selectAll(`.${on.xgrid}`).data(c),s.x.exit().remove(),s.x=s.x.enter().append("line").attr("class",on.xgrid).merge(s.x),t||s.x.each(function(){const v=ot(this);Object.keys(i.xgridAttr).forEach(m=>{v.attr(m,i.xgridAttr[m]).style("opacity",()=>v.attr(l?"y1":"x1")===(l?i.height:0)?"0":null)})})},updateYGrid(){const t=this,{axis:e,config:n,scale:a,state:i,$el:{grid:o,main:s}}=t,l=n.axis_rotated,c=g=>a.y(g),f=e.y.getGeneratedTicks(n.grid_y_ticks)||t.scale.y.ticks(n.grid_y_ticks);o.y=s.select(`.${on.ygrids}`).selectAll(`.${on.ygrid}`).data(f),o.y.exit().remove(),o.y=o.y.enter().append("line").attr("class",on.ygrid).merge(o.y),o.y.attr("x1",l?c:0).attr("x2",l?c:i.width).attr("y1",l?0:c).attr("y2",l?i.height:c),Pu(o.y,"grid")},updateGrid(){const t=this,{$el:{grid:e,gridLines:n}}=t;!n.main&&t.initGridLines(),e.main.style("visibility",t.hasArcType()?"hidden":null),t.hideGridFocus(),t.updateGridLines("x"),t.updateGridLines("y")},updateGridLines(t){const e=this,{config:n,$el:{gridLines:a,main:i},$T:o}=e,s=n.axis_rotated,l=t==="x";n[`grid_${t}_show`]&&e[`update${t.toUpperCase()}Grid`]();let c=i.select(`.${on[`${t}gridLines`]}`).selectAll(`.${on[`${t}gridLine`]}`).data(n[`grid_${t}_lines`]);o(c.exit()).style("opacity","0").remove();const f=c.enter().append("g");f.append("line").style("opacity","0"),c=f.merge(c),c.each(function(g){const v=ot(this);v.select("text").empty()&&g.text&&v.append("text").style("opacity","0")}),o(c.attr("class",g=>`${on[`${t}gridLine`]} ${g.class||""}`.trim()).select("text").attr("text-anchor",Ny).attr("transform",()=>l?s?null:"rotate(-90)":s?"rotate(-90)":null).attr("dx",Fy).attr("dy",-5)).text(function(g){var v;return(v=g.text)!=null?v:this.remove()}),a[t]=c},redrawGrid(t){const e=this,{config:{axis_rotated:n},state:{width:a,height:i},$el:{gridLines:o},$T:s}=e,l=e.xv.bind(e),c=e.yv.bind(e);let f=o.x.select("line"),g=o.x.select("text"),v=o.y.select("line"),m=o.y.select("text");return f=s(f,t).attr("x1",n?0:l).attr("x2",n?a:l).attr("y1",n?l:0).attr("y2",n?l:i),g=s(g,t).attr("x",Cu(!n,a,i)).attr("y",l),v=s(v,t).attr("x1",n?c:0).attr("x2",n?c:a).attr("y1",n?0:c).attr("y2",n?i:c),m=s(m,t).attr("x",Cu(n,a,i)).attr("y",c),[f.style("opacity",null),g.style("opacity",null),v.style("opacity",null),m.style("opacity",null)]},initFocusGrid(){const t=this,{config:e,state:{clip:n},$el:a}=t,i=e.grid_front,o=`.${i&&a.gridLines.main?on.gridLines:Se.chart}${i?" + *":""}`,s=a.main.insert("g",o).attr("clip-path",n.pathGrid).attr("class",on.grid);if(a.grid.main=s,e.grid_x_show&&s.append("g").attr("class",on.xgrids),e.grid_y_show&&s.append("g").attr("class",on.ygrids),e.axis_tooltip){const l=s.append("g").attr("class","bb-axis-tooltip");l.append("line").attr("class","bb-axis-tooltip-x"),l.append("line").attr("class","bb-axis-tooltip-y")}e.interaction_enabled&&e.grid_focus_show&&!e.axis_tooltip&&(s.append("g").attr("class",qe.xgridFocus).append("line").attr("class",qe.xgridFocus),e.grid_focus_y&&!e.tooltip_grouped&&s.append("g").attr("class",qe.ygridFocus).append("line").attr("class",qe.ygridFocus))},showAxisGridFocus(){var t,e;const n=this,{config:a,format:i,state:{event:o,width:s,height:l}}=n,c=a.axis_rotated,[f,g]=Hn(o,(t=n.$el.eventRect)==null?void 0:t.node()),v={x:f,y:g};for(const[m,S]of Object.entries(n.$el.axisTooltip)){const P=m==="x"&&!c||m!=="x"&&c?"x":"y",N=v[P];let L=(e=n.scale[m])==null?void 0:e.invert(N);L&&(L=m==="x"&&n.axis.isTimeSeries()?i.xAxisTick(L):L==null?void 0:L.toFixed(2),S==null||S.attr(P,N).text(L))}n.$el.main.selectAll("line.bb-axis-tooltip-x, line.bb-axis-tooltip-y").style("visibility",null).each(function(m,S){const P=ot(this);S===0?P.attr("x1",f).attr("x2",f).attr("y1",S?0:l).attr("y2",S?l:0):P.attr("x1",S?0:s).attr("x2",S?s:0).attr("y1",g).attr("y2",g)})},hideAxisGridFocus(){const t=this;t.$el.main.selectAll("line.bb-axis-tooltip-x, line.bb-axis-tooltip-y").style("visibility","hidden"),Object.values(t.$el.axisTooltip).forEach(e=>e==null?void 0:e.style("display","none"))},showGridFocus(t){var e;const n=this,{config:a,state:{width:i,height:o}}=n,s=a.axis_rotated,l=n.$el.main.selectAll(`line.${qe.xgridFocus}, line.${qe.ygridFocus}`),c=(t||[l.datum()]).filter(v=>v&&De(n.getBaseValue(v)));if(!a.tooltip_show||c.length===0||!a.axis_x_forceAsSingle&&n.hasType("bubble")||n.hasArcType())return;const f=a.grid_focus_edge&&!a.tooltip_grouped,g=n.xx.bind(n);l.style("visibility",null).data(c.concat(c)).each(function(v){const m=ot(this),S={x:g(v),y:n.getYScaleById(v.id)(v.value)};let P;if(m.classed(qe.xgridFocus))P=s?[null,S.x,f?S.y:i,S.x]:[S.x,f?S.y:null,S.x,o];else{const N=n.axis.getId(v.id)==="y2";P=s?[S.y,f&&!N?S.x:null,S.y,f&&N?S.x:o]:[f&&N?S.x:null,S.y,f&&!N?S.x:i,S.y]}["x1","y1","x2","y2"].forEach((N,L)=>m.attr(N,P[L]))}),Pu(l,"grid"),(e=n.showCircleFocus)==null||e.call(n,t)},hideGridFocus(){var t;const e=this,{state:{inputType:n,resizing:a},$el:{main:i}}=e;(n==="mouse"||!a)&&(i.selectAll(`line.${qe.xgridFocus}, line.${qe.ygridFocus}`).style("visibility","hidden"),(t=e.hideCircleFocus)==null||t.call(e))},updateGridFocus(){var t;const e=this,{state:{inputType:n,width:a,height:i,resizing:o},$el:{grid:s}}=e,l=s.main.select(`line.${qe.xgridFocus}`);if(n==="touch")l.empty()?o&&((t=e.showCircleFocus)==null||t.call(e)):e.showGridFocus();else{const c=e.config.axis_rotated;l.attr("x1",c?0:-10).attr("x2",c?a:-10).attr("y1",c?-10:0).attr("y2",c?-10:i)}return!0},generateGridData(t,e){const n=this,a=n.$el.main.select(`.${Tn.axisX}`).selectAll(".tick").size();let i=[];if(t==="year"){const o=n.getXDomain(),[s,l]=o.map(c=>c.getFullYear());for(let c=s;c<=l;c++)i.push(new Date(`${c}-01-01 00:00:00`))}else i=e.ticks(10),i.length>a&&(i=i.filter(o=>String(o).indexOf(".")<0));return i},getGridFilterToRemove(t){return t?e=>{let n=!1;return(je(t)?t.concat():[t]).forEach(a=>{("value"in a&&e.value===a.value||"class"in a&&e.class===a.class)&&(n=!0)}),n}:()=>!0},removeGridLines(t,e){const n=this,{config:a,$T:i}=n,o=n.getGridFilterToRemove(t),s=g=>!o(g),l=e?on.xgridLines:on.ygridLines,c=e?on.xgridLine:on.ygridLine;i(n.$el.main.select(`.${l}`).selectAll(`.${c}`).filter(o)).style("opacity","0").remove();const f=`grid_${e?"x":"y"}_lines`;a[f]=a[f].filter(s)}},Uy={initRegion(){const t=this,{$el:e}=t;e.region.main=e.main.insert("g",":first-child").attr("clip-path",t.state.clip.path).attr("class",$a.regions)},updateRegion(){const t=this,{config:e,$el:{region:n},$T:a}=t;n.main||t.initRegion(),n.main.style("visibility",t.hasArcType()?"hidden":null);const i=n.main.selectAll(`.${$a.region}`).data(e.regions);a(i.exit()).style("opacity","0").remove();const o=i.enter().append("g");o.append("rect").style("fill-opacity","0"),n.list=o.merge(i).attr("class",t.classRegion.bind(t)),n.list.each(function(s){var l;ot(this).select("text").empty()&&((l=s.label)!=null&&l.text)&&ot(this).append("text").style("opacity","0")})},redrawRegion(t){const e=this,{$el:{region:n},$T:a}=e,i=e.regionX.bind(e),o=e.regionY.bind(e),s=["width","height"];let l=n.list.select("rect"),c=n.list.selectAll("text");return l=a(l,t).attr("x",i).attr("y",o).attr("width",e.regionWidth.bind(e)).attr("height",e.regionHeight.bind(e)),c=a(c,t).text(f=>{var g;return(g=f.label)==null?void 0:g.text}).attr("transform",({label:f})=>f.rotated?" rotate(-90)":null).attr("transform",function(f){var g;const{x:v=0,y:m=0,center:S=!1,rotated:P=!1}=(g=f.label)!=null?g:{},N=this.previousElementSibling,L={x:0,y:0};return ze(S)&&["x","y"].forEach((w,X)=>{S.indexOf(w)>-1&&(L[w]=(+N.getAttribute(s[X])-Ma(this)[s[X]])/2)}),`translate(${i(f)+L.x+v}, ${o(f)+L.y+m})${P?" rotate(-90)":""}`}).attr("text-anchor",({label:f})=>f!=null&&f.rotated?"end":null).attr("dy","1em").style("fill",({label:f})=>{var g;return(g=f==null?void 0:f.color)!=null?g:null}),[l.style("fill-opacity",f=>De(f.opacity)?f.opacity:null).on("end",function(){ot(this.parentNode).selectAll("rect:not([x])").remove()}),c.style("opacity",null)]},regionX(t){return this.getRegionSize("x",t)},regionY(t){return this.getRegionSize("y",t)},regionWidth(t){return this.getRegionSize("width",t)},regionHeight(t){return this.getRegionSize("height",t)},getRegionSize(t,e){const n=this,{config:a,scale:i,state:o}=n,s=a.axis_rotated,l=/(x|y|y2)/.test(t),c=l?t==="x":t==="width",f=!l&&n[c?"regionX":"regionY"](e);let g=l?"start":"end",v=l?0:o[t],m;if(e.axis==="y"||e.axis==="y2"?(!l&&!c?g="start":l&&!c&&(g="end"),(c?s:!s)&&g in e&&(m=i[e.axis])):(c?!s:s)&&g in e&&(m=i.zoom||i.x),m){let S=0;v=e[g],n.axis.isTimeSeries(e.axis)?v=Yn.call(n,v):/(x|width)/.test(t)&&n.axis.isCategorized()&&isNaN(v)&&(v=a.axis_x_categories.indexOf(v),S=n.axis.x.tickOffset()*(g==="start"?-1:1)),v=m(v)+S}return l?v:v0&&(!i.axis_x_tick_autorotate||a.needToRotateXAxisTickTexts());return(i.axis_x_tick_multiline||L)&&N.height>S&&(P+=N.height-S),P+(a.axis.getLabelPositionById(t).isInner?0:10)+(t==="y2"&&!f?-10:0)},getEventRectWidth(){const t=this,{config:e,axis:n}=t,a=e.axis_x_inverted,i=n.x.tickInterval();return Math.max(0,a?Math.abs(i):i)},getAxisTickRotate(t){const e=this,{axis:n,config:a,state:i,$el:o}=e;let s=a[`axis_${t}_tick_rotate`];if(t==="x"){const l=n.isCategorized()||n.isTimeSeries();if(a.axis_x_tick_fit&&l){const c=a.axis_x_tick_count,f=i.current.maxTickSize.x.ticks.length;let g=0;if(c?g=c>f?f:c:f&&(g=f),g!==i.axis.x.tickCount){const{targets:v}=e.data;i.axis.x.padding=e.getXDomainPadding([e.getXDomainMinMax(v,"min"),e.getXDomainMinMax(v,"max")],g)}i.axis.x.tickCount=g}o.svg&&a.axis_x_tick_autorotate&&a.axis_x_tick_fit&&!a.axis_x_tick_multiline&&!a.axis_x_tick_culling&&l&&(s=e.needToRotateXAxisTickTexts()?a.axis_x_tick_rotate:0)}return s},needToRotateXAxisTickTexts(){const t=this,{state:{axis:e,current:n,isLegendRight:a,legendItemWidth:i}}=t,o=a&&i,s=n.width-o-t.getCurrentPaddingByDirection("left")-t.getCurrentPaddingByDirection("right"),l=e.x.tickCount+e.x.padding.left+e.x.padding.right,{width:c}=t.axis.getMaxTickSize("x"),f=l?s/l:0;return c>f}},jy={axis_x_clipPath:!0,axis_x_show:!0,axis_x_forceAsSingle:!1,axis_x_type:"indexed",axis_x_localtime:!0,axis_x_categories:[],axis_x_tick_centered:!1,axis_x_tick_format:void 0,axis_x_tick_culling:{},axis_x_tick_culling_max:10,axis_x_tick_culling_lines:!0,axis_x_tick_count:void 0,axis_x_tick_show:!0,axis_x_tick_text_show:!0,axis_x_tick_text_inner:!1,axis_x_tick_text_position:{x:0,y:0},axis_x_tick_fit:!0,axis_x_tick_values:null,axis_x_tick_autorotate:!1,axis_x_tick_rotate:0,axis_x_tick_outer:!0,axis_x_tick_multiline:!0,axis_x_tick_width:null,axis_x_tick_tooltip:!1,axis_x_max:void 0,axis_x_min:void 0,axis_x_inverted:!1,axis_x_padding:{},axis_x_height:void 0,axis_x_extent:void 0,axis_x_label:{},axis_x_axes:[]},Vy={axis_y_clipPath:!0,axis_y_show:!0,axis_y_type:"indexed",axis_y_max:void 0,axis_y_min:void 0,axis_y_inverted:!1,axis_y_center:void 0,axis_y_inner:!1,axis_y_label:{},axis_y_tick_format:void 0,axis_y_tick_culling:!1,axis_y_tick_culling_max:5,axis_y_tick_culling_lines:!0,axis_y_tick_outer:!0,axis_y_tick_values:null,axis_y_tick_rotate:0,axis_y_tick_count:void 0,axis_y_tick_show:!0,axis_y_tick_stepSize:null,axis_y_tick_text_show:!0,axis_y_tick_text_position:{x:0,y:0},axis_y_tick_time_value:void 0,axis_y_padding:{},axis_y_default:void 0,axis_y_axes:[]},Gy={axis_y2_show:!1,axis_y2_type:"indexed",axis_y2_max:void 0,axis_y2_min:void 0,axis_y2_inverted:!1,axis_y2_center:void 0,axis_y2_inner:!1,axis_y2_label:{},axis_y2_tick_format:void 0,axis_y2_tick_culling:!1,axis_y2_tick_culling_max:5,axis_y2_tick_culling_lines:!0,axis_y2_tick_outer:!0,axis_y2_tick_values:null,axis_y2_tick_rotate:0,axis_y2_tick_count:void 0,axis_y2_tick_show:!0,axis_y2_tick_stepSize:null,axis_y2_tick_text_show:!0,axis_y2_tick_text_position:{x:0,y:0},axis_y2_padding:{},axis_y2_default:void 0,axis_y2_axes:[]},Xy=Object.defineProperty,wu=Object.getOwnPropertySymbols,Hy=Object.prototype.hasOwnProperty,Yy=Object.prototype.propertyIsEnumerable,Mu=(t,e,n)=>e in t?Xy(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,Ts=(t,e)=>{for(var n in e||(e={}))Hy.call(e,n)&&Mu(t,n,e[n]);if(wu)for(var n of wu(e))Yy.call(e,n)&&Mu(t,n,e[n]);return t},Wy=Ts(Ts(Ts({axis_evalTextSize:!0,axis_rotated:!1,axis_tooltip:!1},jy),Vy),Gy),Ky={grid_x_show:!1,grid_x_type:"tick",grid_x_lines:[],grid_y_show:!1,grid_y_lines:[],grid_y_ticks:void 0,grid_focus_edge:!1,grid_focus_show:!0,grid_focus_y:!1,grid_front:!1,grid_lines_front:!0},Zy={data_xs:{},data_xFormat:"%Y-%m-%d",data_xLocaltime:!0,data_xSort:!0,data_axes:{},data_regions:{},data_stack_normalize:!1};const Jy=[sy,ly,cy,uy,fy,dy,hy],Du={axis:Cy,clip:Ly,eventrect:wy,flow:Dy,grid:By,region:Uy,sizeAxis:zy},Lu={optDataAxis:Zy,optAxis:Wy,optGrid:Ky};var I1=Array.prototype.slice;function $s(t){return typeof t=="object"&&"length"in t?t:Array.from(t)}function Le(t){return function(){return t}}function Qy(t,e){return et?1:e>=t?0:NaN}function ky(t){return t}function qy(){var t=ky,e=Qy,n=null,a=Le(0),i=Le(zi),o=Le(0);function s(l){var c,f=(l=$s(l)).length,g,v,m=0,S=new Array(f),P=new Array(f),N=+a.apply(this,arguments),L=Math.min(zi,Math.max(-zi,i.apply(this,arguments)-N)),w,X=Math.min(Math.abs(L)/f,o.apply(this,arguments)),W=X*(L<0?-1:1),H;for(c=0;c0&&(m+=H);for(e!=null?S.sort(function(k,K){return e(P[k],P[K])}):n!=null&&S.sort(function(k,K){return n(l[k],l[K])}),c=0,v=m?(L-f*W)/m:0;c0?H*v:0)+W,P[g]={data:l[g],index:c,value:H,startAngle:N,endAngle:w,padAngle:X};return P}return s.value=function(l){return arguments.length?(t=typeof l=="function"?l:Le(+l),s):t},s.sortValues=function(l){return arguments.length?(e=l,n=null,s):e},s.sort=function(l){return arguments.length?(n=l,e=null,s):n},s.startAngle=function(l){return arguments.length?(a=typeof l=="function"?l:Le(+l),s):a},s.endAngle=function(l){return arguments.length?(i=typeof l=="function"?l:Le(+l),s):i},s.padAngle=function(l){return arguments.length?(o=typeof l=="function"?l:Le(+l),s):o},s}var _y=Math.pow;const Ss=Math.PI,As=2*Ss,Gr=1e-6,tx=As-Gr;function Nu(t){this._+=t[0];for(let e=1,n=t.length;e=0))throw new Error(`invalid digits: ${t}`);if(e>15)return Nu;const n=_y(10,e);return function(a){this._+=a[0];for(let i=1,o=a.length;iGr)if(!(Math.abs(v*c-f*g)>Gr)||!o)this._append`L${this._x1=e},${this._y1=n}`;else{let S=a-s,P=i-l,N=c*c+f*f,L=S*S+P*P,w=Math.sqrt(N),X=Math.sqrt(m),W=o*Math.tan((Ss-Math.acos((N+m-L)/(2*w*X)))/2),H=W/X,k=W/w;Math.abs(H-1)>Gr&&this._append`L${e+H*g},${n+H*v}`,this._append`A${o},${o},0,0,${+(v*S>g*P)},${this._x1=e+k*c},${this._y1=n+k*f}`}}arc(e,n,a,i,o,s){if(e=+e,n=+n,a=+a,s=!!s,a<0)throw new Error(`negative radius: ${a}`);let l=a*Math.cos(i),c=a*Math.sin(i),f=e+l,g=n+c,v=1^s,m=s?i-o:o-i;this._x1===null?this._append`M${f},${g}`:(Math.abs(this._x1-f)>Gr||Math.abs(this._y1-g)>Gr)&&this._append`L${f},${g}`,a&&(m<0&&(m=m%As+As),m>tx?this._append`A${a},${a},0,1,${v},${e-l},${n-c}A${a},${a},0,1,${v},${this._x1=f},${this._y1=g}`:m>Gr&&this._append`A${a},${a},0,${+(m>=Ss)},${v},${this._x1=e+a*Math.cos(o)},${this._y1=n+a*Math.sin(o)}`)}rect(e,n,a,i){this._append`M${this._x0=this._x1=+e},${this._y0=this._y1=+n}h${a=+a}v${+i}h${-a}Z`}toString(){return this._}}function nx(){return new Wi}nx.prototype=Wi.prototype;function O1(t=3){return new Wi(+t)}function Es(t){let e=3;return t.digits=function(n){if(!arguments.length)return e;if(n==null)e=null;else{const a=Math.floor(n);if(!(a>=0))throw new RangeError(`invalid digits: ${n}`);e=a}return t},()=>new Wi(e)}function rx(t){return t.innerRadius}function ax(t){return t.outerRadius}function ix(t){return t.startAngle}function ox(t){return t.endAngle}function sx(t){return t&&t.padAngle}function lx(t,e,n,a,i,o,s,l){var c=n-t,f=a-e,g=s-i,v=l-o,m=v*c-g*f;if(!(m*mQ*Q+St*St&&(ht=dt,$t=st),{cx:ht,cy:$t,x01:-g,y01:-v,x11:ht*(i/k-1),y11:$t*(i/k-1)}}function Fu(){var t=rx,e=ax,n=Le(0),a=null,i=ix,o=ox,s=sx,l=null,c=Es(f);function f(){var g,v,m=+t.apply(this,arguments),S=+e.apply(this,arguments),P=i.apply(this,arguments)-Ui,N=o.apply(this,arguments)-Ui,L=Hc(N-P),w=N>P;if(l||(l=g=c()),Sbn))l.moveTo(0,0);else if(L>zi-bn)l.moveTo(S*jr(P),S*rr(P)),l.arc(0,0,S,P,N,!w),m>bn&&(l.moveTo(m*jr(N),m*rr(N)),l.arc(0,0,m,N,P,w));else{var X=P,W=N,H=P,k=N,K=L,at=L,ht=s.apply(this,arguments)/2,$t=ht>bn&&(a?+a.apply(this,arguments):oa(m*m+S*S)),dt=fs(Hc(S-m)/2,+n.apply(this,arguments)),st=dt,Vt=dt,vt,Q;if($t>bn){var St=Yc($t/m*rr(ht)),ct=Yc($t/S*rr(ht));(K-=St*2)>bn?(St*=w?1:-1,H+=St,k-=St):(K=0,H=k=(P+N)/2),(at-=ct*2)>bn?(ct*=w?1:-1,X+=ct,W-=ct):(at=0,X=W=(P+N)/2)}var At=S*jr(X),Gt=S*rr(X),Bt=m*jr(k),Kt=m*rr(k);if(dt>bn){var ne=S*jr(W),le=S*rr(W),be=m*jr(H),Oe=m*rr(H),Ce;if(Lbn?Vt>bn?(vt=Ki(be,Oe,At,Gt,S,Vt,w),Q=Ki(ne,le,Bt,Kt,S,Vt,w),l.moveTo(vt.cx+vt.x01,vt.cy+vt.y01),Vtbn)||!(K>bn)?l.lineTo(Bt,Kt):st>bn?(vt=Ki(Bt,Kt,ne,le,m,-st,w),Q=Ki(At,Gt,be,Oe,m,-st,w),l.lineTo(vt.cx+vt.x01,vt.cy+vt.y01),ste in t?cx(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,gx=(t,e)=>{for(var n in e||(e={}))dx.call(e,n)&&Uu(t,n,e[n]);if(Bu)for(var n of Bu(e))hx.call(e,n)&&Uu(t,n,e[n]);return t},vx=(t,e)=>ux(t,fx(e));function zu(t=0){const e=this,{config:n,state:a}=e,i=e.hasMultiArcGauge(),o=a.gaugeArcWidth/e.filterTargetsToShow(e.data.targets).length,s=t?Math.min(a.radiusExpanded*t-a.radius,o*.8-(1-t)*100):0;return{inner(l){const{innerRadius:c}=e.getRadius(l);return i?a.radius-o*(l.index+1):he(c)?c:0},outer(l){const{outerRadius:c}=e.getRadius(l);let f;if(i)f=a.radius-o*l.index+s;else if(e.hasType("polar")&&!t)f=e.getPolarOuterRadius(l,c);else if(f=c,t){let{radiusExpanded:g}=a;a.radius!==c&&(g-=Math.abs(a.radius-c)),f=g*t}return f},corner(l,c){const{arc_cornerRadius_ratio:f=0,arc_cornerRadius:g=0}=n,{data:{id:v},value:m}=l;let S=0;return f?S=f*c:S=he(g)?g:g.call(e.api,v,m,c),S}}}function bs(t){return function(e){const n=({startAngle:i=0,endAngle:o=0,padAngle:s=0})=>({startAngle:i,endAngle:o,padAngle:s}),a=Qr(n(this._current),n(e));return this._current=e,function(i){const o=a(i),{data:s,index:l,value:c}=e;return t(vx(gx({},o),{data:s,index:l,value:c}))}}}var px={initPie(){const t=this,{config:e}=t,n=e.data_type,a=e[`${n}_padding`],i=e[`${n}_startingAngle`]||0,o=(a?a*.01:e[`${n}_padAngle`])||0;t.pie=qy().startAngle(i).endAngle(i+2*Math.PI).padAngle(o).value(s=>{var l,c;return(c=(l=s.values)==null?void 0:l.reduce((f,g)=>f+g.value,0))!=null?c:s}).sort(t.getSortCompareFn.bind(t)(!0))},updateRadius(){const t=this,{config:e,state:n}=t,a=e.data_type,i=e[`${a}_padding`],o=e.gauge_width||e.donut_width,s=t.filterTargetsToShow(t.data.targets).length*e.gauge_arcs_minWidth;n.radiusExpanded=Math.min(n.arcWidth,n.arcHeight)/2*(t.hasMultiArcGauge()&&e.gauge_label_show?.85:1),n.radius=n.radiusExpanded*.95,n.innerRadiusRatio=o?(n.radius-o)/n.radius:.6,n.gaugeArcWidth=o||(s<=n.radius-n.innerRadius?n.radius-n.innerRadius:s<=n.radius?s:n.radius);const l=e.pie_innerRadius||(i?i*(n.innerRadiusRatio+.1):0);n.outerRadius=e.pie_outerRadius,n.innerRadius=t.hasType("donut")||t.hasType("gauge")?n.radius*n.innerRadiusRatio:l},getRadius(t){const e=this,n=t==null?void 0:t.data;let{innerRadius:a,outerRadius:i}=e.state;return!he(a)&&n&&(a=a[n.id]||0),Be(i)&&n&&n.id in i?i=i[n.id]:he(i)||(i=e.state.radius),{innerRadius:a,outerRadius:i}},updateArc(){const t=this;t.updateRadius(),t.svgArc=t.getSvgArc(),t.svgArcExpanded=t.getSvgArcExpanded()},getArcLength(){const t=this,{config:e}=t,n=e.gauge_arcLength*3.6;let a=2*(n/360);return n<-360?a=-2:n>360&&(a=2),a*Math.PI},getStartingAngle(){const t=this,{config:e}=t,n=e.data_type,a=t.hasType("gauge")?e.gauge_fullCircle:!1,i=-1*Math.PI/2,o=Math.PI/2;let s=e[`${n}_startingAngle`]||0;return!a&&s<=i?s=i:!a&&s>=o?s=o:(s>Math.PI||s<-1*Math.PI)&&(s=Math.PI),s},updateAngle(t,e=!1){var n;const a=this,{config:i,state:o}=a,s=e&&a.hasType("gauge");let{pie:l}=a,c=t,f=!1;if(!i)return null;const g=a.getStartingAngle(),v=i.gauge_fullCircle||e&&!s?a.getArcLength():g*-2;if(c.data&&a.isGaugeType(c.data)&&!a.hasMultiArcGauge()){const{gauge_min:m,gauge_max:S}=i,P=a.getTotalDataSum(o.rendered),N=v*((P-m)/(S-m));l=l.startAngle(g).endAngle(N+g)}if(e===!1&&l(a.filterTargetsToShow()).forEach((m,S)=>{var P;!f&&m.data.id===((P=c.data)==null?void 0:P.id)&&(f=!0,c=m,c.index=S)}),isNaN(c.startAngle)&&(c.startAngle=0),isNaN(c.endAngle)&&(c.endAngle=c.startAngle),e||c.data&&(i.gauge_enforceMinMax||a.hasMultiArcGauge())){const{gauge_min:m,gauge_max:S}=i,P=e&&!s?a.getTotalDataSum(o.rendered):S,N=v/(P-m),L=(n=c.value)!=null?n:0,w=L{const l=e.updateAngle(s),c=a(l);let f=0;return l&&(f=i(l,c)),l?o.cornerRadius(f)(l):"M 0 0"}},getArc(t,e,n){return n||this.isArcType(t.data)?this.svgArc(t,e):"M 0 0"},redrawArcRangeText(){const t=this,{config:e,$el:{arcs:n},state:a,$T:i}=t,o=e.arc_rangeText_format,s=t.hasType("gauge")&&e.arc_rangeText_fixed;let l=e.arc_rangeText_values;if(l!=null&&l.length){const c=e.arc_rangeText_unit==="%",f=t.getTotalDataSum(a.rendered);c&&(l=l.map(m=>f/100*m));const g=t.pie(l).map((m,S)=>(m.index=S,m));let v=n.selectAll(`.${Ve.arcRange}`).data(l);v.exit(),v=i(v.enter().append("text").attr("class",Ve.arcRange).style("text-anchor","middle").style("pointer-events","none").style("opacity","0").text(m=>{const S=c?m/f*100:m;return ve(o)?o(S):`${S}${c?"%":""}`}).merge(v)),(!a.rendered||a.rendered&&!s)&&f>0&&v.attr("transform",(m,S)=>t.transformForArcLabel(g[S],!0)),v.style("opacity",m=>!s&&(m>f||f===0)?"0":null)}},transformForArcLabel(t,e=!1){var n,a,i;const o=this,{config:s,state:{radiusExpanded:l}}=o,c=o.updateAngle(t,e);let f="";if(c){if(e||o.hasMultiArcGauge()){const g=Math.sin(c.endAngle-Math.PI/2),v=s.arc_rangeText_position;let m=Math.cos(c.endAngle-Math.PI/2)*(l+(e?5:25)),S=g*(l+15-Math.abs(g*10))+3;if(e&&v){const P=s.arc_rangeText_values,N=ve(v)?v(P[t.index]):v;m+=(n=N==null?void 0:N.x)!=null?n:0,S+=(a=N==null?void 0:N.y)!=null?a:0}f=`translate(${m},${S})`}else if(!o.hasType("gauge")||o.data.targets.length>1){let{outerRadius:g}=o.getRadius(t);o.hasType("polar")&&(g=o.getPolarOuterRadius(t,g));const v=this.svgArc.centroid(c),[m,S]=v.map(L=>isNaN(L)?0:L),P=Math.sqrt(m*m+S*S);let N=(i=["donut","gauge","pie","polar"].filter(o.hasType.bind(o)).map(L=>s[`${L}_label_ratio`]))==null?void 0:i[0];N?N=ve(N)?N.bind(o.api)(t,g,P):N:N=g&&(P?(36/g>.375?1.175-36/g:.8)*g/P:0),f=`translate(${m*N},${S*N})`}}return f},convertToArcData(t){return this.addName({id:"data"in t?t.data.id:t.id,value:t.value,ratio:this.getRatio("arc",t),index:t.index})},textForArcLabel(t){const e=this,n=e.hasType("gauge");e.shouldShowArcLabel()&&t.style("fill",e.updateTextColor.bind(e)).attr("filter",a=>e.updateTextBGColor.bind(e)(a,e.config.data_labels_backgroundColors)).each(function(a){var i;const o=ot(this),s=e.updateAngle(a),l=e.getRatio("arc",s);if(e.meetsLabelThreshold(l,(i=["donut","gauge","pie","polar"].filter(e.hasType.bind(e)))==null?void 0:i[0])){const{value:f}=s||a,g=(e.getArcLabelFormat()||e.defaultArcValueFormat)(f,l,a.data.id).toString();wa(o,g,[-1,1],n)}else o.text("")})},expandArc(t){const e=this,{state:{transiting:n},$el:a}=e;if(n){const o=setInterval(()=>{n||(clearInterval(o),a.legend.selectAll(`.${qe.legendItemFocused}`).size()>0&&e.expandArc(t))},10);return}const i=e.mapToTargetIds(t);a.svg.selectAll(e.selectorTargets(i,`.${Ve.chartArc}`)).each(function(o){if(!e.shouldExpand(o.data.id))return;const s=e.getExpandConfig(o.data.id,"duration"),l=e.getSvgArcExpanded(e.getExpandConfig(o.data.id,"rate"));ot(this).selectAll("path").transition().duration(s).attrTween("d",bs(e.svgArcExpanded.bind(e))).transition().duration(s*2).attrTween("d",bs(l.bind(e)))})},unexpandArc(t){const e=this,{state:{transiting:n},$el:{svg:a}}=e;if(n)return;const i=e.mapToTargetIds(t);a.selectAll(e.selectorTargets(i,`.${Ve.chartArc}`)).selectAll("path").transition().duration(o=>e.getExpandConfig(o.data.id,"duration")).attrTween("d",bs(e.svgArc.bind(e))),a.selectAll(`${Ve.arc}`).style("opacity",null)},getExpandConfig(t,e){const n=this,{config:a}=n,i={duration:50,rate:.98};let o;return n.isDonutType(t)?o="donut":n.isGaugeType(t)?o="gauge":n.isPieType(t)&&(o="pie"),o?a[`${o}_expand_${e}`]:i[e]},shouldExpand(t){const e=this,{config:n}=e;return e.isDonutType(t)&&n.donut_expand||e.isGaugeType(t)&&n.gauge_expand||e.isPieType(t)&&n.pie_expand},shouldShowArcLabel(){const t=this,{config:e}=t;return["donut","gauge","pie","polar"].some(n=>t.hasType(n)&&e[`${n}_label_show`])},getArcLabelFormat(){const t=this,{config:e}=t;let n=a=>a;return["donut","gauge","pie","polar"].filter(t.hasType.bind(t)).forEach(a=>{n=e[`${a}_label_format`]}),ve(n)?n.bind(t.api):n},updateTargetsForArc(t){const e=this,{$el:n}=e,a=e.hasType("gauge"),i=e.getChartClass("Arc"),o=e.getClass("arcs",!0),s=e.classFocus.bind(e),l=n.main.select(`.${Ve.chartArcs}`),c=l.selectAll(`.${Ve.chartArc}`).data(e.pie(t)).attr("class",g=>i(g)+s(g.data)),f=c.enter().append("g").attr("class",i).call(this.setCssRule(!1,`.${Ve.chartArcs} text`,["pointer-events:none","text-anchor:middle"]));f.append("g").attr("class",o).merge(c),f.append("text").attr("dy",a&&!e.hasMultiTargets()?"-.1em":".35em").style("opacity","0").style("text-anchor",e.getStylePropValue("middle")).style("pointer-events",e.getStylePropValue("none")),n.text=l.selectAll(`.${Se.target} text`)},initArc(){const t=this,{$el:e}=t;e.arcs=e.main.select(`.${Se.chart}`).append("g").attr("class",Ve.chartArcs).attr("transform",t.getTranslate("arc")),t.setArcTitle()},setArcTitle(t){const e=this,n=t||e.getArcTitle(),a=e.hasType("gauge");if(n){const i=a?Un.chartArcsGaugeTitle:Ve.chartArcsTitle;let o=e.$el.arcs.select(`.${i}`);o.empty()&&(o=e.$el.arcs.append("text").attr("class",i).style("text-anchor","middle")),a&&o.attr("dy","-0.3em"),wa(o,n,a?void 0:[-.6,1.35],!0)}},getArcTitle(){const t=this,e=t.hasType("donut")&&"donut"||t.hasType("gauge")&&"gauge";return e?t.config[`${e}_title`]:""},getArcTitleWithNeedleValue(){const t=this,{config:e,state:n}=t,a=t.getArcTitle();if(a&&t.config.arc_needle_show&&/{=[A-Z_]+}/.test(a)){let i=n.current.needle;return he(i)||(i=e.arc_needle_value),bi(a,{NEEDLE_VALUE:he(i)?i:0})}return!1},redrawArc(t,e,n){const a=this,{config:i,state:o,$el:{main:s}}=a,l=i.interaction_enabled,c=l&&i.data_selection_isselectable;let f=s.selectAll(`.${Ve.arcs}`).selectAll(`.${Ve.arc}`).data(a.arcData.bind(a));f.exit().transition().duration(e).style("opacity","0").remove(),f=f.enter().append("path").attr("class",a.getClass("arc",!0)).style("fill",g=>a.color(g.data)).style("cursor",g=>{var v;return(v=c==null?void 0:c.bind)!=null&&v.call(c,a.api)(g)?"pointer":null}).style("opacity","0").each(function(g){a.isGaugeType(g.data)&&(g.startAngle=i.gauge_startingAngle,g.endAngle=i.gauge_startingAngle),this._current=g}).merge(f),a.hasType("gauge")&&(a.updateGaugeMax(),a.hasMultiArcGauge()&&a.redrawArcGaugeLine()),f.attr("transform",g=>!a.isGaugeType(g.data)&&n?"scale(0)":"").style("opacity",function(g){return g===this._current?"0":null}).each(()=>{o.transiting=!0}).transition().duration(t).attrTween("d",function(g){const v=a.updateAngle(g);if(!v)return()=>"M 0 0";isNaN(this._current.startAngle)&&(this._current.startAngle=0),isNaN(this._current.endAngle)&&(this._current.endAngle=this._current.startAngle);const m=Qr(this._current,v);return this._current=m(0),function(S){const P=m(S);return P.data=g.data,a.getArc(P,!0)}}).attr("transform",n?"scale(1)":"").style("fill",g=>{let v;return a.levelColor?(v=a.levelColor(g.data.values[0].value),i.data_colors[g.data.id]=v):v=a.color(g.data),v}).style("opacity",null).call(Si,function(){if(a.levelColor){const g=ot(this),v=g.datum(this._current);a.updateLegendItemColor(v.data.id,g.style("fill"))}o.transiting=!1,_e(i.onrendered,a.api)}),l&&a.bindArcEvent(f),a.hasType("polar")&&a.redrawPolar(),a.hasType("gauge")&&a.redrawBackgroundArcs(),i.arc_needle_show&&a.redrawNeedle(),a.redrawArcText(t),a.redrawArcRangeText()},redrawNeedle(){const t=this,{$el:e,config:n,state:{hiddenTargetIds:a,radius:i}}=t,o=(i-1)/100*n.arc_needle_length,s=a.length!==t.data.targets.length;let l=t.$el.arcs.select(`.${Ve.needle}`);const c=n.arc_needle_path,f=n.arc_needle_bottom_width/2,g=n.arc_needle_top_width/2,v=n.arc_needle_top_rx,m=n.arc_needle_top_ry,S=n.arc_needle_bottom_len,P=n.arc_needle_bottom_rx,N=n.arc_needle_bottom_ry,L=t.getNeedleAngle(),w=()=>{const X=t.getArcTitleWithNeedleValue();X&&t.setArcTitle(X)};if(w(),l.empty()&&(l=e.arcs.append("path").classed(Ve.needle,!0),e.needle=l,e.needle.updateHelper=(X,W=!1)=>{e.needle.style("display")!=="none"&&t.$T(e.needle).style("transform",`rotate(${t.getNeedleAngle(X)}deg)`).call(Si,()=>{W&&(n.arc_needle_value=X),w()})}),s){const X=ve(c)?c.call(t,o):`M-${f} ${S} A${P} ${N} 0 0 0 ${f} ${S} L${g} -${o} A${v} ${m} 0 0 0 -${g} -${o} L-${f} ${S} Z`;t.$T(l).attr("d",X).style("fill",n.arc_needle_color).style("display",null).style("transform",`rotate(${L}deg)`)}else l.style("display","none")},getNeedleAngle(t){const e=this,{config:n,state:a}=e,i=e.getArcLength(),o=e.hasType("gauge"),s=e.getTotalDataSum(!0);let l=Qe(t)?t:n.arc_needle_value,c=n[`${n.data_type}_startingAngle`]||0,f=0;if(he(l)||(l=o&&e.data.targets.length===1?s:0),a.current.needle=l,o){c=e.getStartingAngle();const g=n.gauge_fullCircle?i:c*-2,{gauge_min:v,gauge_max:m}=n;f=g*((l-v)/(m-v))}else f=i*(l/s);return(c+f)*(180/Math.PI)},redrawBackgroundArcs(){const t=this,{config:e,state:n}=t,a=t.hasMultiArcGauge(),i=e.gauge_fullCircle,o=t.filterTargetsToShow(t.data.targets).length===0&&!!e.data_empty_label_text,s=t.getStartingAngle(),l=i?s+t.getArcLength():s*-1;let c=t.$el.arcs.select(`${a?"g":""}.${Ve.chartArcsBackground}`);if(a){let f=0;c=c.selectAll(`path.${Ve.chartArcsBackground}`).data(t.data.targets),c.enter().append("path").attr("class",(g,v)=>`${Ve.chartArcsBackground} ${Ve.chartArcsBackground}-${v}`).merge(c).style("fill",e.gauge_background||null).attr("d",({id:g})=>{if(o||n.hiddenTargetIds.indexOf(g)>=0)return"M 0 0";const v={data:[{value:e.gauge_max}],startAngle:s,endAngle:l,index:f++};return t.getArc(v,!0,!0)}),c.exit().remove()}else c.attr("d",o?"M 0 0":()=>{const f={data:[{value:e.gauge_max}],startAngle:s,endAngle:l};return t.getArc(f,!0,!0)})},bindArcEvent(t){const e=this,{config:n,state:a}=e,i=a.inputType==="touch",o=a.inputType==="mouse";function s(c,f,g){e.expandArc(g),e.api.focus(g),e.toggleFocusLegend(g,!0),e.showTooltip([f],c)}function l(c){const f=(c==null?void 0:c.id)||void 0;e.unexpandArc(f),e.api.revert(),e.revertLegend(),e.hideTooltip()}if(t.on("click",function(c,f,g){var v;const m=e.updateAngle(f);let S;m&&(S=e.convertToArcData(m),(v=e.toggleShape)==null||v.call(e,this,S,g),n.data_onclick.bind(e.api)(S,this))}),o&&t.on("mouseover",function(c,f){if(a.transiting)return;a.event=c;const g=e.updateAngle(f),v=g?e.convertToArcData(g):null,m=(v==null?void 0:v.id)||void 0;s(this,v,m),e.setOverOut(!0,v)}).on("mouseout",(c,f)=>{if(a.transiting||!n.interaction_onout)return;a.event=c;const g=e.updateAngle(f),v=g?e.convertToArcData(g):null;l(),e.setOverOut(!1,v)}).on("mousemove",function(c,f){const g=e.updateAngle(f),v=g?e.convertToArcData(g):null;a.event=c,e.showTooltip([v],this)}),i&&e.hasArcType()&&!e.radars){const c=f=>{var g,v;const{clientX:m,clientY:S}=(v=(g=f.changedTouches)==null?void 0:g[0])!=null?v:{clientX:0,clientY:0};return ot(gn.elementFromPoint(m,S))};e.$el.svg.on("touchstart touchmove",function(f){if(a.transiting)return;a.event=f;const v=c(f).datum(),m=v!=null&&v.data&&v.data.id?e.updateAngle(v):null,S=m?e.convertToArcData(m):null,P=(S==null?void 0:S.id)||void 0;e.callOverOutForTouch(S),ln(P)?l():s(this,S,P)})}},redrawArcText(t){const e=this,{config:n,state:a,$el:{main:i,arcs:o}}=e,s=e.hasType("gauge"),l=e.hasMultiArcGauge();let c;if(s&&e.data.targets.length===1&&n.gauge_title||(c=i.selectAll(`.${Ve.chartArc}`).select("text").style("opacity","0").attr("class",f=>e.isGaugeType(f.data)?Un.gaugeValue:null).call(e.textForArcLabel.bind(e)).attr("transform",f=>e.transformForArcLabel.bind(e)(f)).style("font-size",f=>e.isGaugeType(f.data)&&e.data.targets.length===1&&!l?`${Math.round(a.radius/5)}px`:null).transition().duration(t).style("opacity",f=>e.isTargetToShow(f.data.id)&&e.isArcType(f.data)?null:"0"),l&&c.attr("dy","-.1em")),i.select(`.${Ve.chartArcsTitle}`).style("opacity",e.hasType("donut")||s?null:"0"),s){const f=n.gauge_fullCircle;f&&(c==null||c.attr("dy",`${l?0:Math.round(a.radius/14)}`)),n.gauge_label_show&&(o.select(`.${Un.chartArcsGaugeUnit}`).attr("dy",`${f?1.5:.75}em`).text(n.gauge_units),o.select(`.${Un.chartArcsGaugeMin}`).attr("dx",`${-1*(a.innerRadius+(a.radius-a.innerRadius)/(f?1:2))}px`).attr("dy","1.2em").text(e.textForGaugeMinMax(n.gauge_min,!1)),!f&&o.select(`.${Un.chartArcsGaugeMax}`).attr("dx",`${a.innerRadius+(a.radius-a.innerRadius)/2}px`).attr("dy","1.2em").text(e.textForGaugeMinMax(n.gauge_max,!0)))}},getArcElementByIdOrIndex(t){const e=this,{$el:{arcs:n}}=e,a=he(t)?i=>i.index===t:i=>i.data.id===t;return n==null?void 0:n.selectAll(`.${Se.target} path`).filter(a)}};function ju(t){return t[0]}function Vu(t){return t[1]}function Gu(t,e){var n=Le(!0),a=null,i=gs,o=null,s=Es(l);t=typeof t=="function"?t:t===void 0?ju:Le(t),e=typeof e=="function"?e:e===void 0?Vu:Le(e);function l(c){var f,g=(c=$s(c)).length,v,m=!1,S;for(a==null&&(o=i(S=s())),f=0;f<=g;++f)!(f=S;--P)l.point(W[P],H[P]);l.lineEnd(),l.areaEnd()}w&&(W[m]=+t(L,m,v),H[m]=+e(L,m,v),l.point(a?+a(L,m,v):W[m],n?+n(L,m,v):H[m]))}if(X)return l=null,X+""||null}function g(){return Gu().defined(i).curve(s).context(o)}return f.x=function(v){return arguments.length?(t=typeof v=="function"?v:Le(+v),a=null,f):t},f.x0=function(v){return arguments.length?(t=typeof v=="function"?v:Le(+v),f):t},f.x1=function(v){return arguments.length?(a=v==null?null:typeof v=="function"?v:Le(+v),f):a},f.y=function(v){return arguments.length?(e=typeof v=="function"?v:Le(+v),n=null,f):e},f.y0=function(v){return arguments.length?(e=typeof v=="function"?v:Le(+v),f):e},f.y1=function(v){return arguments.length?(n=v==null?null:typeof v=="function"?v:Le(+v),f):n},f.lineX0=f.lineY0=function(){return g().x(t).y(e)},f.lineY1=function(){return g().x(t).y(n)},f.lineX1=function(){return g().x(a).y(e)},f.defined=function(v){return arguments.length?(i=typeof v=="function"?v:Le(!!v),f):i},f.curve=function(v){return arguments.length?(s=v,o!=null&&(l=s(o)),f):s},f.context=function(v){return arguments.length?(v==null?o=l=null:l=s(o=v),f):o},f}var sa={initArea(t){const e=this,{config:n}=e;t.insert("g",`.${n.area_front?$n.circles:ur.lines}`).attr("class",e.getClass("areas",!0))},updateAreaColor(t){const e=this;return e.config.area_linearGradient?e.getGradienColortUrl(t.id):e.color(t)},updateArea(t,e=!1){const n=this,{config:a,state:i,$el:o,$T:s}=n,l=e?o.subchart:o;a.area_linearGradient&&n.updateLinearGradient();const c=l.main.selectAll(`.${ti.areas}`).selectAll(`.${ti.area}`).data(n.lineData.bind(n));s(c.exit(),t).style("opacity","0").remove(),l.area=c.enter().append("path").attr("class",n.getClass("area",!0)).style("fill",n.updateAreaColor.bind(n)).style("opacity",function(){return i.orgAreaOpacity=ot(this).style("opacity"),"0"}).merge(c),c.style("opacity",i.orgAreaOpacity),n.setRatioForGroupedData(l.area.data())},redrawArea(t,e,n=!1){const a=this,{area:i}=n?this.$el.subchart:this.$el,{orgAreaOpacity:o}=a.state;return[a.$T(i,e,gr()).attr("d",t).style("fill",a.updateAreaColor.bind(a)).style("opacity",s=>String(a.isAreaRangeType(s)?o/1.75:o))]},generateDrawArea(t,e){const n=this,{config:a}=n,i=a.line_connectNull,o=a.axis_rotated,s=n.generateGetAreaPoints(t,e),l=n.getYScaleById.bind(n),c=v=>(e?n.subxx:n.xx).call(n,v),f=(v,m)=>n.isGrouped(v.id)?s(v,m)[0][1]:l(v.id,e)(n.isAreaRangeType(v)?n.getRangedData(v,"high"):n.getShapeYMin(v.id)),g=(v,m)=>n.isGrouped(v.id)?s(v,m)[1][1]:l(v.id,e)(n.isAreaRangeType(v)?n.getRangedData(v,"low"):v.value);return v=>{let m=i?n.filterRemoveNull(v.values):v.values,S=0,P=0,N;if(n.isAreaType(v)){let L=mx();L=o?L.y(c).x0(f).x1(g):L.x(c).y0(a.area_above?0:a.area_below?n.state.height:f).y1(g),i||(L=L.defined(w=>n.getBaseValue(w)!==null)),n.isStepType(v)&&(m=n.convertValuesToStep(m)),N=L.curve(n.getCurve(v))(m)}else m[0]&&(S=n.scale.x(m[0].x),P=n.getYScaleById(v.id)(m[0].value)),N=o?`M ${P} ${S}`:`M ${S} ${P}`;return N||"M 0 0"}},generateGetAreaPoints(t,e){const n=this,{config:a}=n,i=n.getShapeX(0,t,e),o=n.getShapeY(!!e),s=n.getShapeOffset(n.isAreaType,t,e),l=n.getYScaleById.bind(n);return function(c,f){const g=l.call(n,c.id,e)(n.getShapeYMin(c.id)),v=s(c,f)||g,m=i(c),S=c.value;let P=o(c);return a.axis_rotated&&(S>0&&Pg.values.some(v=>he(v.value)||e.isBarRangeType(v)))).attr("class",g=>i(g)+s(g)).enter().append("g").attr("class",i).style("opacity","0").style("pointer-events",e.getStylePropValue("none")).append("g").attr("class",o).style("cursor",g=>{var v;return(v=l==null?void 0:l.bind)!=null&&v.call(l,e.api)(g)?"pointer":null}).call(e.setCssRule(!0,` .${Kn.bar}`,["fill"],e.color))},updateBar(t,e=!1){const n=this,{config:a,$el:i,$T:o}=n,s=e?i.subchart:i,l=n.getClass("bar",!0),c=n.initialOpacity.bind(n);a.bar_linearGradient&&n.updateLinearGradient();const f=s.main.selectAll(`.${Kn.bars}`).selectAll(`.${Kn.bar}`).data(n.labelishData.bind(n));o(f.exit(),t).style("opacity","0").remove(),s.bar=f.enter().append("path").attr("class",l).style("fill",n.updateBarColor.bind(n)).merge(f).style("opacity",c),n.setRatioForGroupedData(s.bar.data())},updateBarColor(t){const e=this,n=e.getStylePropValue(e.color);return e.config.bar_linearGradient?e.getGradienColortUrl(t.id):n?n(t):null},redrawBar(t,e,n=!1){const a=this,{bar:i}=n?a.$el.subchart:a.$el;return[a.$T(i,e,gr()).attr("d",o=>(he(o.value)||a.isBarRangeType(o))&&t(o)).style("fill",a.updateBarColor.bind(a)).style("clip-path",o=>o.clipPath).style("opacity",null)]},generateDrawBar(t,e){const n=this,{config:a}=n,i=n.generateGetBarPoints(t,e),o=a.axis_rotated,s=a.bar_radius,l=a.bar_radius_ratio,c=he(s)&&s>0?()=>s:he(l)?f=>f*l:null;return(f,g)=>{const v=i(f,g),m=+o,S=+!m,P=f.value<0,N=a[`axis_${n.axis.getId(f.id)}_inverted`],L=!N&&P||N&&!P,w=["",""],X=n.isGrouped(f.id),W=c&&X?n.isStackingRadiusData(f):!1,H=[v[0][m],v[0][S]];let k=0;if(f.clipPath=null,c){const ht=o?S:m,$t=v[2][ht]-v[0][ht];k=!X||W?c($t):0;const dt=`a${k} ${k} ${L?"1 0 0":"0 0 1"} `;w[+!o]=`${dt}${k},${k}`,w[+o]=`${dt}${[-k,k][o?"sort":"reverse"]()}`,L&&w.reverse()}const K=o?v[1][m]+(L?k:-k):v[1][S]+(L?-k:k);if(k){let ht="";o?L&&H[0]K&&(ht=`0 0 0 ${H[0]-K}px`):L&&H[1]>K?ht=`${H[1]-K}px 0 0 0`:!L&&H[1]-1){const m=n.bar.filter(S=>S.id===s&&S.value===c);return!m.empty()&&/a\d+/i.test(m.attr("d"))}const f=a.data_groups.find(m=>m.indexOf(s)>-1),v=e.orderTargets(e.filterTargetsToShow(i.targets.filter(e.isBarType,e))).filter(m=>f.indexOf(m.id)>-1).map(m=>m.values.filter(S=>S.index===l&&(he(c)&&c>0?S.value>0:S.value<0))[0]).filter(Boolean).map(m=>m.id);return c!==0&&v.indexOf(s)===v.length-1},generateGetBarPoints(t,e){const n=this,{config:a}=n,i=e?n.axis.subX:n.axis.x,o=n.getIndicesMax(t)+1,s=n.getBarW("bar",i,o),l=n.getShapeX(s,t,!!e),c=n.getShapeY(!!e),f=n.getShapeOffset(n.isBarType,t,!!e),g=n.getYScaleById.bind(n);return(v,m)=>{const{id:S}=v,P=g.call(n,S,e)(n.getShapeYMin(S)),N=f(v,m)||P,L=he(s)?s:s[v.id]||s._$width,w=a[`axis_${n.axis.getId(S)}_inverted`],X=v.value,W=l(v);let H=c(v);a.axis_rotated&&!w&&(X>0&&He.isBubbleZType(s)?e.getBubbleZData(s.value,"y"):Be(s.value)?s.value.mid:s.value)),i=n*n*Math.PI,o=(e.isBubbleZType(t)?e.getBubbleZData(t.value,"z"):t.value)*(i/a);return Math.sqrt(o/Math.PI)},getBubbleZData(t,e){return Be(t)?t[e]:t[e==="y"?0:1]}},Tx=Object.defineProperty,Xu=Object.getOwnPropertySymbols,$x=Object.prototype.hasOwnProperty,Sx=Object.prototype.propertyIsEnumerable,Hu=(t,e,n)=>e in t?Tx(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,Ax=(t,e)=>{for(var n in e||(e={}))$x.call(e,n)&&Hu(t,n,e[n]);if(Xu)for(var n of Xu(e))Sx.call(e,n)&&Hu(t,n,e[n]);return t},Ex={initCandlestick(){const{$el:t}=this;t.candlestick=t.main.select(`.${Se.chart}`).append("g").attr("class",cr.chartCandlesticks)},updateTargetsForCandlestick(t){const e=this,{$el:n}=e,a=e.getChartClass("Candlestick");n.candlestick||e.initCandlestick(),e.$el.main.select(`.${cr.chartCandlesticks}`).selectAll(`.${cr.chartCandlestick}`).data(t).enter().append("g").attr("class",a).style("pointer-events","none")},updateCandlestick(t,e=!1){const n=this,{$el:a,$T:i}=n,o=e?a.subchart:a,s=n.getClass("candlestick",!0),l=n.initialOpacity.bind(n),c=o.main.selectAll(`.${cr.chartCandlestick}`).selectAll(`.${cr.candlestick}`).data(n.labelishData.bind(n));i(c.exit(),t).style("opacity","0").remove();const f=c.enter().filter(g=>g.value).append("g").attr("class",s);f.append("line"),f.append("path"),o.candlestick=c.merge(f).style("opacity",l)},generateDrawCandlestick(t,e){const n=this,{config:a}=n,i=n.generateGetCandlestickPoints(t,e),o=a.axis_rotated,s=a.candlestick_color_down;return(l,c,f)=>{const g=i(l,c),v=n.getCandlestickData(l),m=v==null?void 0:v._isUp,S=+o,P=+!S;f.classed&&f.classed(cr[m?"valueUp":"valueDown"],!0);const N=o?`H${g[1][1]} V${g[1][0]} H${g[0][1]}`:`V${g[1][1]} H${g[1][0]} V${g[0][1]}`;f.select("path").attr("d",`M${g[0][S]},${g[0][P]}${N}z`).style("fill",X=>(m?n.color(X):Be(s)?s[X.id]:s)||n.color(X));const L=f.select("line"),w=o?{x1:g[2][1],x2:g[2][2],y1:g[2][0],y2:g[2][0]}:{x1:g[2][0],x2:g[2][0],y1:g[2][1],y2:g[2][2]};for(const X in w)L.attr(X,w[X])}},generateGetCandlestickPoints(t,e=!1){const n=this,a=e?n.axis.subX:n.axis.x,i=n.getIndicesMax(t)+1,o=n.getBarW("candlestick",a,i),s=n.getShapeX(o,t,!!e),l=n.getShapeY(!!e),c=n.getShapeOffset(n.isBarType,t,!!e),f=n.getYScaleById.bind(n);return(g,v)=>{const m=f.call(n,g.id,e)(n.getShapeYMin(g.id)),S=c(g,v)||m,P=he(o)?o:o[g.id]||o._$width,N=n.getCandlestickData(g);let L;if(N&&he(N.open)&&he(N.close)){const w={start:s(g),end:0};w.end=w.start+P;const X={start:l(N.open),end:l(N.close)},W={x:w.start+P/2,high:l(N.high),low:l(N.low)};X.start-=m-S,L=[[w.start,X.start],[w.end,X.end],[W.x,W.low,W.high]]}else L=[[0,0],[0,0],[0,0,0]];return L}},redrawCandlestick(t,e,n=!1){const a=this,{$el:i,$T:o}=a,{candlestick:s}=n?i.subchart:i,l=gr(!0);return[s.each(function(c,f){const g=o(ot(this),e,l);t(c,f,g)}).style("opacity",null)]},getCandlestickData({value:t}){let e;if(je(t)){const[n,a,i,o,s=!1]=t;e={open:n,high:a,low:i,close:o},s!==!1&&(e.volume=s)}else Be(t)&&(e=Ax({},t));return e&&(e._isUp=e.close>=e.open),e||null}},bx=Object.defineProperty,Yu=Object.getOwnPropertySymbols,Rx=Object.prototype.hasOwnProperty,Ix=Object.prototype.propertyIsEnumerable,Wu=(t,e,n)=>e in t?bx(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,Ox=(t,e)=>{for(var n in e||(e={}))Rx.call(e,n)&&Wu(t,n,e[n]);if(Yu)for(var n of Yu(e))Ix.call(e,n)&&Wu(t,n,e[n]);return t};function Zi(t=!1){const e=this,{config:n,state:{current:{width:a,height:i}}}=e,o=e.getCurrentPadding(),s=Ox({width:a-(o.left+o.right),height:i-(n.legend_show?e.getLegendHeight()+10:0)-(o.top+o.bottom)},o);if(t){const{width:l,height:c}=Ku.call(e,{width:s.width,height:s.height});s.width{let l=o;return Be(o)&&(l=t[s?"height":"width"]*o.ratio),l}),{width:a,height:i}}function Cx(t){const e=this,{top:n,left:a,width:i}=Zi.call(e,!0),o=[];return t.forEach((s,l)=>{const{ratio:c}=s,f=l>0?o[l-1][2][1]:n;o.push(s.coords=[[a,f],[a+i,f],[a+i,l>0?c+f:c+n],[a,l>0?c+f:c+n],[a,f]])}),o}function Zu(t=!1){const e=this,{width:n,height:a,top:i,left:o}=Zi.call(e,!0),s=Ku.call(e,{width:n,height:a}),l=(n-s.width)/2,c=(n+s.width)/2,f=a-s.height,g=[[0,0],[n,0],[c,f],[c,a],[l,a],[l,f],[0,0]];return t&&g.forEach(v=>{v[0]+=o,v[1]+=i}),`M${g.join("L")}z`}function Px(t){const e=this,{config:n}=e,a=t.map(i=>({id:i.id,value:i.values.reduce((o,s)=>o+s.value,0)}));return n.data_order&&a.sort(e.getSortCompareFn.bind(e)(!0)),Ju.call(e,a)}function Ju(t){const e=this,{height:n}=Zi.call(e),a=e.getTotalDataSum(!0);return t.forEach(i=>{i.ratio=i.value/a*n}),t}var wx={initFunnel(){const t=this,{$el:e}=t;e.funnel=e.main.select(`.${Se.chart}`).append("g").classed(Ta.chartFunnels,!0),e.funnel.background=e.funnel.append("path").classed(Ta.funnelBackground,!0),t.bindFunnelEvent()},bindFunnelEvent(){const t=this,{$el:{funnel:e},config:n,state:a}=t,i=o=>{var s;const l=o.isTrusted?o.target:(s=a.eventReceiver.rect)==null?void 0:s.node();let c;return/^path$/i.test(l.tagName)&&(a.event=o,c=ot(l).datum()),c};if(n.interaction_enabled){const o=a.inputType==="touch";e.on(o?"touchstart":"mouseover mousemove",s=>{const l=i(s);l&&(t.showTooltip([l],s.target),/^(touchstart|mouseover)$/.test(s.type)&&t.setOverOut(!0,l))}).on(o?"touchend":"mouseout",s=>{const l=i(s);n.interaction_onout&&(t.hideTooltip(),t.setOverOut(!1,l))})}},updateTargetsForFunnel(t){const e=this,{$el:{funnel:n}}=e,a=e.getChartClass("Funnel"),i=e.getClass("funnel",!0);n||e.initFunnel();const o=Px.call(e,t.filter(e.isFunnelType.bind(e))),s=n.selectAll(`.${Ta.chartFunnel}`).data(o);s.exit().remove();const l=s.enter().insert("g",`.${Ta.funnelBackground}`);l.append("path"),n.path=l.merge(s).attr("class",c=>a(c)).select("path").attr("class",i).style("opacity","0").style("fill",e.color)},updateFunnel(t){const e=this,{$el:{funnel:n}}=e,a=t.map(({id:i})=>i);n.path=n.path.filter(i=>a.indexOf(i.id)>=0)},generateGetFunnelPoints(){const t=this,{$el:{funnel:e}}=t,n=t.filterTargetsToShow(e.path),{top:a,left:i,right:o}=Zi.call(t),s=(i-o)/2,l={};let c=a!=null?a:0;return n.each((f,g)=>{var v;l[f.id]=[[s,c],[s,c+=((v=n==null?void 0:n[g])!=null?v:f).ratio]]}),f=>l[f.id]},redrawFunnel(){const t=this,{$T:e,$el:{funnel:n}}=t,a=t.filterTargetsToShow(n.path),i=Cx.call(t,Ju.call(t,a.data()));n.attr("clip-path",`path('${Zu.bind(t)()}')`),n.background.attr("d",Zu.call(t,!0)),e(a).attr("d",(o,s)=>`M${i[s].join("L")}z`).style("opacity","1"),n.selectAll("g").style("opacity",null)}},Mx={initGauge(){const t=this,{config:e,$el:{arcs:n}}=t,a=(i=null,o="")=>{n.append("text").attr("class",i).style("text-anchor","middle").style("pointer-events","none").text(o)};if(t.hasType("gauge")){const i=t.hasMultiArcGauge();n.append(i?"g":"path").attr("class",Ve.chartArcsBackground).style("fill",!i&&e.gauge_background||null),e.gauge_units&&a(Un.chartArcsGaugeUnit),e.gauge_label_show&&(a(Un.chartArcsGaugeMin),!e.gauge_fullCircle&&a(Un.chartArcsGaugeMax))}},updateGaugeMax(){const t=this,{config:e,state:n}=t,i=t.hasMultiArcGauge()?t.getMinMaxData().max[0].value:t.getTotalDataSum(n.rendered);!e.gauge_enforceMinMax&&i+e.gauge_min*(e.gauge_min>0?-1:1)>e.gauge_max&&(e.gauge_max=i-e.gauge_min)},redrawArcGaugeLine(){const t=this,{config:e,state:n,$el:a}=t,{hiddenTargetIds:i}=t.state,o=a.main.selectAll(`.${Ve.arcs}`).selectAll(`.${Ve.arcLabelLine}`).data(t.arcData.bind(t));o.enter().append("rect").attr("class",l=>`${Ve.arcLabelLine} ${Se.target} ${Se.target}-${l.data.id}`).merge(o).style("fill",l=>t.levelColor?t.levelColor(l.data.values[0].value):t.color(l.data)).style("display",e.gauge_label_show?null:"none").each(function(l){let c=0;const f=2;let g=0,v=0,m="";if(i.indexOf(l.data.id)<0){const S=t.updateAngle(l),P=n.gaugeArcWidth/t.filterTargetsToShow(t.data.targets).length*(S.index+1),N=S.endAngle-Math.PI/2,L=n.radius-P,w=N-(L===0?0:1/L);c=n.radiusExpanded-n.radius+P,g=Math.cos(w)*L,v=Math.sin(w)*L,m=`rotate(${N*180/Math.PI}, ${g}, ${v})`}ot(this).attr("x",g).attr("y",v).attr("width",c).attr("height",f).attr("transform",m).style("stroke-dasharray",`0, ${c+f}, 0`)})},textForGaugeMinMax(t,e){const n=this,{config:a}=n,i=a.gauge_label_extents;return ve(i)?i.bind(n.api)(t,e):t},getGaugeLabelHeight(){const{config:t}=this;return this.config.gauge_label_show&&!t.gauge_fullCircle?20:0},getPaddingBottomForGauge(){const t=this;return t.getGaugeLabelHeight()*(t.config.gauge_label_show?2:2.5)}};function Dx(t,e,n,a=!1){const i=t?[t,0]:n;for(let o=t||n.reduce((s,l)=>s+l);o<=e;)n.forEach(s=>{o+s<=e&&i.push(s),o+=s});return i.length%2!==0&&i.push(a?n[1]:0),{dash:i.join(" "),length:i.reduce((o,s)=>o+s,0)}}function Lx(t,e,n){const a=this,i=[],o="2 2";if(Qe(e)){const s=(l,c)=>ln(l)?c:n?Yn.call(a,l):l;for(let l=0,c;c=e[l];l++){const f=s(c.start,t[0].x),g=s(c.end,t[t.length-1].x),v=c.style||{dasharray:o};i[l]={start:f,end:g,style:v}}}return i}var Nx={initLine(){const{$el:t}=this;t.line=t.main.select(`.${Se.chart}`).append("g").attr("class",ur.chartLines).call(this.setCssRule(!1,`.${ur.chartLines}`,["pointer-events:none"]))},updateTargetsForLine(t){const e=this,{$el:{area:n,line:a,main:i}}=e,o=e.getChartClass("Line"),s=e.getClass("lines",!0),l=e.classFocus.bind(e);a||e.initLine();const c=t.filter(v=>!(e.isScatterType(v)||e.isBubbleType(v))),f=i.select(`.${ur.chartLines}`).selectAll(`.${ur.chartLine}`).data(c).attr("class",v=>o(v)+l(v)),g=f.enter().append("g").attr("class",o).style("opacity","0").style("pointer-events",e.getStylePropValue("none"));if(g.append("g").attr("class",s),e.hasTypeOf("Area")){const v=(!n&&g.empty()?f:g).filter(e.isAreaType.bind(e));e.initArea(v)}e.updateTargetForCircle(c,g)},updateLine(t,e=!1){const n=this,{format:{extraLineClasses:a},$el:i,$T:o}=n,s=e?i.subchart:i,l=s.main.selectAll(`.${ur.lines}`).selectAll(`.${ur.line}`).data(n.lineData.bind(n));o(l.exit(),t).style("opacity","0").remove(),s.line=l.enter().append("path").attr("class",c=>`${n.getClass("line",!0)(c)} ${a(c)||""}`).style("stroke",n.color).merge(l).style("opacity",n.initialOpacity.bind(n)).attr("transform",null)},redrawLine(t,e,n=!1){const a=this,{$el:i,$T:o}=a,{line:s}=n?i.subchart:i;return[o(s,e,gr()).attr("d",t).style("stroke",this.color).style("opacity",null)]},getCurve(t){const e=this;return e.config.axis_rotated&&e.isStepType(t)?a=>{const i=e.getInterpolate(t)(a);return i.orgPoint=i.point,i.pointRotated=function(o,s){this._point===1&&(this._point=2);const l=this._y*(1-this._t)+s*this._t;this._context.lineTo(this._x,l),this._context.lineTo(o,l),this._x=o,this._y=s},i.point=function(o,s){this._point===0?this.orgPoint(o,s):this.pointRotated(o,s)},i}:e.getInterpolate(t)},generateDrawLine(t,e){const n=this,{config:a,scale:i}=n,o=a.line_connectNull,s=a.axis_rotated,l=n.generateGetLinePoints(t,e),c=n.getYScaleById.bind(n),f=S=>(e?n.subxx:n.xx).call(n,S),g=(S,P)=>n.isGrouped(S.id)?l(S,P)[0][1]:c(S.id,e)(n.getBaseValue(S));let v=Gu();v=s?v.x(g).y(f):v.x(f).y(g),o||(v=v.defined(S=>n.getBaseValue(S)!==null));const m=e?i.subX:i.x;return S=>{const P=c(S.id,e);let N=o?n.filterRemoveNull(S.values):S.values,L=0,w=0,X;if(n.isLineType(S)){const W=a.data_regions[S.id];W?X=n.lineWithRegions(N,i.zoom||m,P,W):(n.isStepType(S)&&(N=n.convertValuesToStep(N)),X=v.curve(n.getCurve(S))(N))}else N[0]&&(L=m(N[0].x),w=P(N[0].value)),X=s?`M ${w} ${L}`:`M ${L} ${w}`;return X||"M 0 0"}},lineWithRegions(t,e,n,a){const i=this,{config:o}=i,s=o.axis_rotated,l=i.axis.isTimeSeries(),c="2 2",f=Lx.bind(i)(t,a,l),g=i.hasNullDataValue(t);let v,m,S,P;const N=s?dt=>n(dt.value):dt=>e(dt.x),L=s?dt=>e(dt.x):dt=>n(dt.value),w=dt=>`M${dt[0][0]},${dt[0][1]}L${dt[1][0]},${dt[1][1]}`,X=l?(dt,st,Vt,vt)=>{const Q=dt.x.getTime(),St=st.x-dt.x,ct=new Date(Q+St*Vt),At=new Date(Q+St*(Vt+vt)),Gt=s?[[n(m(Vt)),e(ct)],[n(m(Vt+S)),e(At)]]:[[e(ct),n(m(Vt))],[e(At),n(m(Vt+S))]];return w(Gt)}:(dt,st,Vt,vt)=>{const Q=e(st.x,!s),St=n(st.value,s),ct=Vt+vt,At=e(v(Vt),!s),Gt=n(m(Vt),s);let Bt=e(v(ct),!s),Kt=n(m(ct),s);Bt>Q&&(Bt=Q),dt.value>st.value&&(s?KtSt)&&(Kt=St);const ne=[[At,Gt],[Bt,Kt]];return s&&ne.forEach(le=>le.reverse()),w(ne)},W={x:i.axis.getAxisType("x"),y:i.axis.getAxisType("y")};let H="";const k=i.$el.line.filter(({id:dt})=>dt===t[0].id),K=k.clone().style("display","none"),at=(dt,st)=>dt.attr("d",st).node().getTotalLength(),ht={dash:[],lastLength:0};let $t=!1;for(let dt=0,st;st=t[dt];dt++){const Vt=t[dt-1],vt=Vt&&De(Vt.value);let Q=i.isWithinRegions(st.x,f);if(De(st.value)){if(ln(f)||!Q||!vt)H+=`${dt&&vt?"L":"M"}${N(st)},${L(st)}`;else if(vt)if(Q=((Q==null?void 0:Q.dasharray)||c).split(" ").map(Number),v=zr(W.x,Vt.x,st.x),m=zr(W.y,Vt.value,st.value),g){const St=e(st.x)-e(Vt.x),ct=n(st.value)-n(Vt.value),At=Math.sqrt(Math.pow(St,2)+Math.pow(ct,2));S=Q[0]/At,P=S*Q[1];for(let Gt=S;Gt<=1;Gt+=P)H+=X(Vt,st,Gt,S),Gt+P>=1&&(H+=X(Vt,st,1,0))}else{let St=[];if($t=st.x===t[t.length-1].x,l){const Bt=+Vt.x,Kt=new Date(Bt),ne=new Date(Bt+(+st.x-Bt));St=[[e(Kt),n(m(0))],[e(ne),n(m(1))]]}else St=[[e(v(0)),n(m(0))],[e(v(1)),n(m(1))]];s&&St.forEach(Bt=>Bt.reverse());const ct=at(K,H),At=at(K,H+=`L${St[1].join(",")}`),Gt=Dx(ct-ht.lastLength,At-ht.lastLength,Q,$t);ht.lastLength+=Gt.length,ht.dash.push(Gt.dash)}}}return ht.dash.length&&(!$t&&ht.dash.push(at(K,H)),K.remove(),k.attr("stroke-dasharray",ht.dash.join(" "))),H},isWithinRegions(t,e){for(let n=0,a;a=e[n];n++)if(a.startgr();var Ji={initialOpacityForCircle(t){const{config:e,state:{withoutFadeIn:n}}=this;let a=e.point_opacity;return ln(a)&&(a=this.getBaseValue(t)!==null&&n[t.id]?this.opacityForCircle(t):"0"),a},opacityForCircle(t){var e;const{config:n}=this;let a=n.point_opacity;return ln(a)&&(a=n.point_show&&!((e=this.isPointFocusOnly)!=null&&e.call(this))?null:"0",a=De(this.getBaseValue(t))?this.isBubbleType(t)||this.isScatterType(t)?"0.5":a:"0"),a},initCircle(){const t=this,{$el:{main:e}}=t;!t.point&&(t.point=t.generatePoint()),(t.hasType("bubble")||t.hasType("scatter"))&&e.select(`.${Se.chart} > .${$n.chartCircles}`).empty()&&e.select(`.${Se.chart}`).append("g").attr("class",$n.chartCircles)},updateTargetForCircle(t,e){const n=this,{config:a,data:i,$el:o}=n,s=a.interaction_enabled&&a.data_selection_enabled,l=s&&a.data_selection_isselectable,c=n.getClass("circles",!0);if(!a.point_show)return;n.initCircle();let f=t,g=e;if(!f){f=i.targets.filter(m=>this.isScatterType(m)||this.isBubbleType(m));const v=o.main.select(`.${$n.chartCircles}`).style("pointer-events","none").selectAll(`.${$n.circles}`).data(f);v.exit().remove(),g=v.enter()}s&&g.append("g").attr("class",v=>n.generateClass(tn.selectedCircles,v.id)),g.append("g").attr("class",c).call(v=>{n.setCssRule(!0,`.${$n.circles}`,["cursor:pointer"],l)(v),n.setCssRule(!0,` .${$n.circle}`,["fill","stroke"],n.color)(v)}).style("opacity",function(){return ot(this.parentNode).attr("class").indexOf($n.chartCircles)>-1?"0":null}),s&&f.forEach(v=>{o.main.selectAll(`.${tn.selectedCircles}${n.getTargetSelectorSuffix(v.id)}`).selectAll(`${tn.selectedCircle}`).each(m=>{m.value=v.values[m.index].value})})},updateCircle(t=!1){const e=this,{config:n,state:a,$el:i}=e,o=e.isPointFocusOnly(),s=t?i.subchart:i;if(n.point_show&&!a.toggling){n.point_radialGradient&&e.updateLinearGradient();const l=s.main.selectAll(`.${$n.circles}`).selectAll(`.${$n.circle}`).data(c=>e.isLineType(c)&&e.shouldDrawPointsForLine(c)||e.isBubbleType(c)||e.isRadarType(c)||e.isScatterType(c)?o?[c.values[0]]:c.values:[]);l.exit().remove(),l.enter().filter(Boolean).append(e.point("create",this,e.pointR.bind(e),e.updateCircleColor.bind(e))),s.circle=s.main.selectAll(`.${$n.circles} .${$n.circle}`).style("stroke",e.getStylePropValue(e.color)).style("opacity",e.initialOpacityForCircle.bind(e))}},updateCircleColor(t){const e=this,n=e.getStylePropValue(e.color);return e.config.point_radialGradient?e.getGradienColortUrl(t.id):n?n(t):null},redrawCircle(t,e,n,a,i=!1){const o=this,{state:{rendered:s},$el:l,$T:c}=o,f=i?l.subchart:l,g=f.main.selectAll(`.${tn.selectedCircle}`);if(!o.config.point_show)return[];const v=o.point("update",o,t,e,o.updateCircleColor.bind(o),n,a,g),m=o.isCirclePoint()?"c":"",S=gr(),P=o.opacityForCircle.bind(o),N=[];return f.circle.each(function(L){let w=v.bind(this)(L);w=c(w,n||!s,S).style("opacity",P),N.push(w)}),[N,c(g,n).attr(`${m}x`,t).attr(`${m}y`,e)]},showCircleFocus(t){const e=this,{state:{hasRadar:n,resizing:a,toggling:i,transiting:o},$el:s}=e;let{circle:l}=s;if(o===!1&&l&&e.isPointFocusOnly()){const c=(n?e.radarCircleX:e.circleX).bind(e),f=(n?e.radarCircleY:e.circleY).bind(e),g=i||ln(t),v=e.point("update",e,c,f,e.getStylePropValue(e.color),a?!1:g);t&&(l=l.filter(function(m){var S;const P=(S=t.filter)==null?void 0:S.call(t,N=>N.id===m.id);return P.length?ot(this).datum(P[0]):!1})),l.attr("class",this.updatePointClass.bind(this)).style("opacity",null).each(function(m){const{id:S,index:P,value:N}=m;let L="hidden";De(N)&&(v.bind(this)(m),e.expandCircles(P,S),L=""),this.style.visibility=L})}},hideCircleFocus(){const t=this,{$el:{circle:e}}=t;t.isPointFocusOnly()&&e&&(t.unexpandCircles(),e.style("visibility","hidden"))},circleX(t){return this.xx(t)},updateCircleY(t=!1){const e=this,n=e.generateGetLinePoints(e.getShapeIndices(e.isLineType),t);return(a,i)=>{const o=a.id;return e.isGrouped(o)?n(a,i)[0][1]:e.getYScaleById(o,t)(e.getBaseValue(a))}},expandCircles(t,e,n){const a=this,i=a.pointExpandedR.bind(a);n&&a.unexpandCircles();const o=a.getShapeByIndex("circle",t,e).classed(Se.EXPANDED,!0),s=i(o)/a.config.point_r,l=1-s;a.isCirclePoint()?o.attr("r",i):o.each(function(){const c=ot(this);if(this.tagName==="circle")c.attr("r",i);else{const{width:f,height:g}=this.getBBox(),v=l*(+c.attr("x")+f/2),m=l*(+c.attr("y")+g/2);c.attr("transform",`translate(${v} ${m}) scale(${s})`)}})},unexpandCircles(t){const e=this,n=e.pointR.bind(e),a=e.getShapeByIndex("circle",t).filter(function(){return ot(this).classed(Se.EXPANDED)}).classed(Se.EXPANDED,!1);if(a.attr("r",n),!e.isCirclePoint()){const i=n(a)/e.config.point_r;a.attr("transform",i!==1?`scale(${i})`:null)}},pointR(t){const e=this,{config:n}=e,a=n.point_r;let i=a;return e.isBubbleType(t)?i=e.getBubbleR(t):ve(a)&&(i=a.bind(e.api)(t)),t.r=i,i},pointExpandedR(t){const e=this,{config:n}=e,a=e.isBubbleType(t)?1.15:1.75;return n.point_focus_expand_enabled?n.point_focus_expand_r||e.pointR(t)*a:e.pointR(t)},pointSelectR(t){const e=this,n=e.config.point_select_r;return ve(n)?n(t):n||e.pointR(t)*4},isPointFocusOnly(){const t=this;return t.config.point_focus_only&&!t.hasType("bubble")&&!t.hasType("scatter")&&!t.hasArcType(null,["radar"])},isWithinCircle(t,e){const{state:n}=this,a=Hn(n.event,t),i=ot(t),o=this.isCirclePoint(t)?"c":"",s=this.getPointSensitivity(i==null?void 0:i.datum());let l=+i.attr(`${o}x`),c=+i.attr(`${o}y`);if(!(l||c)&&t.nodeType===1){const{x:f,y:g}=Ma(t);l=f,c=g}return Math.sqrt(Math.pow(l-a[0],2)+Math.pow(c-a[1],2))<(e||s)},getPointSensitivity(t){const e=this;let n=e.config.point_sensitivity;if(t)ve(n)?n=n.call(e.api,t):n==="radius"&&(n=t.r);else return n;return n},updatePointClass(t){const e=this,{circle:n}=e.$el;let a=!1;return(Be(t)||n)&&(a=t===!0?n.each(function(i){let o=e.getClass("circle",!0)(i);this.getAttribute("class").indexOf(Se.EXPANDED)>-1&&(o+=` ${Se.EXPANDED}`),this.setAttribute("class",o)}):e.getClass("circle",!0)(t)),a},generateGetLinePoints(t,e){const n=this,{config:a}=n,i=n.getShapeX(0,t,e),o=n.getShapeY(e),s=n.getShapeOffset(n.isLineType,t,e),l=n.getYScaleById.bind(n);return(c,f)=>{const g=l.call(n,c.id,e)(n.getShapeYMin(c.id)),v=s(c,f)||g,m=i(c);let S=o(c);a.axis_rotated&&(c.value>0&&SDe(S.value)?e(S)-c/2:0,v=S=>De(S.value)?n(S)-f/2:0;let m=t;return i&&(o&&m.attr("x",g),m=l.$T(m,i,la()),s&&l.$T(s,i,la())),m.attr("x",g).attr("y",v).style("fill",a)}},circle:{create(t,e,n){return t.append("circle").attr("class",this.updatePointClass.bind(this)).attr("r",e).style("fill",n).node()},update(t,e,n,a,i,o,s){const l=this;let c=t;return l.hasType("bubble")&&c.attr("r",l.pointR.bind(l)),i&&(o&&c.attr("cx",e),c.attr("cx")&&(c=l.$T(c,i,la())),s&&l.$T(c,i,la())),c.attr("cx",e).attr("cy",n).style("fill",a)}},rectangle:{create(t,e,n){const a=i=>e(i)*2;return t.append("rect").attr("class",this.updatePointClass.bind(this)).attr("width",a).attr("height",a).style("fill",n).node()},update(t,e,n,a,i,o,s){const l=this,c=l.config.point_r,f=m=>e(m)-c,g=m=>n(m)-c;let v=t;return i&&(o&&v.attr("x",f),v=l.$T(v,i,la()),s&&l.$T(s,i,la())),v.attr("x",f).attr("y",g).style("fill",a)}}};function Fx(t){return nr(t)&&ve(t.create)&&ve(t.update)}function Bx(t,e){var n;const a=this,i=(c,f)=>{const g=c.attributes;for(let v=0,m;m=g[v];v++)m=m.name,f.setAttribute(m,c.getAttribute(m))},s=new DOMParser().parseFromString(t,"image/svg+xml").documentElement,l=gn.createElementNS(ae.svg,s.nodeName.toLowerCase());if(l.id=e,l.style.fill="inherit",l.style.stroke="inherit",i(s,l),(n=s.childNodes)!=null&&n.length){const c=ot(l);"innerHTML"in l?c.html(s.innerHTML):Lr(s.childNodes).forEach(f=>{i(f,c.append(f.tagName).node())})}a.$el.defs.node().appendChild(l)}var ca={hasValidPointType(t){return/^(circle|rect(angle)?|polygon|ellipse|use)$/i.test(t||this.config.point_type)},hasLegendDefsPoint(){var t;const{config:e}=this;return e.legend_show&&((t=e.point_pattern)==null?void 0:t.length)&&e.legend_usePoint},getDefsPointId(t){const{state:{datetimeId:e}}=this;return`${e}-point${t}`},generatePoint(){const t=this,{$el:e,config:n}=t,a=[],i=cn(n.point_pattern)?n.point_pattern:[n.point_type];return function(o,s,...l){return function(c){var f,g,v,m;const S=t.getTargetSelectorSuffix(c.id||((f=c.data)==null?void 0:f.id)||c),P=ot(this);a.indexOf(S)<0&&a.push(S);let N=i[a.indexOf(S)%i.length];if(t.hasValidPointType(N))N=t[N];else if(!Fx(N||n.point_type)){const L=t.getDefsPointId(S);if(e.defs.select(`#${L}`).size()<1&&Bx.bind(t)(N,L),o==="create")return(g=t.custom)==null?void 0:g.create.bind(s)(P,L,...l);if(o==="update")return(v=t.custom)==null?void 0:v.update.bind(s)(P,...l)}return(m=N[o])==null?void 0:m.bind(s)(P,...l)}}}};function Qu(t){const e=t.config.polar_level_max;let n=t.getMinMaxData().max[0].value;return e&&e>n&&(n=e),n}var Ux={initPolar(){const t=this,{$el:{arcs:e},config:n}=t,a=n.polar_level_text_show,i=n.polar_level_text_backgroundColor;e.levels=e.append("g").attr("class",Tr.levels),a&&i&&t.generateTextBGColorFilter(i)},getPolarOuterRadius(t,e){var n;const a=Qu(this);return((n=t==null?void 0:t.data.values[0].value)!=null?n:0)/a*e},updateTargetsForPolar(t){this.updateTargetsForArc(t)},redrawPolar(){const t=this,{config:e}=t;e.polar_level_show&&t.updatePolarLevel()},updatePolarLevel(){const t=this,{config:e,state:n,$el:{arcs:{levels:a}}}=t,i=e.polar_level_depth,o=Qu(t),s=Ei(0,i),l=n.radius,c=s.map(m=>l*((m+1)/i)),f=(e.polar_level_text_format||function(){}).bind(t.api),g=a.selectAll(`.${Tr.level}`).data(s);g.exit().remove();const v=g.enter().append("g").attr("class",(m,S)=>`${Tr.level} ${Tr.level}-${S}`);if(v.append("circle"),v.merge(g).selectAll("circle").style("visibility",e.polar_level_show?null:"hidden").attr("cx",0).attr("cy",0).attr("r",m=>c[m]),e.polar_level_text_show){const m=e.polar_level_text_backgroundColor,S=`#${n.datetimeId}-labels-bg${t.getTargetSelectorSuffix(m)}`;v.append("text").style("text-anchor","middle"),v.merge(g).selectAll("text").attr("dy",P=>-c[P]+5).attr("filter",m?`url(${S})`:null).text(P=>f(o/s.length*(P+1)))}}};function zx(t,e,n,a,i,o){const s=t&&a>0?n-a:a,l=2*Math.PI;return i*(1-o*(e==="x"?Math.sin:Math.cos)(s*l/n))}const ua=Ln.radarPoints,ku=Ln.radarTextWidth;var jx={initRadar(){const t=this,{config:e,state:{current:n},$el:a}=t;t.hasType("radar")&&(a.radar=a.main.select(`.${Se.chart}`).append("g").attr("class",Qs.chartRadars),a.radar.levels=a.radar.append("g").attr("class",Tr.levels),a.radar.axes=a.radar.append("g").attr("class",Tn.axis),a.radar.shapes=a.radar.append("g").attr("class",sn.shapes),n.dataMax=e.radar_axis_max||t.getMinMaxData().max[0].value,e.radar_axis_text_show&&(e.interaction_enabled&&t.bindRadarEvent(),t.updateRadarLevel(),t.updateRadarAxes()))},getRadarSize(){const t=this,{config:e,state:{arcWidth:n,arcHeight:a}}=t,i=e.axis_x_categories.length<4?-20:10,o=(Math.min(n,a)-i)/2;return[o,o]},updateTargetsForRadar(t){const e=this,{config:n}=e;qn(n.axis_x_categories)&&(n.axis_x_categories=Ei(0,_n("max",t.map(a=>a.values.length)))),e.generateRadarPoints()},getRadarPosition(t,e,n,a){const i=this,{config:o}=i,[s,l]=i.getRadarSize(),c=o.axis_x_categories.length,f=o.radar_direction_clockwise,g=Lr(t).map(v=>zx(f,v,c,e,Qe(n)?n:t==="x"?s:l,he(a)?a:o.radar_size_ratio));return g.length===1?g[0]:g},generateRadarPoints(){const t=this,e=t.data.targets,[n,a]=t.getRadarSize(),i=t.cache.get(ua)||{},o=i._size;(!o||o.width!==n&&o.height!==a)&&(e.forEach(s=>{i[s.id]=s.values.map((l,c)=>t.getRadarPosition(["x","y"],c,void 0,t.getRatio("radar",l)))}),i._size={width:n,height:a},t.cache.add(ua,i))},redrawRadar(){const t=this,{radar:e,main:n}=t.$el,a=t.getTranslate("radar");a&&(e.attr("transform",a),n.select(`.${On.chartTexts}`).attr("transform",a),t.generateRadarPoints(),t.updateRadarLevel(),t.updateRadarAxes(),t.updateRadarShape())},generateGetRadarPoints(){const t=this.cache.get(ua);return(e,n)=>{const a=t[e.id][n];return[a,a,a,a]}},updateRadarLevel(){const t=this,{config:e,state:n,$el:{radar:a}}=t,[i,o]=t.getRadarSize(),s=e.radar_level_depth,l=e.axis_x_categories.length,c=e.radar_level_text_show,f=a.levels,g=Ei(0,s),v=e.radar_size_ratio*Math.min(i,o),m=g.map(w=>v*((w+1)/s)),S=(e.radar_level_text_format||function(){}).bind(t.api),P=g.map(w=>{const X=m[w];return Ei(0,l).map(H=>t.getRadarPosition(["x","y"],H,X,1).join(",")).join(" ")}),N=f.selectAll(`.${Tr.level}`).data(g);N.exit().remove();const L=N.enter().append("g").attr("class",(w,X)=>`${Tr.level} ${Tr.level}-${X}`);L.append("polygon").style("visibility",e.radar_level_show?null:"hidden"),c&&(f.select("text").empty()&&f.append("text").attr("dx","-.5em").attr("dy","-.7em").style("text-anchor","end").text(()=>S(0)),L.append("text").attr("dx","-.5em").style("text-anchor","end").text(w=>S(n.current.dataMax/g.length*(w+1)))),L.merge(N).attr("transform",w=>`translate(${i-m[w]}, ${o-m[w]})`).selectAll("polygon").attr("points",w=>P[w]),c&&f.selectAll("text").attr("x",w=>ln(w)?i:P[w].split(",")[0]).attr("y",w=>ln(w)?o:0)},updateRadarAxes(){const t=this,{config:e,$el:{radar:n}}=t,[a,i]=t.getRadarSize(),o=e.axis_x_categories;let s=n.axes.selectAll("g").data(o);s.exit().remove();const l=s.enter().append("g").attr("class",(c,f)=>`${Tn.axis}-${f}`);if(e.radar_axis_line_show&&l.append("line"),e.radar_axis_text_show&&l.append("text"),s=l.merge(s),e.radar_axis_line_show&&s.select("line").attr("x1",a).attr("y1",i).attr("x2",(c,f)=>t.getRadarPosition("x",f)).attr("y2",(c,f)=>t.getRadarPosition("y",f)),e.radar_axis_text_show){const{x:c=0,y:f=0}=e.radar_axis_text_position,g=t.cache.get(ku)||0;if(s.select("text").style("text-anchor","middle").attr("dy",".5em").call(v=>{v.each(function(m){wa(ot(this),String(m),[-.6,1.2])})}).datum((v,m)=>({index:m})).attr("transform",function(v){ln(this.width)&&(this.width=this.getBoundingClientRect().width/2);let m=t.getRadarPosition("x",v.index,void 0,1),S=Math.round(t.getRadarPosition("y",v.index,void 0,1));return m>a?m+=this.width+c:Math.round(m)i?(S/2===i&&this.firstChild.tagName==="tspan"&&this.firstChild.setAttribute("dy","0em"),S+=f):SYl(m.node()).width);v.every(m=>m>0)&&t.cache.add(ku,v[0]-v[1])}}},bindRadarEvent(){const t=this,{config:e,state:n,$el:{radar:a,svg:i}}=t,o=t.isPointFocusOnly(),{inputType:s,transiting:l}=n,c=s==="mouse",f=g=>{if(n.event=g,!e.interaction_onout)return;const v=t.getDataIndexFromEvent(g),m=ln(v);(c||m)&&(t.hideTooltip(),o?t.hideCircleFocus():t.unexpandCircles(),c?t.setOverOut(!1,v):m&&t.callOverOutForTouch())};a.axes.on(c?"mouseover ":"touchstart",g=>{if(l)return;n.event=g;const v=t.getDataIndexFromEvent(g);t.selectRectForSingle(i.node(),v),c?t.setOverOut(!0,v):t.callOverOutForTouch(v)}).on("mouseout",c?f:null),c||i.on("touchstart",f)},updateRadarShape(){const t=this,e=t.data.targets.filter(o=>t.isRadarType(o)),n=t.cache.get(ua),a=t.$el.radar.shapes.selectAll("polygon").data(e),i=a.enter().append("g").attr("class",t.getChartClass("Radar"));t.$T(a.exit()).remove(),i.append("polygon").merge(a).style("fill",t.color).style("stroke",t.color).attr("points",o=>n[o.id].join(" ")),t.updateTargetForCircle(e,i)},radarCircleX(t){return this.cache.get(ua)[t.id][t.index][0]},radarCircleY(t){return this.cache.get(ua)[t.id][t.index][1]}};function Vx(t){var e=0,n=t.children,a=n&&n.length;if(!a)e=1;else for(;--a>=0;)e+=n[a].value;t.value=e}function Gx(){return this.eachAfter(Vx)}function Xx(t,e){let n=-1;for(const a of this)t.call(e,a,++n,this);return this}function Hx(t,e){for(var n=this,a=[n],i,o,s=-1;n=a.pop();)if(t.call(e,n,++s,this),i=n.children)for(o=i.length-1;o>=0;--o)a.push(i[o]);return this}function Yx(t,e){for(var n=this,a=[n],i=[],o,s,l,c=-1;n=a.pop();)if(i.push(n),o=n.children)for(s=0,l=o.length;s=0;)n+=a[i].value;e.value=n})}function Zx(t){return this.eachBefore(function(e){e.children&&e.children.sort(t)})}function Jx(t){for(var e=this,n=Qx(e,t),a=[e];e!==n;)e=e.parent,a.push(e);for(var i=a.length;t!==n;)a.splice(i,0,t),t=t.parent;return a}function Qx(t,e){if(t===e)return t;var n=t.ancestors(),a=e.ancestors(),i=null;for(t=n.pop(),e=a.pop();t===e;)i=t,t=n.pop(),e=a.pop();return i}function kx(){for(var t=this,e=[t];t=t.parent;)e.push(t);return e}function qx(){return Array.from(this)}function _x(){var t=[];return this.eachBefore(function(e){e.children||t.push(e)}),t}function t0(){var t=this,e=[];return t.each(function(n){n!==t&&e.push({source:n.parent,target:n})}),e}function*e0(){var t=this,e,n=[t],a,i,o;do for(e=n.reverse(),n=[];t=e.pop();)if(yield t,a=t.children)for(i=0,o=a.length;i=0;--l)i.push(o=s[l]=new Qi(s[l])),o.parent=a,o.depth=a.depth+1;return n.eachBefore(o0)}function n0(){return Rs(this).eachBefore(i0)}function r0(t){return t.children}function a0(t){return Array.isArray(t)?t[1]:null}function i0(t){t.data.value!==void 0&&(t.value=t.data.value),t.data=t.data.data}function o0(t){var e=0;do t.height=e;while((t=t.parent)&&t.height<++e)}function Qi(t){this.data=t,this.depth=this.height=0,this.parent=null}Qi.prototype=Rs.prototype={constructor:Qi,count:Gx,each:Xx,eachAfter:Yx,eachBefore:Hx,find:Wx,sum:Kx,sort:Zx,path:Jx,ancestors:kx,descendants:qx,leaves:_x,links:t0,copy:n0,[Symbol.iterator]:e0};function s0(t){t.x0=Math.round(t.x0),t.y0=Math.round(t.y0),t.x1=Math.round(t.x1),t.y1=Math.round(t.y1)}function ki(t,e,n,a,i){for(var o=t.children,s,l=-1,c=o.length,f=t.value&&(a-e)/t.value;++lX&&(X=f),K=L*L*k,W=Math.max(X/K,K/w),W>H){L-=f;break}H=W}s.push(c={value:L,dice:S1?a:1)},n}(qu);function C1(t){return t==null?null:ef(t)}function ef(t){if(typeof t!="function")throw new Error;return t}function ja(){return 0}function Va(t){return function(){return t}}function l0(){var t=tf,e=!1,n=1,a=1,i=[0],o=ja,s=ja,l=ja,c=ja,f=ja;function g(m){return m.x0=m.y0=0,m.x1=n,m.y1=a,m.eachBefore(v),i=[0],e&&m.eachBefore(s0),m}function v(m){var S=i[m.depth],P=m.x0+S,N=m.y0+S,L=m.x1-S,w=m.y1-S;L=m-1){var X=o[v];X.x0=P,X.y0=N,X.x1=L,X.y1=w;return}for(var W=f[v],H=S/2+W,k=v+1,K=m-1;k>>1;f[at]w-N){var dt=S?(P*$t+L*ht)/S:L;g(v,k,ht,P,N,dt,w),g(k,m,$t,dt,N,L,w)}else{var st=S?(N*$t+w*ht)/S:w;g(v,k,ht,P,N,L,st),g(k,m,$t,P,st,L,w)}}}function c0(t,e,n,a,i){(t.depth&1?qi:ki)(t,e,n,a,i)}var u0=function t(e){function n(a,i,o,s,l){if((c=a._squarify)&&c.ratio===e)for(var c,f,g,v,m=-1,S,P=c.length,N=a.value;++m1?a:1)},n}(qu);function f0(t,e){const n=this,{scale:{x:a,y:i},state:{width:o}}=n;t.selectAll("g").attr("transform",s=>`translate(${s===e?"0,0":`${a(s.x0)},${i(s.y0)}`})`).select("rect").attr("width",s=>s===e?o:a(s.x1)-a(s.x0)).attr("height",s=>s===e?0:i(s.y1)-i(s.y0))}function d0(t){const e=this;return t.map(n=>{const{id:a,values:i}=n,{value:o}=i[0];return{name:a,id:a,value:o,ratio:e.getRatio("treemap",i[0])}})}function h0(t){const e=this,n=Rs(t).sum(i=>i.value),a=e.getSortCompareFn(!0);return[e.treemap(a?n.sort(a):n)]}var g0={initTreemap(){const t=this,{$el:e,state:{current:{width:n,height:a},clip:i,datetimeId:o}}=t;i.id=`${o}-clip`,t.treemap=l0().tile(t.getTreemapTile()),e.defs.append("clipPath").attr("id",i.id).append("rect").attr("width",n).attr("height",a),e.treemap=e.main.select(`.${Se.chart}`).attr("clip-path",`url(#${i.id})`).append("g").classed(qs.chartTreemaps,!0),t.bindTreemapEvent()},bindTreemapEvent(){const t=this,{$el:e,config:n,state:a}=t,i=o=>{var s;const l=o.isTrusted?o.target:(s=a.eventReceiver.rect)==null?void 0:s.node();let c;return/^rect$/i.test(l.tagName)&&(a.event=o,c=ot(l).datum()),c==null?void 0:c.data};if(n.interaction_enabled){const o=a.inputType==="touch";e.treemap.on(o?"touchstart":"mouseover mousemove",s=>{const l=i(s);l&&(t.showTooltip([l],s.currentTarget),/^(touchstart|mouseover)$/.test(s.type)&&t.setOverOut(!0,l))}).on(o?"touchend":"mouseout",s=>{const l=i(s);n.interaction_onout&&(t.hideTooltip(),t.setOverOut(!1,l))})}},getTreemapTile(){var t,e;const n=this,{config:a,state:{current:{width:i,height:o}}}=n,s=(e={binary:nf,dice:ki,slice:qi,sliceDice:c0,squarify:tf,resquarify:u0}[(t=a.treemap_tile)!=null?t:"binary"])!=null?e:nf;return(l,c,f,g,v)=>{s(l,0,0,i,o);for(const m of l.children)m.x0=c+m.x0/i*(g-c),m.x1=c+m.x1/i*(g-c),m.y0=f+m.y0/o*(v-f),m.y1=f+m.y1/o*(v-f)}},getTreemapData(t){const e=this;return{name:"root",children:d0.bind(e)(e.filterTargetsToShow(t.filter(e.isTreemapType,e)))}},updateTargetsForTreemap(t){const e=this,{$el:{treemap:n}}=e,a=h0.call(e,e.getTreemapData(t!=null?t:e.data.targets));n.data(a)},updateTreemap(t){const e=this,{$el:n,$T:a}=e,i=n.treemap.datum(),o=e.getChartClass("Treemap"),s=e.getClass("treemap",!0),l=n.treemap.selectAll("g").data(i.children);a(l.exit(),t).style("opacity","0").remove(),l.enter().append("g").append("rect"),n.treemap.selectAll("g").attr("class",o).select("rect").attr("class",s).attr("fill",c=>e.color(c.data.name))},generateGetTreemapPoints(){const t=this,{$el:e,scale:{x:n,y:a}}=t,i={};return e.treemap.selectAll("g").each(o=>{i[o.data.name]=[[n(o.x0),a(o.y0)],[n(o.x1),a(o.y1)]]}),o=>i[o.id]},redrawTreemap(t){const e=this,{$el:n,state:{current:{width:a,height:i}}}=e;return n.defs.select("rect").attr("width",a).attr("height",i),[e.$T(n.treemap,t,gr()).call(f0.bind(e),n.treemap.datum())]},treemapDataLabelFormat(t){const e=this,{config:n}=e,{id:a,value:i}=t,o=n.treemap_label_format,s=e.getRatio("treemap",t),l=(s*100).toFixed(2),c=n.treemap_label_show&&e.meetsLabelThreshold(s,"treemap")?null:"0";return function(f){return f.style("opacity",c),ve(o)?o.bind(e.api)(i,s,a):`${a} +${l}%`}}},Xr={point_show:!0,point_r:2.5,point_radialGradient:!1,point_sensitivity:10,point_focus_expand_enabled:!0,point_focus_expand_r:void 0,point_focus_only:!1,point_opacity:void 0,point_pattern:[],point_select_r:void 0,point_type:"circle"},fa={area_above:!1,area_below:!1,area_front:!0,area_linearGradient:!1,area_zerobased:!0},v0={bar_front:!1,bar_indices_removeNull:!1,bar_label_threshold:0,bar_linearGradient:!1,bar_overlap:!1,bar_padding:0,bar_radius:void 0,bar_radius_ratio:void 0,bar_sensitivity:2,bar_width:void 0,bar_width_ratio:.6,bar_width_max:void 0,bar_zerobased:!0},p0={bubble_maxR:35,bubble_zerobased:!1},m0={candlestick_width:void 0,candlestick_width_ratio:.6,candlestick_width_max:void 0,candlestick_color_down:"red"},y0={line_connectNull:!1,line_step_type:"step",line_step_tooltipMatch:!1,line_zerobased:!1,line_classes:void 0,line_point:!0},x0={scatter_zerobased:!1},Is={spline_interpolation_type:"cardinal"},_i={arc_cornerRadius:0,arc_cornerRadius_ratio:0,arc_needle_show:!1,arc_needle_color:void 0,arc_needle_value:void 0,arc_needle_path:void 0,arc_needle_length:100,arc_needle_top_rx:0,arc_needle_top_ry:0,arc_needle_top_width:0,arc_needle_bottom_rx:1,arc_needle_bottom_ry:1,arc_needle_bottom_width:15,arc_needle_bottom_len:0,arc_rangeText_values:void 0,arc_rangeText_unit:"absolute",arc_rangeText_fixed:!1,arc_rangeText_format:void 0,arc_rangeText_position:void 0},T0={donut_label_show:!0,donut_label_format:void 0,donut_label_threshold:.05,donut_label_ratio:void 0,donut_width:void 0,donut_title:"",donut_expand:{},donut_expand_rate:.98,donut_expand_duration:50,donut_padAngle:0,donut_startingAngle:0},$0={funnel_neck_width:0,funnel_neck_height:0},S0={gauge_background:"",gauge_fullCircle:!1,gauge_label_show:!0,gauge_label_extents:void 0,gauge_label_format:void 0,gauge_label_ratio:void 0,gauge_label_threshold:0,gauge_enforceMinMax:!1,gauge_min:0,gauge_max:100,gauge_type:"single",gauge_startingAngle:-1*Math.PI/2,gauge_arcLength:100,gauge_title:"",gauge_units:void 0,gauge_width:void 0,gauge_arcs_minWidth:5,gauge_expand:{},gauge_expand_rate:.98,gauge_expand_duration:50},A0={pie_label_show:!0,pie_label_format:void 0,pie_label_ratio:void 0,pie_label_threshold:.05,pie_expand:{},pie_expand_rate:.98,pie_expand_duration:50,pie_innerRadius:0,pie_outerRadius:void 0,pie_padAngle:0,pie_padding:0,pie_startingAngle:0},E0={polar_label_show:!0,polar_label_format:void 0,polar_label_threshold:.05,polar_label_ratio:void 0,polar_level_depth:3,polar_level_max:void 0,polar_level_show:!0,polar_level_text_backgroundColor:"#fff",polar_level_text_format:t=>t%1===0?t:t.toFixed(2),polar_level_text_show:!0,polar_padAngle:0,polar_padding:0,polar_startingAngle:0},b0={radar_axis_max:void 0,radar_axis_line_show:!0,radar_axis_text_show:!0,radar_axis_text_position:{},radar_level_depth:3,radar_level_show:!0,radar_level_text_format:t=>t%1===0?t:t.toFixed(2),radar_level_text_show:!0,radar_size_ratio:.87,radar_direction_clockwise:!1},R0={treemap_tile:"binary",treemap_label_format:void 0,treemap_label_threshold:.05,treemap_label_show:!0};function da(t,e){yn(Vr.prototype,Object.values(Du).concat(t)),yn(Er.prototype,Jy),Nr.setOptions(Object.values(Lu).concat(e||[]))}function mr(t,e){da([ca,Ji,Nx].concat(t||[])),Nr.setOptions([Xr,y0].concat(e||[]))}function ha(t,e){yn(Vr.prototype,[px,ca].concat(t||[])),Nr.setOptions([Xr].concat(e||[]))}let rf=()=>(mr(sa,[fa]),(rf=()=>oe.AREA)()),af=()=>(mr(sa,[fa]),(af=()=>oe.AREA_LINE_RANGE)()),of=()=>(mr(sa,[fa]),(of=()=>oe.AREA_STEP_RANGE)()),sf=()=>(mr(sa,[fa,Is]),(sf=()=>oe.AREA_SPLINE)()),lf=()=>(mr(sa,[fa,Is]),(lf=()=>oe.AREA_SPLINE_RANGE)()),cf=()=>(mr(sa,[fa]),(cf=()=>oe.AREA_STEP)()),uf=()=>(mr(),(uf=()=>oe.LINE)()),ff=()=>(mr(void 0,[Is]),(ff=()=>oe.SPLINE)()),df=()=>(mr(),(df=()=>oe.STEP)()),hf=()=>(ha(void 0,[_i,T0]),(hf=()=>oe.DONUT)()),gf=()=>(ha([Mx],[_i,S0]),(gf=()=>oe.GAUGE)()),vf=()=>(ha(void 0,[_i,A0]),(vf=()=>oe.PIE)()),pf=()=>(ha([Ux],[_i,E0]),(pf=()=>oe.POLAR)()),mf=()=>(ha([Du.eventrect,Ji,jx],[Xr,b0,{axis_x_categories:Lu.optAxis.axis_x_categories}]),(mf=()=>oe.RADAR)()),yf=()=>(da([yx,ca],[v0,Xr]),(yf=()=>oe.BAR)()),xf=()=>(da([ca,Ji,xx],[p0,Xr]),(xf=()=>oe.BUBBLE)()),Tf=()=>(da([Ex,ca],[m0,Xr]),(Tf=()=>oe.CANDLESTICK)()),$f=()=>(da([ca,Ji],[Xr,x0]),($f=()=>oe.SCATTER)()),Sf=()=>(ha([wx],[$0]),(Sf=()=>oe.FUNNEL)()),Af=()=>(da([g0],[R0]),(Af=()=>oe.TREEMAP)()),Os=Object.create(null);const Ef={version:"3.15.1",generate(t){const e=ea(Object.create(null),Os,t),n=new Er(e);return n.internal.charts=this.instance,this.instance.push(n),n},defaults(t){return Be(t)&&(Os=t),Os},instance:[],plugin:{}};Object.keys(d).forEach(t=>d[t]()),Object.keys(u).forEach(t=>u[t]())}],Xa={};function zn(x){var b=Xa[x];if(b!==void 0)return b.exports;var r=Xa[x]={exports:{}};return Cs[x].call(r.exports,r,r.exports,zn),r.exports}(function(){zn.d=function(x,b){for(var r in b)zn.o(b,r)&&!zn.o(x,r)&&Object.defineProperty(x,r,{enumerable:!0,get:b[r]})}})(),function(){zn.o=function(x,b){return Object.prototype.hasOwnProperty.call(x,b)}}(),function(){zn.r=function(x){typeof Symbol!="undefined"&&Symbol.toStringTag&&Object.defineProperty(x,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(x,"__esModule",{value:!0})}}(),zn(0);var Ha=zn(584);return Ha}()}); diff --git a/packages/image/build/report/_js/bootstrap.bundle.min.js b/packages/image/build/report/_js/bootstrap.bundle.min.js new file mode 100644 index 000000000..8739c91c5 --- /dev/null +++ b/packages/image/build/report/_js/bootstrap.bundle.min.js @@ -0,0 +1,7 @@ +/*! + * Bootstrap v5.3.6 (https://getbootstrap.com/) + * Copyright 2011-2025 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).bootstrap=e()}(this,(function(){"use strict";const t=new Map,e={set(e,i,n){t.has(e)||t.set(e,new Map);const s=t.get(e);s.has(i)||0===s.size?s.set(i,n):console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(s.keys())[0]}.`)},get:(e,i)=>t.has(e)&&t.get(e).get(i)||null,remove(e,i){if(!t.has(e))return;const n=t.get(e);n.delete(i),0===n.size&&t.delete(e)}},i="transitionend",n=t=>(t&&window.CSS&&window.CSS.escape&&(t=t.replace(/#([^\s"#']+)/g,((t,e)=>`#${CSS.escape(e)}`))),t),s=t=>{t.dispatchEvent(new Event(i))},o=t=>!(!t||"object"!=typeof t)&&(void 0!==t.jquery&&(t=t[0]),void 0!==t.nodeType),r=t=>o(t)?t.jquery?t[0]:t:"string"==typeof t&&t.length>0?document.querySelector(n(t)):null,a=t=>{if(!o(t)||0===t.getClientRects().length)return!1;const e="visible"===getComputedStyle(t).getPropertyValue("visibility"),i=t.closest("details:not([open])");if(!i)return e;if(i!==t){const e=t.closest("summary");if(e&&e.parentNode!==i)return!1;if(null===e)return!1}return e},l=t=>!t||t.nodeType!==Node.ELEMENT_NODE||!!t.classList.contains("disabled")||(void 0!==t.disabled?t.disabled:t.hasAttribute("disabled")&&"false"!==t.getAttribute("disabled")),c=t=>{if(!document.documentElement.attachShadow)return null;if("function"==typeof t.getRootNode){const e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?c(t.parentNode):null},h=()=>{},d=t=>{t.offsetHeight},u=()=>window.jQuery&&!document.body.hasAttribute("data-bs-no-jquery")?window.jQuery:null,f=[],p=()=>"rtl"===document.documentElement.dir,m=t=>{var e;e=()=>{const e=u();if(e){const i=t.NAME,n=e.fn[i];e.fn[i]=t.jQueryInterface,e.fn[i].Constructor=t,e.fn[i].noConflict=()=>(e.fn[i]=n,t.jQueryInterface)}},"loading"===document.readyState?(f.length||document.addEventListener("DOMContentLoaded",(()=>{for(const t of f)t()})),f.push(e)):e()},g=(t,e=[],i=t)=>"function"==typeof t?t.call(...e):i,_=(t,e,n=!0)=>{if(!n)return void g(t);const o=(t=>{if(!t)return 0;let{transitionDuration:e,transitionDelay:i}=window.getComputedStyle(t);const n=Number.parseFloat(e),s=Number.parseFloat(i);return n||s?(e=e.split(",")[0],i=i.split(",")[0],1e3*(Number.parseFloat(e)+Number.parseFloat(i))):0})(e)+5;let r=!1;const a=({target:n})=>{n===e&&(r=!0,e.removeEventListener(i,a),g(t))};e.addEventListener(i,a),setTimeout((()=>{r||s(e)}),o)},b=(t,e,i,n)=>{const s=t.length;let o=t.indexOf(e);return-1===o?!i&&n?t[s-1]:t[0]:(o+=i?1:-1,n&&(o=(o+s)%s),t[Math.max(0,Math.min(o,s-1))])},v=/[^.]*(?=\..*)\.|.*/,y=/\..*/,w=/::\d+$/,A={};let E=1;const T={mouseenter:"mouseover",mouseleave:"mouseout"},C=new Set(["click","dblclick","mouseup","mousedown","contextmenu","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","selectstart","selectend","keydown","keypress","keyup","orientationchange","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointerleave","pointercancel","gesturestart","gesturechange","gestureend","focus","blur","change","reset","select","submit","focusin","focusout","load","unload","beforeunload","resize","move","DOMContentLoaded","readystatechange","error","abort","scroll"]);function O(t,e){return e&&`${e}::${E++}`||t.uidEvent||E++}function x(t){const e=O(t);return t.uidEvent=e,A[e]=A[e]||{},A[e]}function k(t,e,i=null){return Object.values(t).find((t=>t.callable===e&&t.delegationSelector===i))}function L(t,e,i){const n="string"==typeof e,s=n?i:e||i;let o=I(t);return C.has(o)||(o=t),[n,s,o]}function S(t,e,i,n,s){if("string"!=typeof e||!t)return;let[o,r,a]=L(e,i,n);if(e in T){const t=t=>function(e){if(!e.relatedTarget||e.relatedTarget!==e.delegateTarget&&!e.delegateTarget.contains(e.relatedTarget))return t.call(this,e)};r=t(r)}const l=x(t),c=l[a]||(l[a]={}),h=k(c,r,o?i:null);if(h)return void(h.oneOff=h.oneOff&&s);const d=O(r,e.replace(v,"")),u=o?function(t,e,i){return function n(s){const o=t.querySelectorAll(e);for(let{target:r}=s;r&&r!==this;r=r.parentNode)for(const a of o)if(a===r)return P(s,{delegateTarget:r}),n.oneOff&&N.off(t,s.type,e,i),i.apply(r,[s])}}(t,i,r):function(t,e){return function i(n){return P(n,{delegateTarget:t}),i.oneOff&&N.off(t,n.type,e),e.apply(t,[n])}}(t,r);u.delegationSelector=o?i:null,u.callable=r,u.oneOff=s,u.uidEvent=d,c[d]=u,t.addEventListener(a,u,o)}function D(t,e,i,n,s){const o=k(e[i],n,s);o&&(t.removeEventListener(i,o,Boolean(s)),delete e[i][o.uidEvent])}function $(t,e,i,n){const s=e[i]||{};for(const[o,r]of Object.entries(s))o.includes(n)&&D(t,e,i,r.callable,r.delegationSelector)}function I(t){return t=t.replace(y,""),T[t]||t}const N={on(t,e,i,n){S(t,e,i,n,!1)},one(t,e,i,n){S(t,e,i,n,!0)},off(t,e,i,n){if("string"!=typeof e||!t)return;const[s,o,r]=L(e,i,n),a=r!==e,l=x(t),c=l[r]||{},h=e.startsWith(".");if(void 0===o){if(h)for(const i of Object.keys(l))$(t,l,i,e.slice(1));for(const[i,n]of Object.entries(c)){const s=i.replace(w,"");a&&!e.includes(s)||D(t,l,r,n.callable,n.delegationSelector)}}else{if(!Object.keys(c).length)return;D(t,l,r,o,s?i:null)}},trigger(t,e,i){if("string"!=typeof e||!t)return null;const n=u();let s=null,o=!0,r=!0,a=!1;e!==I(e)&&n&&(s=n.Event(e,i),n(t).trigger(s),o=!s.isPropagationStopped(),r=!s.isImmediatePropagationStopped(),a=s.isDefaultPrevented());const l=P(new Event(e,{bubbles:o,cancelable:!0}),i);return a&&l.preventDefault(),r&&t.dispatchEvent(l),l.defaultPrevented&&s&&s.preventDefault(),l}};function P(t,e={}){for(const[i,n]of Object.entries(e))try{t[i]=n}catch(e){Object.defineProperty(t,i,{configurable:!0,get:()=>n})}return t}function j(t){if("true"===t)return!0;if("false"===t)return!1;if(t===Number(t).toString())return Number(t);if(""===t||"null"===t)return null;if("string"!=typeof t)return t;try{return JSON.parse(decodeURIComponent(t))}catch(e){return t}}function M(t){return t.replace(/[A-Z]/g,(t=>`-${t.toLowerCase()}`))}const F={setDataAttribute(t,e,i){t.setAttribute(`data-bs-${M(e)}`,i)},removeDataAttribute(t,e){t.removeAttribute(`data-bs-${M(e)}`)},getDataAttributes(t){if(!t)return{};const e={},i=Object.keys(t.dataset).filter((t=>t.startsWith("bs")&&!t.startsWith("bsConfig")));for(const n of i){let i=n.replace(/^bs/,"");i=i.charAt(0).toLowerCase()+i.slice(1),e[i]=j(t.dataset[n])}return e},getDataAttribute:(t,e)=>j(t.getAttribute(`data-bs-${M(e)}`))};class H{static get Default(){return{}}static get DefaultType(){return{}}static get NAME(){throw new Error('You have to implement the static method "NAME", for each component!')}_getConfig(t){return t=this._mergeConfigObj(t),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}_configAfterMerge(t){return t}_mergeConfigObj(t,e){const i=o(e)?F.getDataAttribute(e,"config"):{};return{...this.constructor.Default,..."object"==typeof i?i:{},...o(e)?F.getDataAttributes(e):{},..."object"==typeof t?t:{}}}_typeCheckConfig(t,e=this.constructor.DefaultType){for(const[n,s]of Object.entries(e)){const e=t[n],r=o(e)?"element":null==(i=e)?`${i}`:Object.prototype.toString.call(i).match(/\s([a-z]+)/i)[1].toLowerCase();if(!new RegExp(s).test(r))throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option "${n}" provided type "${r}" but expected type "${s}".`)}var i}}class W extends H{constructor(t,i){super(),(t=r(t))&&(this._element=t,this._config=this._getConfig(i),e.set(this._element,this.constructor.DATA_KEY,this))}dispose(){e.remove(this._element,this.constructor.DATA_KEY),N.off(this._element,this.constructor.EVENT_KEY);for(const t of Object.getOwnPropertyNames(this))this[t]=null}_queueCallback(t,e,i=!0){_(t,e,i)}_getConfig(t){return t=this._mergeConfigObj(t,this._element),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}static getInstance(t){return e.get(r(t),this.DATA_KEY)}static getOrCreateInstance(t,e={}){return this.getInstance(t)||new this(t,"object"==typeof e?e:null)}static get VERSION(){return"5.3.6"}static get DATA_KEY(){return`bs.${this.NAME}`}static get EVENT_KEY(){return`.${this.DATA_KEY}`}static eventName(t){return`${t}${this.EVENT_KEY}`}}const B=t=>{let e=t.getAttribute("data-bs-target");if(!e||"#"===e){let i=t.getAttribute("href");if(!i||!i.includes("#")&&!i.startsWith("."))return null;i.includes("#")&&!i.startsWith("#")&&(i=`#${i.split("#")[1]}`),e=i&&"#"!==i?i.trim():null}return e?e.split(",").map((t=>n(t))).join(","):null},z={find:(t,e=document.documentElement)=>[].concat(...Element.prototype.querySelectorAll.call(e,t)),findOne:(t,e=document.documentElement)=>Element.prototype.querySelector.call(e,t),children:(t,e)=>[].concat(...t.children).filter((t=>t.matches(e))),parents(t,e){const i=[];let n=t.parentNode.closest(e);for(;n;)i.push(n),n=n.parentNode.closest(e);return i},prev(t,e){let i=t.previousElementSibling;for(;i;){if(i.matches(e))return[i];i=i.previousElementSibling}return[]},next(t,e){let i=t.nextElementSibling;for(;i;){if(i.matches(e))return[i];i=i.nextElementSibling}return[]},focusableChildren(t){const e=["a","button","input","textarea","select","details","[tabindex]",'[contenteditable="true"]'].map((t=>`${t}:not([tabindex^="-"])`)).join(",");return this.find(e,t).filter((t=>!l(t)&&a(t)))},getSelectorFromElement(t){const e=B(t);return e&&z.findOne(e)?e:null},getElementFromSelector(t){const e=B(t);return e?z.findOne(e):null},getMultipleElementsFromSelector(t){const e=B(t);return e?z.find(e):[]}},R=(t,e="hide")=>{const i=`click.dismiss${t.EVENT_KEY}`,n=t.NAME;N.on(document,i,`[data-bs-dismiss="${n}"]`,(function(i){if(["A","AREA"].includes(this.tagName)&&i.preventDefault(),l(this))return;const s=z.getElementFromSelector(this)||this.closest(`.${n}`);t.getOrCreateInstance(s)[e]()}))},q=".bs.alert",V=`close${q}`,K=`closed${q}`;class Q extends W{static get NAME(){return"alert"}close(){if(N.trigger(this._element,V).defaultPrevented)return;this._element.classList.remove("show");const t=this._element.classList.contains("fade");this._queueCallback((()=>this._destroyElement()),this._element,t)}_destroyElement(){this._element.remove(),N.trigger(this._element,K),this.dispose()}static jQueryInterface(t){return this.each((function(){const e=Q.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}R(Q,"close"),m(Q);const X='[data-bs-toggle="button"]';class Y extends W{static get NAME(){return"button"}toggle(){this._element.setAttribute("aria-pressed",this._element.classList.toggle("active"))}static jQueryInterface(t){return this.each((function(){const e=Y.getOrCreateInstance(this);"toggle"===t&&e[t]()}))}}N.on(document,"click.bs.button.data-api",X,(t=>{t.preventDefault();const e=t.target.closest(X);Y.getOrCreateInstance(e).toggle()})),m(Y);const U=".bs.swipe",G=`touchstart${U}`,J=`touchmove${U}`,Z=`touchend${U}`,tt=`pointerdown${U}`,et=`pointerup${U}`,it={endCallback:null,leftCallback:null,rightCallback:null},nt={endCallback:"(function|null)",leftCallback:"(function|null)",rightCallback:"(function|null)"};class st extends H{constructor(t,e){super(),this._element=t,t&&st.isSupported()&&(this._config=this._getConfig(e),this._deltaX=0,this._supportPointerEvents=Boolean(window.PointerEvent),this._initEvents())}static get Default(){return it}static get DefaultType(){return nt}static get NAME(){return"swipe"}dispose(){N.off(this._element,U)}_start(t){this._supportPointerEvents?this._eventIsPointerPenTouch(t)&&(this._deltaX=t.clientX):this._deltaX=t.touches[0].clientX}_end(t){this._eventIsPointerPenTouch(t)&&(this._deltaX=t.clientX-this._deltaX),this._handleSwipe(),g(this._config.endCallback)}_move(t){this._deltaX=t.touches&&t.touches.length>1?0:t.touches[0].clientX-this._deltaX}_handleSwipe(){const t=Math.abs(this._deltaX);if(t<=40)return;const e=t/this._deltaX;this._deltaX=0,e&&g(e>0?this._config.rightCallback:this._config.leftCallback)}_initEvents(){this._supportPointerEvents?(N.on(this._element,tt,(t=>this._start(t))),N.on(this._element,et,(t=>this._end(t))),this._element.classList.add("pointer-event")):(N.on(this._element,G,(t=>this._start(t))),N.on(this._element,J,(t=>this._move(t))),N.on(this._element,Z,(t=>this._end(t))))}_eventIsPointerPenTouch(t){return this._supportPointerEvents&&("pen"===t.pointerType||"touch"===t.pointerType)}static isSupported(){return"ontouchstart"in document.documentElement||navigator.maxTouchPoints>0}}const ot=".bs.carousel",rt=".data-api",at="ArrowLeft",lt="ArrowRight",ct="next",ht="prev",dt="left",ut="right",ft=`slide${ot}`,pt=`slid${ot}`,mt=`keydown${ot}`,gt=`mouseenter${ot}`,_t=`mouseleave${ot}`,bt=`dragstart${ot}`,vt=`load${ot}${rt}`,yt=`click${ot}${rt}`,wt="carousel",At="active",Et=".active",Tt=".carousel-item",Ct=Et+Tt,Ot={[at]:ut,[lt]:dt},xt={interval:5e3,keyboard:!0,pause:"hover",ride:!1,touch:!0,wrap:!0},kt={interval:"(number|boolean)",keyboard:"boolean",pause:"(string|boolean)",ride:"(boolean|string)",touch:"boolean",wrap:"boolean"};class Lt extends W{constructor(t,e){super(t,e),this._interval=null,this._activeElement=null,this._isSliding=!1,this.touchTimeout=null,this._swipeHelper=null,this._indicatorsElement=z.findOne(".carousel-indicators",this._element),this._addEventListeners(),this._config.ride===wt&&this.cycle()}static get Default(){return xt}static get DefaultType(){return kt}static get NAME(){return"carousel"}next(){this._slide(ct)}nextWhenVisible(){!document.hidden&&a(this._element)&&this.next()}prev(){this._slide(ht)}pause(){this._isSliding&&s(this._element),this._clearInterval()}cycle(){this._clearInterval(),this._updateInterval(),this._interval=setInterval((()=>this.nextWhenVisible()),this._config.interval)}_maybeEnableCycle(){this._config.ride&&(this._isSliding?N.one(this._element,pt,(()=>this.cycle())):this.cycle())}to(t){const e=this._getItems();if(t>e.length-1||t<0)return;if(this._isSliding)return void N.one(this._element,pt,(()=>this.to(t)));const i=this._getItemIndex(this._getActive());if(i===t)return;const n=t>i?ct:ht;this._slide(n,e[t])}dispose(){this._swipeHelper&&this._swipeHelper.dispose(),super.dispose()}_configAfterMerge(t){return t.defaultInterval=t.interval,t}_addEventListeners(){this._config.keyboard&&N.on(this._element,mt,(t=>this._keydown(t))),"hover"===this._config.pause&&(N.on(this._element,gt,(()=>this.pause())),N.on(this._element,_t,(()=>this._maybeEnableCycle()))),this._config.touch&&st.isSupported()&&this._addTouchEventListeners()}_addTouchEventListeners(){for(const t of z.find(".carousel-item img",this._element))N.on(t,bt,(t=>t.preventDefault()));const t={leftCallback:()=>this._slide(this._directionToOrder(dt)),rightCallback:()=>this._slide(this._directionToOrder(ut)),endCallback:()=>{"hover"===this._config.pause&&(this.pause(),this.touchTimeout&&clearTimeout(this.touchTimeout),this.touchTimeout=setTimeout((()=>this._maybeEnableCycle()),500+this._config.interval))}};this._swipeHelper=new st(this._element,t)}_keydown(t){if(/input|textarea/i.test(t.target.tagName))return;const e=Ot[t.key];e&&(t.preventDefault(),this._slide(this._directionToOrder(e)))}_getItemIndex(t){return this._getItems().indexOf(t)}_setActiveIndicatorElement(t){if(!this._indicatorsElement)return;const e=z.findOne(Et,this._indicatorsElement);e.classList.remove(At),e.removeAttribute("aria-current");const i=z.findOne(`[data-bs-slide-to="${t}"]`,this._indicatorsElement);i&&(i.classList.add(At),i.setAttribute("aria-current","true"))}_updateInterval(){const t=this._activeElement||this._getActive();if(!t)return;const e=Number.parseInt(t.getAttribute("data-bs-interval"),10);this._config.interval=e||this._config.defaultInterval}_slide(t,e=null){if(this._isSliding)return;const i=this._getActive(),n=t===ct,s=e||b(this._getItems(),i,n,this._config.wrap);if(s===i)return;const o=this._getItemIndex(s),r=e=>N.trigger(this._element,e,{relatedTarget:s,direction:this._orderToDirection(t),from:this._getItemIndex(i),to:o});if(r(ft).defaultPrevented)return;if(!i||!s)return;const a=Boolean(this._interval);this.pause(),this._isSliding=!0,this._setActiveIndicatorElement(o),this._activeElement=s;const l=n?"carousel-item-start":"carousel-item-end",c=n?"carousel-item-next":"carousel-item-prev";s.classList.add(c),d(s),i.classList.add(l),s.classList.add(l),this._queueCallback((()=>{s.classList.remove(l,c),s.classList.add(At),i.classList.remove(At,c,l),this._isSliding=!1,r(pt)}),i,this._isAnimated()),a&&this.cycle()}_isAnimated(){return this._element.classList.contains("slide")}_getActive(){return z.findOne(Ct,this._element)}_getItems(){return z.find(Tt,this._element)}_clearInterval(){this._interval&&(clearInterval(this._interval),this._interval=null)}_directionToOrder(t){return p()?t===dt?ht:ct:t===dt?ct:ht}_orderToDirection(t){return p()?t===ht?dt:ut:t===ht?ut:dt}static jQueryInterface(t){return this.each((function(){const e=Lt.getOrCreateInstance(this,t);if("number"!=typeof t){if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}else e.to(t)}))}}N.on(document,yt,"[data-bs-slide], [data-bs-slide-to]",(function(t){const e=z.getElementFromSelector(this);if(!e||!e.classList.contains(wt))return;t.preventDefault();const i=Lt.getOrCreateInstance(e),n=this.getAttribute("data-bs-slide-to");return n?(i.to(n),void i._maybeEnableCycle()):"next"===F.getDataAttribute(this,"slide")?(i.next(),void i._maybeEnableCycle()):(i.prev(),void i._maybeEnableCycle())})),N.on(window,vt,(()=>{const t=z.find('[data-bs-ride="carousel"]');for(const e of t)Lt.getOrCreateInstance(e)})),m(Lt);const St=".bs.collapse",Dt=`show${St}`,$t=`shown${St}`,It=`hide${St}`,Nt=`hidden${St}`,Pt=`click${St}.data-api`,jt="show",Mt="collapse",Ft="collapsing",Ht=`:scope .${Mt} .${Mt}`,Wt='[data-bs-toggle="collapse"]',Bt={parent:null,toggle:!0},zt={parent:"(null|element)",toggle:"boolean"};class Rt extends W{constructor(t,e){super(t,e),this._isTransitioning=!1,this._triggerArray=[];const i=z.find(Wt);for(const t of i){const e=z.getSelectorFromElement(t),i=z.find(e).filter((t=>t===this._element));null!==e&&i.length&&this._triggerArray.push(t)}this._initializeChildren(),this._config.parent||this._addAriaAndCollapsedClass(this._triggerArray,this._isShown()),this._config.toggle&&this.toggle()}static get Default(){return Bt}static get DefaultType(){return zt}static get NAME(){return"collapse"}toggle(){this._isShown()?this.hide():this.show()}show(){if(this._isTransitioning||this._isShown())return;let t=[];if(this._config.parent&&(t=this._getFirstLevelChildren(".collapse.show, .collapse.collapsing").filter((t=>t!==this._element)).map((t=>Rt.getOrCreateInstance(t,{toggle:!1})))),t.length&&t[0]._isTransitioning)return;if(N.trigger(this._element,Dt).defaultPrevented)return;for(const e of t)e.hide();const e=this._getDimension();this._element.classList.remove(Mt),this._element.classList.add(Ft),this._element.style[e]=0,this._addAriaAndCollapsedClass(this._triggerArray,!0),this._isTransitioning=!0;const i=`scroll${e[0].toUpperCase()+e.slice(1)}`;this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(Ft),this._element.classList.add(Mt,jt),this._element.style[e]="",N.trigger(this._element,$t)}),this._element,!0),this._element.style[e]=`${this._element[i]}px`}hide(){if(this._isTransitioning||!this._isShown())return;if(N.trigger(this._element,It).defaultPrevented)return;const t=this._getDimension();this._element.style[t]=`${this._element.getBoundingClientRect()[t]}px`,d(this._element),this._element.classList.add(Ft),this._element.classList.remove(Mt,jt);for(const t of this._triggerArray){const e=z.getElementFromSelector(t);e&&!this._isShown(e)&&this._addAriaAndCollapsedClass([t],!1)}this._isTransitioning=!0,this._element.style[t]="",this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(Ft),this._element.classList.add(Mt),N.trigger(this._element,Nt)}),this._element,!0)}_isShown(t=this._element){return t.classList.contains(jt)}_configAfterMerge(t){return t.toggle=Boolean(t.toggle),t.parent=r(t.parent),t}_getDimension(){return this._element.classList.contains("collapse-horizontal")?"width":"height"}_initializeChildren(){if(!this._config.parent)return;const t=this._getFirstLevelChildren(Wt);for(const e of t){const t=z.getElementFromSelector(e);t&&this._addAriaAndCollapsedClass([e],this._isShown(t))}}_getFirstLevelChildren(t){const e=z.find(Ht,this._config.parent);return z.find(t,this._config.parent).filter((t=>!e.includes(t)))}_addAriaAndCollapsedClass(t,e){if(t.length)for(const i of t)i.classList.toggle("collapsed",!e),i.setAttribute("aria-expanded",e)}static jQueryInterface(t){const e={};return"string"==typeof t&&/show|hide/.test(t)&&(e.toggle=!1),this.each((function(){const i=Rt.getOrCreateInstance(this,e);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t]()}}))}}N.on(document,Pt,Wt,(function(t){("A"===t.target.tagName||t.delegateTarget&&"A"===t.delegateTarget.tagName)&&t.preventDefault();for(const t of z.getMultipleElementsFromSelector(this))Rt.getOrCreateInstance(t,{toggle:!1}).toggle()})),m(Rt);var qt="top",Vt="bottom",Kt="right",Qt="left",Xt="auto",Yt=[qt,Vt,Kt,Qt],Ut="start",Gt="end",Jt="clippingParents",Zt="viewport",te="popper",ee="reference",ie=Yt.reduce((function(t,e){return t.concat([e+"-"+Ut,e+"-"+Gt])}),[]),ne=[].concat(Yt,[Xt]).reduce((function(t,e){return t.concat([e,e+"-"+Ut,e+"-"+Gt])}),[]),se="beforeRead",oe="read",re="afterRead",ae="beforeMain",le="main",ce="afterMain",he="beforeWrite",de="write",ue="afterWrite",fe=[se,oe,re,ae,le,ce,he,de,ue];function pe(t){return t?(t.nodeName||"").toLowerCase():null}function me(t){if(null==t)return window;if("[object Window]"!==t.toString()){var e=t.ownerDocument;return e&&e.defaultView||window}return t}function ge(t){return t instanceof me(t).Element||t instanceof Element}function _e(t){return t instanceof me(t).HTMLElement||t instanceof HTMLElement}function be(t){return"undefined"!=typeof ShadowRoot&&(t instanceof me(t).ShadowRoot||t instanceof ShadowRoot)}const ve={name:"applyStyles",enabled:!0,phase:"write",fn:function(t){var e=t.state;Object.keys(e.elements).forEach((function(t){var i=e.styles[t]||{},n=e.attributes[t]||{},s=e.elements[t];_e(s)&&pe(s)&&(Object.assign(s.style,i),Object.keys(n).forEach((function(t){var e=n[t];!1===e?s.removeAttribute(t):s.setAttribute(t,!0===e?"":e)})))}))},effect:function(t){var e=t.state,i={popper:{position:e.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(e.elements.popper.style,i.popper),e.styles=i,e.elements.arrow&&Object.assign(e.elements.arrow.style,i.arrow),function(){Object.keys(e.elements).forEach((function(t){var n=e.elements[t],s=e.attributes[t]||{},o=Object.keys(e.styles.hasOwnProperty(t)?e.styles[t]:i[t]).reduce((function(t,e){return t[e]="",t}),{});_e(n)&&pe(n)&&(Object.assign(n.style,o),Object.keys(s).forEach((function(t){n.removeAttribute(t)})))}))}},requires:["computeStyles"]};function ye(t){return t.split("-")[0]}var we=Math.max,Ae=Math.min,Ee=Math.round;function Te(){var t=navigator.userAgentData;return null!=t&&t.brands&&Array.isArray(t.brands)?t.brands.map((function(t){return t.brand+"/"+t.version})).join(" "):navigator.userAgent}function Ce(){return!/^((?!chrome|android).)*safari/i.test(Te())}function Oe(t,e,i){void 0===e&&(e=!1),void 0===i&&(i=!1);var n=t.getBoundingClientRect(),s=1,o=1;e&&_e(t)&&(s=t.offsetWidth>0&&Ee(n.width)/t.offsetWidth||1,o=t.offsetHeight>0&&Ee(n.height)/t.offsetHeight||1);var r=(ge(t)?me(t):window).visualViewport,a=!Ce()&&i,l=(n.left+(a&&r?r.offsetLeft:0))/s,c=(n.top+(a&&r?r.offsetTop:0))/o,h=n.width/s,d=n.height/o;return{width:h,height:d,top:c,right:l+h,bottom:c+d,left:l,x:l,y:c}}function xe(t){var e=Oe(t),i=t.offsetWidth,n=t.offsetHeight;return Math.abs(e.width-i)<=1&&(i=e.width),Math.abs(e.height-n)<=1&&(n=e.height),{x:t.offsetLeft,y:t.offsetTop,width:i,height:n}}function ke(t,e){var i=e.getRootNode&&e.getRootNode();if(t.contains(e))return!0;if(i&&be(i)){var n=e;do{if(n&&t.isSameNode(n))return!0;n=n.parentNode||n.host}while(n)}return!1}function Le(t){return me(t).getComputedStyle(t)}function Se(t){return["table","td","th"].indexOf(pe(t))>=0}function De(t){return((ge(t)?t.ownerDocument:t.document)||window.document).documentElement}function $e(t){return"html"===pe(t)?t:t.assignedSlot||t.parentNode||(be(t)?t.host:null)||De(t)}function Ie(t){return _e(t)&&"fixed"!==Le(t).position?t.offsetParent:null}function Ne(t){for(var e=me(t),i=Ie(t);i&&Se(i)&&"static"===Le(i).position;)i=Ie(i);return i&&("html"===pe(i)||"body"===pe(i)&&"static"===Le(i).position)?e:i||function(t){var e=/firefox/i.test(Te());if(/Trident/i.test(Te())&&_e(t)&&"fixed"===Le(t).position)return null;var i=$e(t);for(be(i)&&(i=i.host);_e(i)&&["html","body"].indexOf(pe(i))<0;){var n=Le(i);if("none"!==n.transform||"none"!==n.perspective||"paint"===n.contain||-1!==["transform","perspective"].indexOf(n.willChange)||e&&"filter"===n.willChange||e&&n.filter&&"none"!==n.filter)return i;i=i.parentNode}return null}(t)||e}function Pe(t){return["top","bottom"].indexOf(t)>=0?"x":"y"}function je(t,e,i){return we(t,Ae(e,i))}function Me(t){return Object.assign({},{top:0,right:0,bottom:0,left:0},t)}function Fe(t,e){return e.reduce((function(e,i){return e[i]=t,e}),{})}const He={name:"arrow",enabled:!0,phase:"main",fn:function(t){var e,i=t.state,n=t.name,s=t.options,o=i.elements.arrow,r=i.modifiersData.popperOffsets,a=ye(i.placement),l=Pe(a),c=[Qt,Kt].indexOf(a)>=0?"height":"width";if(o&&r){var h=function(t,e){return Me("number"!=typeof(t="function"==typeof t?t(Object.assign({},e.rects,{placement:e.placement})):t)?t:Fe(t,Yt))}(s.padding,i),d=xe(o),u="y"===l?qt:Qt,f="y"===l?Vt:Kt,p=i.rects.reference[c]+i.rects.reference[l]-r[l]-i.rects.popper[c],m=r[l]-i.rects.reference[l],g=Ne(o),_=g?"y"===l?g.clientHeight||0:g.clientWidth||0:0,b=p/2-m/2,v=h[u],y=_-d[c]-h[f],w=_/2-d[c]/2+b,A=je(v,w,y),E=l;i.modifiersData[n]=((e={})[E]=A,e.centerOffset=A-w,e)}},effect:function(t){var e=t.state,i=t.options.element,n=void 0===i?"[data-popper-arrow]":i;null!=n&&("string"!=typeof n||(n=e.elements.popper.querySelector(n)))&&ke(e.elements.popper,n)&&(e.elements.arrow=n)},requires:["popperOffsets"],requiresIfExists:["preventOverflow"]};function We(t){return t.split("-")[1]}var Be={top:"auto",right:"auto",bottom:"auto",left:"auto"};function ze(t){var e,i=t.popper,n=t.popperRect,s=t.placement,o=t.variation,r=t.offsets,a=t.position,l=t.gpuAcceleration,c=t.adaptive,h=t.roundOffsets,d=t.isFixed,u=r.x,f=void 0===u?0:u,p=r.y,m=void 0===p?0:p,g="function"==typeof h?h({x:f,y:m}):{x:f,y:m};f=g.x,m=g.y;var _=r.hasOwnProperty("x"),b=r.hasOwnProperty("y"),v=Qt,y=qt,w=window;if(c){var A=Ne(i),E="clientHeight",T="clientWidth";A===me(i)&&"static"!==Le(A=De(i)).position&&"absolute"===a&&(E="scrollHeight",T="scrollWidth"),(s===qt||(s===Qt||s===Kt)&&o===Gt)&&(y=Vt,m-=(d&&A===w&&w.visualViewport?w.visualViewport.height:A[E])-n.height,m*=l?1:-1),s!==Qt&&(s!==qt&&s!==Vt||o!==Gt)||(v=Kt,f-=(d&&A===w&&w.visualViewport?w.visualViewport.width:A[T])-n.width,f*=l?1:-1)}var C,O=Object.assign({position:a},c&&Be),x=!0===h?function(t,e){var i=t.x,n=t.y,s=e.devicePixelRatio||1;return{x:Ee(i*s)/s||0,y:Ee(n*s)/s||0}}({x:f,y:m},me(i)):{x:f,y:m};return f=x.x,m=x.y,l?Object.assign({},O,((C={})[y]=b?"0":"",C[v]=_?"0":"",C.transform=(w.devicePixelRatio||1)<=1?"translate("+f+"px, "+m+"px)":"translate3d("+f+"px, "+m+"px, 0)",C)):Object.assign({},O,((e={})[y]=b?m+"px":"",e[v]=_?f+"px":"",e.transform="",e))}const Re={name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:function(t){var e=t.state,i=t.options,n=i.gpuAcceleration,s=void 0===n||n,o=i.adaptive,r=void 0===o||o,a=i.roundOffsets,l=void 0===a||a,c={placement:ye(e.placement),variation:We(e.placement),popper:e.elements.popper,popperRect:e.rects.popper,gpuAcceleration:s,isFixed:"fixed"===e.options.strategy};null!=e.modifiersData.popperOffsets&&(e.styles.popper=Object.assign({},e.styles.popper,ze(Object.assign({},c,{offsets:e.modifiersData.popperOffsets,position:e.options.strategy,adaptive:r,roundOffsets:l})))),null!=e.modifiersData.arrow&&(e.styles.arrow=Object.assign({},e.styles.arrow,ze(Object.assign({},c,{offsets:e.modifiersData.arrow,position:"absolute",adaptive:!1,roundOffsets:l})))),e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-placement":e.placement})},data:{}};var qe={passive:!0};const Ve={name:"eventListeners",enabled:!0,phase:"write",fn:function(){},effect:function(t){var e=t.state,i=t.instance,n=t.options,s=n.scroll,o=void 0===s||s,r=n.resize,a=void 0===r||r,l=me(e.elements.popper),c=[].concat(e.scrollParents.reference,e.scrollParents.popper);return o&&c.forEach((function(t){t.addEventListener("scroll",i.update,qe)})),a&&l.addEventListener("resize",i.update,qe),function(){o&&c.forEach((function(t){t.removeEventListener("scroll",i.update,qe)})),a&&l.removeEventListener("resize",i.update,qe)}},data:{}};var Ke={left:"right",right:"left",bottom:"top",top:"bottom"};function Qe(t){return t.replace(/left|right|bottom|top/g,(function(t){return Ke[t]}))}var Xe={start:"end",end:"start"};function Ye(t){return t.replace(/start|end/g,(function(t){return Xe[t]}))}function Ue(t){var e=me(t);return{scrollLeft:e.pageXOffset,scrollTop:e.pageYOffset}}function Ge(t){return Oe(De(t)).left+Ue(t).scrollLeft}function Je(t){var e=Le(t),i=e.overflow,n=e.overflowX,s=e.overflowY;return/auto|scroll|overlay|hidden/.test(i+s+n)}function Ze(t){return["html","body","#document"].indexOf(pe(t))>=0?t.ownerDocument.body:_e(t)&&Je(t)?t:Ze($e(t))}function ti(t,e){var i;void 0===e&&(e=[]);var n=Ze(t),s=n===(null==(i=t.ownerDocument)?void 0:i.body),o=me(n),r=s?[o].concat(o.visualViewport||[],Je(n)?n:[]):n,a=e.concat(r);return s?a:a.concat(ti($e(r)))}function ei(t){return Object.assign({},t,{left:t.x,top:t.y,right:t.x+t.width,bottom:t.y+t.height})}function ii(t,e,i){return e===Zt?ei(function(t,e){var i=me(t),n=De(t),s=i.visualViewport,o=n.clientWidth,r=n.clientHeight,a=0,l=0;if(s){o=s.width,r=s.height;var c=Ce();(c||!c&&"fixed"===e)&&(a=s.offsetLeft,l=s.offsetTop)}return{width:o,height:r,x:a+Ge(t),y:l}}(t,i)):ge(e)?function(t,e){var i=Oe(t,!1,"fixed"===e);return i.top=i.top+t.clientTop,i.left=i.left+t.clientLeft,i.bottom=i.top+t.clientHeight,i.right=i.left+t.clientWidth,i.width=t.clientWidth,i.height=t.clientHeight,i.x=i.left,i.y=i.top,i}(e,i):ei(function(t){var e,i=De(t),n=Ue(t),s=null==(e=t.ownerDocument)?void 0:e.body,o=we(i.scrollWidth,i.clientWidth,s?s.scrollWidth:0,s?s.clientWidth:0),r=we(i.scrollHeight,i.clientHeight,s?s.scrollHeight:0,s?s.clientHeight:0),a=-n.scrollLeft+Ge(t),l=-n.scrollTop;return"rtl"===Le(s||i).direction&&(a+=we(i.clientWidth,s?s.clientWidth:0)-o),{width:o,height:r,x:a,y:l}}(De(t)))}function ni(t){var e,i=t.reference,n=t.element,s=t.placement,o=s?ye(s):null,r=s?We(s):null,a=i.x+i.width/2-n.width/2,l=i.y+i.height/2-n.height/2;switch(o){case qt:e={x:a,y:i.y-n.height};break;case Vt:e={x:a,y:i.y+i.height};break;case Kt:e={x:i.x+i.width,y:l};break;case Qt:e={x:i.x-n.width,y:l};break;default:e={x:i.x,y:i.y}}var c=o?Pe(o):null;if(null!=c){var h="y"===c?"height":"width";switch(r){case Ut:e[c]=e[c]-(i[h]/2-n[h]/2);break;case Gt:e[c]=e[c]+(i[h]/2-n[h]/2)}}return e}function si(t,e){void 0===e&&(e={});var i=e,n=i.placement,s=void 0===n?t.placement:n,o=i.strategy,r=void 0===o?t.strategy:o,a=i.boundary,l=void 0===a?Jt:a,c=i.rootBoundary,h=void 0===c?Zt:c,d=i.elementContext,u=void 0===d?te:d,f=i.altBoundary,p=void 0!==f&&f,m=i.padding,g=void 0===m?0:m,_=Me("number"!=typeof g?g:Fe(g,Yt)),b=u===te?ee:te,v=t.rects.popper,y=t.elements[p?b:u],w=function(t,e,i,n){var s="clippingParents"===e?function(t){var e=ti($e(t)),i=["absolute","fixed"].indexOf(Le(t).position)>=0&&_e(t)?Ne(t):t;return ge(i)?e.filter((function(t){return ge(t)&&ke(t,i)&&"body"!==pe(t)})):[]}(t):[].concat(e),o=[].concat(s,[i]),r=o[0],a=o.reduce((function(e,i){var s=ii(t,i,n);return e.top=we(s.top,e.top),e.right=Ae(s.right,e.right),e.bottom=Ae(s.bottom,e.bottom),e.left=we(s.left,e.left),e}),ii(t,r,n));return a.width=a.right-a.left,a.height=a.bottom-a.top,a.x=a.left,a.y=a.top,a}(ge(y)?y:y.contextElement||De(t.elements.popper),l,h,r),A=Oe(t.elements.reference),E=ni({reference:A,element:v,placement:s}),T=ei(Object.assign({},v,E)),C=u===te?T:A,O={top:w.top-C.top+_.top,bottom:C.bottom-w.bottom+_.bottom,left:w.left-C.left+_.left,right:C.right-w.right+_.right},x=t.modifiersData.offset;if(u===te&&x){var k=x[s];Object.keys(O).forEach((function(t){var e=[Kt,Vt].indexOf(t)>=0?1:-1,i=[qt,Vt].indexOf(t)>=0?"y":"x";O[t]+=k[i]*e}))}return O}function oi(t,e){void 0===e&&(e={});var i=e,n=i.placement,s=i.boundary,o=i.rootBoundary,r=i.padding,a=i.flipVariations,l=i.allowedAutoPlacements,c=void 0===l?ne:l,h=We(n),d=h?a?ie:ie.filter((function(t){return We(t)===h})):Yt,u=d.filter((function(t){return c.indexOf(t)>=0}));0===u.length&&(u=d);var f=u.reduce((function(e,i){return e[i]=si(t,{placement:i,boundary:s,rootBoundary:o,padding:r})[ye(i)],e}),{});return Object.keys(f).sort((function(t,e){return f[t]-f[e]}))}const ri={name:"flip",enabled:!0,phase:"main",fn:function(t){var e=t.state,i=t.options,n=t.name;if(!e.modifiersData[n]._skip){for(var s=i.mainAxis,o=void 0===s||s,r=i.altAxis,a=void 0===r||r,l=i.fallbackPlacements,c=i.padding,h=i.boundary,d=i.rootBoundary,u=i.altBoundary,f=i.flipVariations,p=void 0===f||f,m=i.allowedAutoPlacements,g=e.options.placement,_=ye(g),b=l||(_!==g&&p?function(t){if(ye(t)===Xt)return[];var e=Qe(t);return[Ye(t),e,Ye(e)]}(g):[Qe(g)]),v=[g].concat(b).reduce((function(t,i){return t.concat(ye(i)===Xt?oi(e,{placement:i,boundary:h,rootBoundary:d,padding:c,flipVariations:p,allowedAutoPlacements:m}):i)}),[]),y=e.rects.reference,w=e.rects.popper,A=new Map,E=!0,T=v[0],C=0;C=0,S=L?"width":"height",D=si(e,{placement:O,boundary:h,rootBoundary:d,altBoundary:u,padding:c}),$=L?k?Kt:Qt:k?Vt:qt;y[S]>w[S]&&($=Qe($));var I=Qe($),N=[];if(o&&N.push(D[x]<=0),a&&N.push(D[$]<=0,D[I]<=0),N.every((function(t){return t}))){T=O,E=!1;break}A.set(O,N)}if(E)for(var P=function(t){var e=v.find((function(e){var i=A.get(e);if(i)return i.slice(0,t).every((function(t){return t}))}));if(e)return T=e,"break"},j=p?3:1;j>0&&"break"!==P(j);j--);e.placement!==T&&(e.modifiersData[n]._skip=!0,e.placement=T,e.reset=!0)}},requiresIfExists:["offset"],data:{_skip:!1}};function ai(t,e,i){return void 0===i&&(i={x:0,y:0}),{top:t.top-e.height-i.y,right:t.right-e.width+i.x,bottom:t.bottom-e.height+i.y,left:t.left-e.width-i.x}}function li(t){return[qt,Kt,Vt,Qt].some((function(e){return t[e]>=0}))}const ci={name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:function(t){var e=t.state,i=t.name,n=e.rects.reference,s=e.rects.popper,o=e.modifiersData.preventOverflow,r=si(e,{elementContext:"reference"}),a=si(e,{altBoundary:!0}),l=ai(r,n),c=ai(a,s,o),h=li(l),d=li(c);e.modifiersData[i]={referenceClippingOffsets:l,popperEscapeOffsets:c,isReferenceHidden:h,hasPopperEscaped:d},e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-reference-hidden":h,"data-popper-escaped":d})}},hi={name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:function(t){var e=t.state,i=t.options,n=t.name,s=i.offset,o=void 0===s?[0,0]:s,r=ne.reduce((function(t,i){return t[i]=function(t,e,i){var n=ye(t),s=[Qt,qt].indexOf(n)>=0?-1:1,o="function"==typeof i?i(Object.assign({},e,{placement:t})):i,r=o[0],a=o[1];return r=r||0,a=(a||0)*s,[Qt,Kt].indexOf(n)>=0?{x:a,y:r}:{x:r,y:a}}(i,e.rects,o),t}),{}),a=r[e.placement],l=a.x,c=a.y;null!=e.modifiersData.popperOffsets&&(e.modifiersData.popperOffsets.x+=l,e.modifiersData.popperOffsets.y+=c),e.modifiersData[n]=r}},di={name:"popperOffsets",enabled:!0,phase:"read",fn:function(t){var e=t.state,i=t.name;e.modifiersData[i]=ni({reference:e.rects.reference,element:e.rects.popper,placement:e.placement})},data:{}},ui={name:"preventOverflow",enabled:!0,phase:"main",fn:function(t){var e=t.state,i=t.options,n=t.name,s=i.mainAxis,o=void 0===s||s,r=i.altAxis,a=void 0!==r&&r,l=i.boundary,c=i.rootBoundary,h=i.altBoundary,d=i.padding,u=i.tether,f=void 0===u||u,p=i.tetherOffset,m=void 0===p?0:p,g=si(e,{boundary:l,rootBoundary:c,padding:d,altBoundary:h}),_=ye(e.placement),b=We(e.placement),v=!b,y=Pe(_),w="x"===y?"y":"x",A=e.modifiersData.popperOffsets,E=e.rects.reference,T=e.rects.popper,C="function"==typeof m?m(Object.assign({},e.rects,{placement:e.placement})):m,O="number"==typeof C?{mainAxis:C,altAxis:C}:Object.assign({mainAxis:0,altAxis:0},C),x=e.modifiersData.offset?e.modifiersData.offset[e.placement]:null,k={x:0,y:0};if(A){if(o){var L,S="y"===y?qt:Qt,D="y"===y?Vt:Kt,$="y"===y?"height":"width",I=A[y],N=I+g[S],P=I-g[D],j=f?-T[$]/2:0,M=b===Ut?E[$]:T[$],F=b===Ut?-T[$]:-E[$],H=e.elements.arrow,W=f&&H?xe(H):{width:0,height:0},B=e.modifiersData["arrow#persistent"]?e.modifiersData["arrow#persistent"].padding:{top:0,right:0,bottom:0,left:0},z=B[S],R=B[D],q=je(0,E[$],W[$]),V=v?E[$]/2-j-q-z-O.mainAxis:M-q-z-O.mainAxis,K=v?-E[$]/2+j+q+R+O.mainAxis:F+q+R+O.mainAxis,Q=e.elements.arrow&&Ne(e.elements.arrow),X=Q?"y"===y?Q.clientTop||0:Q.clientLeft||0:0,Y=null!=(L=null==x?void 0:x[y])?L:0,U=I+K-Y,G=je(f?Ae(N,I+V-Y-X):N,I,f?we(P,U):P);A[y]=G,k[y]=G-I}if(a){var J,Z="x"===y?qt:Qt,tt="x"===y?Vt:Kt,et=A[w],it="y"===w?"height":"width",nt=et+g[Z],st=et-g[tt],ot=-1!==[qt,Qt].indexOf(_),rt=null!=(J=null==x?void 0:x[w])?J:0,at=ot?nt:et-E[it]-T[it]-rt+O.altAxis,lt=ot?et+E[it]+T[it]-rt-O.altAxis:st,ct=f&&ot?function(t,e,i){var n=je(t,e,i);return n>i?i:n}(at,et,lt):je(f?at:nt,et,f?lt:st);A[w]=ct,k[w]=ct-et}e.modifiersData[n]=k}},requiresIfExists:["offset"]};function fi(t,e,i){void 0===i&&(i=!1);var n,s,o=_e(e),r=_e(e)&&function(t){var e=t.getBoundingClientRect(),i=Ee(e.width)/t.offsetWidth||1,n=Ee(e.height)/t.offsetHeight||1;return 1!==i||1!==n}(e),a=De(e),l=Oe(t,r,i),c={scrollLeft:0,scrollTop:0},h={x:0,y:0};return(o||!o&&!i)&&(("body"!==pe(e)||Je(a))&&(c=(n=e)!==me(n)&&_e(n)?{scrollLeft:(s=n).scrollLeft,scrollTop:s.scrollTop}:Ue(n)),_e(e)?((h=Oe(e,!0)).x+=e.clientLeft,h.y+=e.clientTop):a&&(h.x=Ge(a))),{x:l.left+c.scrollLeft-h.x,y:l.top+c.scrollTop-h.y,width:l.width,height:l.height}}function pi(t){var e=new Map,i=new Set,n=[];function s(t){i.add(t.name),[].concat(t.requires||[],t.requiresIfExists||[]).forEach((function(t){if(!i.has(t)){var n=e.get(t);n&&s(n)}})),n.push(t)}return t.forEach((function(t){e.set(t.name,t)})),t.forEach((function(t){i.has(t.name)||s(t)})),n}var mi={placement:"bottom",modifiers:[],strategy:"absolute"};function gi(){for(var t=arguments.length,e=new Array(t),i=0;iNumber.parseInt(t,10))):"function"==typeof t?e=>t(e,this._element):t}_getPopperConfig(){const t={placement:this._getPlacement(),modifiers:[{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"offset",options:{offset:this._getOffset()}}]};return(this._inNavbar||"static"===this._config.display)&&(F.setDataAttribute(this._menu,"popper","static"),t.modifiers=[{name:"applyStyles",enabled:!1}]),{...t,...g(this._config.popperConfig,[void 0,t])}}_selectMenuItem({key:t,target:e}){const i=z.find(".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)",this._menu).filter((t=>a(t)));i.length&&b(i,e,t===Oi,!i.includes(e)).focus()}static jQueryInterface(t){return this.each((function(){const e=Ki.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}static clearMenus(t){if(2===t.button||"keyup"===t.type&&"Tab"!==t.key)return;const e=z.find(ji);for(const i of e){const e=Ki.getInstance(i);if(!e||!1===e._config.autoClose)continue;const n=t.composedPath(),s=n.includes(e._menu);if(n.includes(e._element)||"inside"===e._config.autoClose&&!s||"outside"===e._config.autoClose&&s)continue;if(e._menu.contains(t.target)&&("keyup"===t.type&&"Tab"===t.key||/input|select|option|textarea|form/i.test(t.target.tagName)))continue;const o={relatedTarget:e._element};"click"===t.type&&(o.clickEvent=t),e._completeHide(o)}}static dataApiKeydownHandler(t){const e=/input|textarea/i.test(t.target.tagName),i="Escape"===t.key,n=[Ci,Oi].includes(t.key);if(!n&&!i)return;if(e&&!i)return;t.preventDefault();const s=this.matches(Pi)?this:z.prev(this,Pi)[0]||z.next(this,Pi)[0]||z.findOne(Pi,t.delegateTarget.parentNode),o=Ki.getOrCreateInstance(s);if(n)return t.stopPropagation(),o.show(),void o._selectMenuItem(t);o._isShown()&&(t.stopPropagation(),o.hide(),s.focus())}}N.on(document,$i,Pi,Ki.dataApiKeydownHandler),N.on(document,$i,Mi,Ki.dataApiKeydownHandler),N.on(document,Di,Ki.clearMenus),N.on(document,Ii,Ki.clearMenus),N.on(document,Di,Pi,(function(t){t.preventDefault(),Ki.getOrCreateInstance(this).toggle()})),m(Ki);const Qi="backdrop",Xi="show",Yi=`mousedown.bs.${Qi}`,Ui={className:"modal-backdrop",clickCallback:null,isAnimated:!1,isVisible:!0,rootElement:"body"},Gi={className:"string",clickCallback:"(function|null)",isAnimated:"boolean",isVisible:"boolean",rootElement:"(element|string)"};class Ji extends H{constructor(t){super(),this._config=this._getConfig(t),this._isAppended=!1,this._element=null}static get Default(){return Ui}static get DefaultType(){return Gi}static get NAME(){return Qi}show(t){if(!this._config.isVisible)return void g(t);this._append();const e=this._getElement();this._config.isAnimated&&d(e),e.classList.add(Xi),this._emulateAnimation((()=>{g(t)}))}hide(t){this._config.isVisible?(this._getElement().classList.remove(Xi),this._emulateAnimation((()=>{this.dispose(),g(t)}))):g(t)}dispose(){this._isAppended&&(N.off(this._element,Yi),this._element.remove(),this._isAppended=!1)}_getElement(){if(!this._element){const t=document.createElement("div");t.className=this._config.className,this._config.isAnimated&&t.classList.add("fade"),this._element=t}return this._element}_configAfterMerge(t){return t.rootElement=r(t.rootElement),t}_append(){if(this._isAppended)return;const t=this._getElement();this._config.rootElement.append(t),N.on(t,Yi,(()=>{g(this._config.clickCallback)})),this._isAppended=!0}_emulateAnimation(t){_(t,this._getElement(),this._config.isAnimated)}}const Zi=".bs.focustrap",tn=`focusin${Zi}`,en=`keydown.tab${Zi}`,nn="backward",sn={autofocus:!0,trapElement:null},on={autofocus:"boolean",trapElement:"element"};class rn extends H{constructor(t){super(),this._config=this._getConfig(t),this._isActive=!1,this._lastTabNavDirection=null}static get Default(){return sn}static get DefaultType(){return on}static get NAME(){return"focustrap"}activate(){this._isActive||(this._config.autofocus&&this._config.trapElement.focus(),N.off(document,Zi),N.on(document,tn,(t=>this._handleFocusin(t))),N.on(document,en,(t=>this._handleKeydown(t))),this._isActive=!0)}deactivate(){this._isActive&&(this._isActive=!1,N.off(document,Zi))}_handleFocusin(t){const{trapElement:e}=this._config;if(t.target===document||t.target===e||e.contains(t.target))return;const i=z.focusableChildren(e);0===i.length?e.focus():this._lastTabNavDirection===nn?i[i.length-1].focus():i[0].focus()}_handleKeydown(t){"Tab"===t.key&&(this._lastTabNavDirection=t.shiftKey?nn:"forward")}}const an=".fixed-top, .fixed-bottom, .is-fixed, .sticky-top",ln=".sticky-top",cn="padding-right",hn="margin-right";class dn{constructor(){this._element=document.body}getWidth(){const t=document.documentElement.clientWidth;return Math.abs(window.innerWidth-t)}hide(){const t=this.getWidth();this._disableOverFlow(),this._setElementAttributes(this._element,cn,(e=>e+t)),this._setElementAttributes(an,cn,(e=>e+t)),this._setElementAttributes(ln,hn,(e=>e-t))}reset(){this._resetElementAttributes(this._element,"overflow"),this._resetElementAttributes(this._element,cn),this._resetElementAttributes(an,cn),this._resetElementAttributes(ln,hn)}isOverflowing(){return this.getWidth()>0}_disableOverFlow(){this._saveInitialAttribute(this._element,"overflow"),this._element.style.overflow="hidden"}_setElementAttributes(t,e,i){const n=this.getWidth();this._applyManipulationCallback(t,(t=>{if(t!==this._element&&window.innerWidth>t.clientWidth+n)return;this._saveInitialAttribute(t,e);const s=window.getComputedStyle(t).getPropertyValue(e);t.style.setProperty(e,`${i(Number.parseFloat(s))}px`)}))}_saveInitialAttribute(t,e){const i=t.style.getPropertyValue(e);i&&F.setDataAttribute(t,e,i)}_resetElementAttributes(t,e){this._applyManipulationCallback(t,(t=>{const i=F.getDataAttribute(t,e);null!==i?(F.removeDataAttribute(t,e),t.style.setProperty(e,i)):t.style.removeProperty(e)}))}_applyManipulationCallback(t,e){if(o(t))e(t);else for(const i of z.find(t,this._element))e(i)}}const un=".bs.modal",fn=`hide${un}`,pn=`hidePrevented${un}`,mn=`hidden${un}`,gn=`show${un}`,_n=`shown${un}`,bn=`resize${un}`,vn=`click.dismiss${un}`,yn=`mousedown.dismiss${un}`,wn=`keydown.dismiss${un}`,An=`click${un}.data-api`,En="modal-open",Tn="show",Cn="modal-static",On={backdrop:!0,focus:!0,keyboard:!0},xn={backdrop:"(boolean|string)",focus:"boolean",keyboard:"boolean"};class kn extends W{constructor(t,e){super(t,e),this._dialog=z.findOne(".modal-dialog",this._element),this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._isShown=!1,this._isTransitioning=!1,this._scrollBar=new dn,this._addEventListeners()}static get Default(){return On}static get DefaultType(){return xn}static get NAME(){return"modal"}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||this._isTransitioning||N.trigger(this._element,gn,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._isTransitioning=!0,this._scrollBar.hide(),document.body.classList.add(En),this._adjustDialog(),this._backdrop.show((()=>this._showElement(t))))}hide(){this._isShown&&!this._isTransitioning&&(N.trigger(this._element,fn).defaultPrevented||(this._isShown=!1,this._isTransitioning=!0,this._focustrap.deactivate(),this._element.classList.remove(Tn),this._queueCallback((()=>this._hideModal()),this._element,this._isAnimated())))}dispose(){N.off(window,un),N.off(this._dialog,un),this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}handleUpdate(){this._adjustDialog()}_initializeBackDrop(){return new Ji({isVisible:Boolean(this._config.backdrop),isAnimated:this._isAnimated()})}_initializeFocusTrap(){return new rn({trapElement:this._element})}_showElement(t){document.body.contains(this._element)||document.body.append(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.scrollTop=0;const e=z.findOne(".modal-body",this._dialog);e&&(e.scrollTop=0),d(this._element),this._element.classList.add(Tn),this._queueCallback((()=>{this._config.focus&&this._focustrap.activate(),this._isTransitioning=!1,N.trigger(this._element,_n,{relatedTarget:t})}),this._dialog,this._isAnimated())}_addEventListeners(){N.on(this._element,wn,(t=>{"Escape"===t.key&&(this._config.keyboard?this.hide():this._triggerBackdropTransition())})),N.on(window,bn,(()=>{this._isShown&&!this._isTransitioning&&this._adjustDialog()})),N.on(this._element,yn,(t=>{N.one(this._element,vn,(e=>{this._element===t.target&&this._element===e.target&&("static"!==this._config.backdrop?this._config.backdrop&&this.hide():this._triggerBackdropTransition())}))}))}_hideModal(){this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._backdrop.hide((()=>{document.body.classList.remove(En),this._resetAdjustments(),this._scrollBar.reset(),N.trigger(this._element,mn)}))}_isAnimated(){return this._element.classList.contains("fade")}_triggerBackdropTransition(){if(N.trigger(this._element,pn).defaultPrevented)return;const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._element.style.overflowY;"hidden"===e||this._element.classList.contains(Cn)||(t||(this._element.style.overflowY="hidden"),this._element.classList.add(Cn),this._queueCallback((()=>{this._element.classList.remove(Cn),this._queueCallback((()=>{this._element.style.overflowY=e}),this._dialog)}),this._dialog),this._element.focus())}_adjustDialog(){const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._scrollBar.getWidth(),i=e>0;if(i&&!t){const t=p()?"paddingLeft":"paddingRight";this._element.style[t]=`${e}px`}if(!i&&t){const t=p()?"paddingRight":"paddingLeft";this._element.style[t]=`${e}px`}}_resetAdjustments(){this._element.style.paddingLeft="",this._element.style.paddingRight=""}static jQueryInterface(t,e){return this.each((function(){const i=kn.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t](e)}}))}}N.on(document,An,'[data-bs-toggle="modal"]',(function(t){const e=z.getElementFromSelector(this);["A","AREA"].includes(this.tagName)&&t.preventDefault(),N.one(e,gn,(t=>{t.defaultPrevented||N.one(e,mn,(()=>{a(this)&&this.focus()}))}));const i=z.findOne(".modal.show");i&&kn.getInstance(i).hide(),kn.getOrCreateInstance(e).toggle(this)})),R(kn),m(kn);const Ln=".bs.offcanvas",Sn=".data-api",Dn=`load${Ln}${Sn}`,$n="show",In="showing",Nn="hiding",Pn=".offcanvas.show",jn=`show${Ln}`,Mn=`shown${Ln}`,Fn=`hide${Ln}`,Hn=`hidePrevented${Ln}`,Wn=`hidden${Ln}`,Bn=`resize${Ln}`,zn=`click${Ln}${Sn}`,Rn=`keydown.dismiss${Ln}`,qn={backdrop:!0,keyboard:!0,scroll:!1},Vn={backdrop:"(boolean|string)",keyboard:"boolean",scroll:"boolean"};class Kn extends W{constructor(t,e){super(t,e),this._isShown=!1,this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._addEventListeners()}static get Default(){return qn}static get DefaultType(){return Vn}static get NAME(){return"offcanvas"}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||N.trigger(this._element,jn,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._backdrop.show(),this._config.scroll||(new dn).hide(),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.classList.add(In),this._queueCallback((()=>{this._config.scroll&&!this._config.backdrop||this._focustrap.activate(),this._element.classList.add($n),this._element.classList.remove(In),N.trigger(this._element,Mn,{relatedTarget:t})}),this._element,!0))}hide(){this._isShown&&(N.trigger(this._element,Fn).defaultPrevented||(this._focustrap.deactivate(),this._element.blur(),this._isShown=!1,this._element.classList.add(Nn),this._backdrop.hide(),this._queueCallback((()=>{this._element.classList.remove($n,Nn),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._config.scroll||(new dn).reset(),N.trigger(this._element,Wn)}),this._element,!0)))}dispose(){this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}_initializeBackDrop(){const t=Boolean(this._config.backdrop);return new Ji({className:"offcanvas-backdrop",isVisible:t,isAnimated:!0,rootElement:this._element.parentNode,clickCallback:t?()=>{"static"!==this._config.backdrop?this.hide():N.trigger(this._element,Hn)}:null})}_initializeFocusTrap(){return new rn({trapElement:this._element})}_addEventListeners(){N.on(this._element,Rn,(t=>{"Escape"===t.key&&(this._config.keyboard?this.hide():N.trigger(this._element,Hn))}))}static jQueryInterface(t){return this.each((function(){const e=Kn.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}N.on(document,zn,'[data-bs-toggle="offcanvas"]',(function(t){const e=z.getElementFromSelector(this);if(["A","AREA"].includes(this.tagName)&&t.preventDefault(),l(this))return;N.one(e,Wn,(()=>{a(this)&&this.focus()}));const i=z.findOne(Pn);i&&i!==e&&Kn.getInstance(i).hide(),Kn.getOrCreateInstance(e).toggle(this)})),N.on(window,Dn,(()=>{for(const t of z.find(Pn))Kn.getOrCreateInstance(t).show()})),N.on(window,Bn,(()=>{for(const t of z.find("[aria-modal][class*=show][class*=offcanvas-]"))"fixed"!==getComputedStyle(t).position&&Kn.getOrCreateInstance(t).hide()})),R(Kn),m(Kn);const Qn={"*":["class","dir","id","lang","role",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],dd:[],div:[],dl:[],dt:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},Xn=new Set(["background","cite","href","itemtype","longdesc","poster","src","xlink:href"]),Yn=/^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i,Un=(t,e)=>{const i=t.nodeName.toLowerCase();return e.includes(i)?!Xn.has(i)||Boolean(Yn.test(t.nodeValue)):e.filter((t=>t instanceof RegExp)).some((t=>t.test(i)))},Gn={allowList:Qn,content:{},extraClass:"",html:!1,sanitize:!0,sanitizeFn:null,template:"
"},Jn={allowList:"object",content:"object",extraClass:"(string|function)",html:"boolean",sanitize:"boolean",sanitizeFn:"(null|function)",template:"string"},Zn={entry:"(string|element|function|null)",selector:"(string|element)"};class ts extends H{constructor(t){super(),this._config=this._getConfig(t)}static get Default(){return Gn}static get DefaultType(){return Jn}static get NAME(){return"TemplateFactory"}getContent(){return Object.values(this._config.content).map((t=>this._resolvePossibleFunction(t))).filter(Boolean)}hasContent(){return this.getContent().length>0}changeContent(t){return this._checkContent(t),this._config.content={...this._config.content,...t},this}toHtml(){const t=document.createElement("div");t.innerHTML=this._maybeSanitize(this._config.template);for(const[e,i]of Object.entries(this._config.content))this._setContent(t,i,e);const e=t.children[0],i=this._resolvePossibleFunction(this._config.extraClass);return i&&e.classList.add(...i.split(" ")),e}_typeCheckConfig(t){super._typeCheckConfig(t),this._checkContent(t.content)}_checkContent(t){for(const[e,i]of Object.entries(t))super._typeCheckConfig({selector:e,entry:i},Zn)}_setContent(t,e,i){const n=z.findOne(i,t);n&&((e=this._resolvePossibleFunction(e))?o(e)?this._putElementInTemplate(r(e),n):this._config.html?n.innerHTML=this._maybeSanitize(e):n.textContent=e:n.remove())}_maybeSanitize(t){return this._config.sanitize?function(t,e,i){if(!t.length)return t;if(i&&"function"==typeof i)return i(t);const n=(new window.DOMParser).parseFromString(t,"text/html"),s=[].concat(...n.body.querySelectorAll("*"));for(const t of s){const i=t.nodeName.toLowerCase();if(!Object.keys(e).includes(i)){t.remove();continue}const n=[].concat(...t.attributes),s=[].concat(e["*"]||[],e[i]||[]);for(const e of n)Un(e,s)||t.removeAttribute(e.nodeName)}return n.body.innerHTML}(t,this._config.allowList,this._config.sanitizeFn):t}_resolvePossibleFunction(t){return g(t,[void 0,this])}_putElementInTemplate(t,e){if(this._config.html)return e.innerHTML="",void e.append(t);e.textContent=t.textContent}}const es=new Set(["sanitize","allowList","sanitizeFn"]),is="fade",ns="show",ss=".tooltip-inner",os=".modal",rs="hide.bs.modal",as="hover",ls="focus",cs={AUTO:"auto",TOP:"top",RIGHT:p()?"left":"right",BOTTOM:"bottom",LEFT:p()?"right":"left"},hs={allowList:Qn,animation:!0,boundary:"clippingParents",container:!1,customClass:"",delay:0,fallbackPlacements:["top","right","bottom","left"],html:!1,offset:[0,6],placement:"top",popperConfig:null,sanitize:!0,sanitizeFn:null,selector:!1,template:'',title:"",trigger:"hover focus"},ds={allowList:"object",animation:"boolean",boundary:"(string|element)",container:"(string|element|boolean)",customClass:"(string|function)",delay:"(number|object)",fallbackPlacements:"array",html:"boolean",offset:"(array|string|function)",placement:"(string|function)",popperConfig:"(null|object|function)",sanitize:"boolean",sanitizeFn:"(null|function)",selector:"(string|boolean)",template:"string",title:"(string|element|function)",trigger:"string"};class us extends W{constructor(t,e){if(void 0===wi)throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org/docs/v2/)");super(t,e),this._isEnabled=!0,this._timeout=0,this._isHovered=null,this._activeTrigger={},this._popper=null,this._templateFactory=null,this._newContent=null,this.tip=null,this._setListeners(),this._config.selector||this._fixTitle()}static get Default(){return hs}static get DefaultType(){return ds}static get NAME(){return"tooltip"}enable(){this._isEnabled=!0}disable(){this._isEnabled=!1}toggleEnabled(){this._isEnabled=!this._isEnabled}toggle(){this._isEnabled&&(this._isShown()?this._leave():this._enter())}dispose(){clearTimeout(this._timeout),N.off(this._element.closest(os),rs,this._hideModalHandler),this._element.getAttribute("data-bs-original-title")&&this._element.setAttribute("title",this._element.getAttribute("data-bs-original-title")),this._disposePopper(),super.dispose()}show(){if("none"===this._element.style.display)throw new Error("Please use show on visible elements");if(!this._isWithContent()||!this._isEnabled)return;const t=N.trigger(this._element,this.constructor.eventName("show")),e=(c(this._element)||this._element.ownerDocument.documentElement).contains(this._element);if(t.defaultPrevented||!e)return;this._disposePopper();const i=this._getTipElement();this._element.setAttribute("aria-describedby",i.getAttribute("id"));const{container:n}=this._config;if(this._element.ownerDocument.documentElement.contains(this.tip)||(n.append(i),N.trigger(this._element,this.constructor.eventName("inserted"))),this._popper=this._createPopper(i),i.classList.add(ns),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))N.on(t,"mouseover",h);this._queueCallback((()=>{N.trigger(this._element,this.constructor.eventName("shown")),!1===this._isHovered&&this._leave(),this._isHovered=!1}),this.tip,this._isAnimated())}hide(){if(this._isShown()&&!N.trigger(this._element,this.constructor.eventName("hide")).defaultPrevented){if(this._getTipElement().classList.remove(ns),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))N.off(t,"mouseover",h);this._activeTrigger.click=!1,this._activeTrigger[ls]=!1,this._activeTrigger[as]=!1,this._isHovered=null,this._queueCallback((()=>{this._isWithActiveTrigger()||(this._isHovered||this._disposePopper(),this._element.removeAttribute("aria-describedby"),N.trigger(this._element,this.constructor.eventName("hidden")))}),this.tip,this._isAnimated())}}update(){this._popper&&this._popper.update()}_isWithContent(){return Boolean(this._getTitle())}_getTipElement(){return this.tip||(this.tip=this._createTipElement(this._newContent||this._getContentForTemplate())),this.tip}_createTipElement(t){const e=this._getTemplateFactory(t).toHtml();if(!e)return null;e.classList.remove(is,ns),e.classList.add(`bs-${this.constructor.NAME}-auto`);const i=(t=>{do{t+=Math.floor(1e6*Math.random())}while(document.getElementById(t));return t})(this.constructor.NAME).toString();return e.setAttribute("id",i),this._isAnimated()&&e.classList.add(is),e}setContent(t){this._newContent=t,this._isShown()&&(this._disposePopper(),this.show())}_getTemplateFactory(t){return this._templateFactory?this._templateFactory.changeContent(t):this._templateFactory=new ts({...this._config,content:t,extraClass:this._resolvePossibleFunction(this._config.customClass)}),this._templateFactory}_getContentForTemplate(){return{[ss]:this._getTitle()}}_getTitle(){return this._resolvePossibleFunction(this._config.title)||this._element.getAttribute("data-bs-original-title")}_initializeOnDelegatedTarget(t){return this.constructor.getOrCreateInstance(t.delegateTarget,this._getDelegateConfig())}_isAnimated(){return this._config.animation||this.tip&&this.tip.classList.contains(is)}_isShown(){return this.tip&&this.tip.classList.contains(ns)}_createPopper(t){const e=g(this._config.placement,[this,t,this._element]),i=cs[e.toUpperCase()];return yi(this._element,t,this._getPopperConfig(i))}_getOffset(){const{offset:t}=this._config;return"string"==typeof t?t.split(",").map((t=>Number.parseInt(t,10))):"function"==typeof t?e=>t(e,this._element):t}_resolvePossibleFunction(t){return g(t,[this._element,this._element])}_getPopperConfig(t){const e={placement:t,modifiers:[{name:"flip",options:{fallbackPlacements:this._config.fallbackPlacements}},{name:"offset",options:{offset:this._getOffset()}},{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"arrow",options:{element:`.${this.constructor.NAME}-arrow`}},{name:"preSetPlacement",enabled:!0,phase:"beforeMain",fn:t=>{this._getTipElement().setAttribute("data-popper-placement",t.state.placement)}}]};return{...e,...g(this._config.popperConfig,[void 0,e])}}_setListeners(){const t=this._config.trigger.split(" ");for(const e of t)if("click"===e)N.on(this._element,this.constructor.eventName("click"),this._config.selector,(t=>{this._initializeOnDelegatedTarget(t).toggle()}));else if("manual"!==e){const t=e===as?this.constructor.eventName("mouseenter"):this.constructor.eventName("focusin"),i=e===as?this.constructor.eventName("mouseleave"):this.constructor.eventName("focusout");N.on(this._element,t,this._config.selector,(t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger["focusin"===t.type?ls:as]=!0,e._enter()})),N.on(this._element,i,this._config.selector,(t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger["focusout"===t.type?ls:as]=e._element.contains(t.relatedTarget),e._leave()}))}this._hideModalHandler=()=>{this._element&&this.hide()},N.on(this._element.closest(os),rs,this._hideModalHandler)}_fixTitle(){const t=this._element.getAttribute("title");t&&(this._element.getAttribute("aria-label")||this._element.textContent.trim()||this._element.setAttribute("aria-label",t),this._element.setAttribute("data-bs-original-title",t),this._element.removeAttribute("title"))}_enter(){this._isShown()||this._isHovered?this._isHovered=!0:(this._isHovered=!0,this._setTimeout((()=>{this._isHovered&&this.show()}),this._config.delay.show))}_leave(){this._isWithActiveTrigger()||(this._isHovered=!1,this._setTimeout((()=>{this._isHovered||this.hide()}),this._config.delay.hide))}_setTimeout(t,e){clearTimeout(this._timeout),this._timeout=setTimeout(t,e)}_isWithActiveTrigger(){return Object.values(this._activeTrigger).includes(!0)}_getConfig(t){const e=F.getDataAttributes(this._element);for(const t of Object.keys(e))es.has(t)&&delete e[t];return t={...e,..."object"==typeof t&&t?t:{}},t=this._mergeConfigObj(t),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}_configAfterMerge(t){return t.container=!1===t.container?document.body:r(t.container),"number"==typeof t.delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),t}_getDelegateConfig(){const t={};for(const[e,i]of Object.entries(this._config))this.constructor.Default[e]!==i&&(t[e]=i);return t.selector=!1,t.trigger="manual",t}_disposePopper(){this._popper&&(this._popper.destroy(),this._popper=null),this.tip&&(this.tip.remove(),this.tip=null)}static jQueryInterface(t){return this.each((function(){const e=us.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}m(us);const fs=".popover-header",ps=".popover-body",ms={...us.Default,content:"",offset:[0,8],placement:"right",template:'',trigger:"click"},gs={...us.DefaultType,content:"(null|string|element|function)"};class _s extends us{static get Default(){return ms}static get DefaultType(){return gs}static get NAME(){return"popover"}_isWithContent(){return this._getTitle()||this._getContent()}_getContentForTemplate(){return{[fs]:this._getTitle(),[ps]:this._getContent()}}_getContent(){return this._resolvePossibleFunction(this._config.content)}static jQueryInterface(t){return this.each((function(){const e=_s.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}m(_s);const bs=".bs.scrollspy",vs=`activate${bs}`,ys=`click${bs}`,ws=`load${bs}.data-api`,As="active",Es="[href]",Ts=".nav-link",Cs=`${Ts}, .nav-item > ${Ts}, .list-group-item`,Os={offset:null,rootMargin:"0px 0px -25%",smoothScroll:!1,target:null,threshold:[.1,.5,1]},xs={offset:"(number|null)",rootMargin:"string",smoothScroll:"boolean",target:"element",threshold:"array"};class ks extends W{constructor(t,e){super(t,e),this._targetLinks=new Map,this._observableSections=new Map,this._rootElement="visible"===getComputedStyle(this._element).overflowY?null:this._element,this._activeTarget=null,this._observer=null,this._previousScrollData={visibleEntryTop:0,parentScrollTop:0},this.refresh()}static get Default(){return Os}static get DefaultType(){return xs}static get NAME(){return"scrollspy"}refresh(){this._initializeTargetsAndObservables(),this._maybeEnableSmoothScroll(),this._observer?this._observer.disconnect():this._observer=this._getNewObserver();for(const t of this._observableSections.values())this._observer.observe(t)}dispose(){this._observer.disconnect(),super.dispose()}_configAfterMerge(t){return t.target=r(t.target)||document.body,t.rootMargin=t.offset?`${t.offset}px 0px -30%`:t.rootMargin,"string"==typeof t.threshold&&(t.threshold=t.threshold.split(",").map((t=>Number.parseFloat(t)))),t}_maybeEnableSmoothScroll(){this._config.smoothScroll&&(N.off(this._config.target,ys),N.on(this._config.target,ys,Es,(t=>{const e=this._observableSections.get(t.target.hash);if(e){t.preventDefault();const i=this._rootElement||window,n=e.offsetTop-this._element.offsetTop;if(i.scrollTo)return void i.scrollTo({top:n,behavior:"smooth"});i.scrollTop=n}})))}_getNewObserver(){const t={root:this._rootElement,threshold:this._config.threshold,rootMargin:this._config.rootMargin};return new IntersectionObserver((t=>this._observerCallback(t)),t)}_observerCallback(t){const e=t=>this._targetLinks.get(`#${t.target.id}`),i=t=>{this._previousScrollData.visibleEntryTop=t.target.offsetTop,this._process(e(t))},n=(this._rootElement||document.documentElement).scrollTop,s=n>=this._previousScrollData.parentScrollTop;this._previousScrollData.parentScrollTop=n;for(const o of t){if(!o.isIntersecting){this._activeTarget=null,this._clearActiveClass(e(o));continue}const t=o.target.offsetTop>=this._previousScrollData.visibleEntryTop;if(s&&t){if(i(o),!n)return}else s||t||i(o)}}_initializeTargetsAndObservables(){this._targetLinks=new Map,this._observableSections=new Map;const t=z.find(Es,this._config.target);for(const e of t){if(!e.hash||l(e))continue;const t=z.findOne(decodeURI(e.hash),this._element);a(t)&&(this._targetLinks.set(decodeURI(e.hash),e),this._observableSections.set(e.hash,t))}}_process(t){this._activeTarget!==t&&(this._clearActiveClass(this._config.target),this._activeTarget=t,t.classList.add(As),this._activateParents(t),N.trigger(this._element,vs,{relatedTarget:t}))}_activateParents(t){if(t.classList.contains("dropdown-item"))z.findOne(".dropdown-toggle",t.closest(".dropdown")).classList.add(As);else for(const e of z.parents(t,".nav, .list-group"))for(const t of z.prev(e,Cs))t.classList.add(As)}_clearActiveClass(t){t.classList.remove(As);const e=z.find(`${Es}.${As}`,t);for(const t of e)t.classList.remove(As)}static jQueryInterface(t){return this.each((function(){const e=ks.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}))}}N.on(window,ws,(()=>{for(const t of z.find('[data-bs-spy="scroll"]'))ks.getOrCreateInstance(t)})),m(ks);const Ls=".bs.tab",Ss=`hide${Ls}`,Ds=`hidden${Ls}`,$s=`show${Ls}`,Is=`shown${Ls}`,Ns=`click${Ls}`,Ps=`keydown${Ls}`,js=`load${Ls}`,Ms="ArrowLeft",Fs="ArrowRight",Hs="ArrowUp",Ws="ArrowDown",Bs="Home",zs="End",Rs="active",qs="fade",Vs="show",Ks=".dropdown-toggle",Qs=`:not(${Ks})`,Xs='[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]',Ys=`.nav-link${Qs}, .list-group-item${Qs}, [role="tab"]${Qs}, ${Xs}`,Us=`.${Rs}[data-bs-toggle="tab"], .${Rs}[data-bs-toggle="pill"], .${Rs}[data-bs-toggle="list"]`;class Gs extends W{constructor(t){super(t),this._parent=this._element.closest('.list-group, .nav, [role="tablist"]'),this._parent&&(this._setInitialAttributes(this._parent,this._getChildren()),N.on(this._element,Ps,(t=>this._keydown(t))))}static get NAME(){return"tab"}show(){const t=this._element;if(this._elemIsActive(t))return;const e=this._getActiveElem(),i=e?N.trigger(e,Ss,{relatedTarget:t}):null;N.trigger(t,$s,{relatedTarget:e}).defaultPrevented||i&&i.defaultPrevented||(this._deactivate(e,t),this._activate(t,e))}_activate(t,e){t&&(t.classList.add(Rs),this._activate(z.getElementFromSelector(t)),this._queueCallback((()=>{"tab"===t.getAttribute("role")?(t.removeAttribute("tabindex"),t.setAttribute("aria-selected",!0),this._toggleDropDown(t,!0),N.trigger(t,Is,{relatedTarget:e})):t.classList.add(Vs)}),t,t.classList.contains(qs)))}_deactivate(t,e){t&&(t.classList.remove(Rs),t.blur(),this._deactivate(z.getElementFromSelector(t)),this._queueCallback((()=>{"tab"===t.getAttribute("role")?(t.setAttribute("aria-selected",!1),t.setAttribute("tabindex","-1"),this._toggleDropDown(t,!1),N.trigger(t,Ds,{relatedTarget:e})):t.classList.remove(Vs)}),t,t.classList.contains(qs)))}_keydown(t){if(![Ms,Fs,Hs,Ws,Bs,zs].includes(t.key))return;t.stopPropagation(),t.preventDefault();const e=this._getChildren().filter((t=>!l(t)));let i;if([Bs,zs].includes(t.key))i=e[t.key===Bs?0:e.length-1];else{const n=[Fs,Ws].includes(t.key);i=b(e,t.target,n,!0)}i&&(i.focus({preventScroll:!0}),Gs.getOrCreateInstance(i).show())}_getChildren(){return z.find(Ys,this._parent)}_getActiveElem(){return this._getChildren().find((t=>this._elemIsActive(t)))||null}_setInitialAttributes(t,e){this._setAttributeIfNotExists(t,"role","tablist");for(const t of e)this._setInitialAttributesOnChild(t)}_setInitialAttributesOnChild(t){t=this._getInnerElement(t);const e=this._elemIsActive(t),i=this._getOuterElement(t);t.setAttribute("aria-selected",e),i!==t&&this._setAttributeIfNotExists(i,"role","presentation"),e||t.setAttribute("tabindex","-1"),this._setAttributeIfNotExists(t,"role","tab"),this._setInitialAttributesOnTargetPanel(t)}_setInitialAttributesOnTargetPanel(t){const e=z.getElementFromSelector(t);e&&(this._setAttributeIfNotExists(e,"role","tabpanel"),t.id&&this._setAttributeIfNotExists(e,"aria-labelledby",`${t.id}`))}_toggleDropDown(t,e){const i=this._getOuterElement(t);if(!i.classList.contains("dropdown"))return;const n=(t,n)=>{const s=z.findOne(t,i);s&&s.classList.toggle(n,e)};n(Ks,Rs),n(".dropdown-menu",Vs),i.setAttribute("aria-expanded",e)}_setAttributeIfNotExists(t,e,i){t.hasAttribute(e)||t.setAttribute(e,i)}_elemIsActive(t){return t.classList.contains(Rs)}_getInnerElement(t){return t.matches(Ys)?t:z.findOne(Ys,t)}_getOuterElement(t){return t.closest(".nav-item, .list-group-item")||t}static jQueryInterface(t){return this.each((function(){const e=Gs.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}))}}N.on(document,Ns,Xs,(function(t){["A","AREA"].includes(this.tagName)&&t.preventDefault(),l(this)||Gs.getOrCreateInstance(this).show()})),N.on(window,js,(()=>{for(const t of z.find(Us))Gs.getOrCreateInstance(t)})),m(Gs);const Js=".bs.toast",Zs=`mouseover${Js}`,to=`mouseout${Js}`,eo=`focusin${Js}`,io=`focusout${Js}`,no=`hide${Js}`,so=`hidden${Js}`,oo=`show${Js}`,ro=`shown${Js}`,ao="hide",lo="show",co="showing",ho={animation:"boolean",autohide:"boolean",delay:"number"},uo={animation:!0,autohide:!0,delay:5e3};class fo extends W{constructor(t,e){super(t,e),this._timeout=null,this._hasMouseInteraction=!1,this._hasKeyboardInteraction=!1,this._setListeners()}static get Default(){return uo}static get DefaultType(){return ho}static get NAME(){return"toast"}show(){N.trigger(this._element,oo).defaultPrevented||(this._clearTimeout(),this._config.animation&&this._element.classList.add("fade"),this._element.classList.remove(ao),d(this._element),this._element.classList.add(lo,co),this._queueCallback((()=>{this._element.classList.remove(co),N.trigger(this._element,ro),this._maybeScheduleHide()}),this._element,this._config.animation))}hide(){this.isShown()&&(N.trigger(this._element,no).defaultPrevented||(this._element.classList.add(co),this._queueCallback((()=>{this._element.classList.add(ao),this._element.classList.remove(co,lo),N.trigger(this._element,so)}),this._element,this._config.animation)))}dispose(){this._clearTimeout(),this.isShown()&&this._element.classList.remove(lo),super.dispose()}isShown(){return this._element.classList.contains(lo)}_maybeScheduleHide(){this._config.autohide&&(this._hasMouseInteraction||this._hasKeyboardInteraction||(this._timeout=setTimeout((()=>{this.hide()}),this._config.delay)))}_onInteraction(t,e){switch(t.type){case"mouseover":case"mouseout":this._hasMouseInteraction=e;break;case"focusin":case"focusout":this._hasKeyboardInteraction=e}if(e)return void this._clearTimeout();const i=t.relatedTarget;this._element===i||this._element.contains(i)||this._maybeScheduleHide()}_setListeners(){N.on(this._element,Zs,(t=>this._onInteraction(t,!0))),N.on(this._element,to,(t=>this._onInteraction(t,!1))),N.on(this._element,eo,(t=>this._onInteraction(t,!0))),N.on(this._element,io,(t=>this._onInteraction(t,!1)))}_clearTimeout(){clearTimeout(this._timeout),this._timeout=null}static jQueryInterface(t){return this.each((function(){const e=fo.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}return R(fo),m(fo),{Alert:Q,Button:Y,Carousel:Lt,Collapse:Rt,Dropdown:Ki,Modal:kn,Offcanvas:Kn,Popover:_s,ScrollSpy:ks,Tab:Gs,Toast:fo,Tooltip:us}})); +//# sourceMappingURL=bootstrap.bundle.min.js.map \ No newline at end of file diff --git a/packages/image/build/report/_js/file.js b/packages/image/build/report/_js/file.js new file mode 100644 index 000000000..124a8a18f --- /dev/null +++ b/packages/image/build/report/_js/file.js @@ -0,0 +1,53 @@ +$(function () { + var $window = $(window) + , $top_link = $('#toplink') + , $body = $('body, html') + , offset = $('#code').offset().top; + + $top_link.hide().click(function (event) { + event.preventDefault(); + $body.animate({scrollTop: 0}, 800); + }); + + $window.scroll(function () { + if ($window.scrollTop() > offset) { + $top_link.fadeIn(); + } else { + $top_link.fadeOut(); + } + }); + + var $popovers = $('.popin > :first-child'); + $('.popin').on({ + 'click.popover': function (event) { + event.stopPropagation(); + + var $container = $(this).children().first(); + + //Close all other popovers: + $popovers.each(function () { + var $current = $(this); + if (!$current.is($container)) { + $current.popover('hide'); + } + }); + + // Toggle this popover: + $container.popover('toggle'); + }, + }); + + //Hide all popovers on outside click: + $(document).click(function (event) { + if ($(event.target).closest($('.popover')).length === 0) { + $popovers.popover('hide'); + } + }); + + //Hide all popovers on escape: + $(document).keyup(function (event) { + if (event.key === 'Escape') { + $popovers.popover('hide'); + } + }); +}); diff --git a/packages/image/build/report/_js/jquery.min.js b/packages/image/build/report/_js/jquery.min.js new file mode 100644 index 000000000..798cc8bf7 --- /dev/null +++ b/packages/image/build/report/_js/jquery.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.7.1 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(ie,e){"use strict";var oe=[],r=Object.getPrototypeOf,ae=oe.slice,g=oe.flat?function(e){return oe.flat.call(e)}:function(e){return oe.concat.apply([],e)},s=oe.push,se=oe.indexOf,n={},i=n.toString,ue=n.hasOwnProperty,o=ue.toString,a=o.call(Object),le={},v=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},y=function(e){return null!=e&&e===e.window},C=ie.document,u={type:!0,src:!0,nonce:!0,noModule:!0};function m(e,t,n){var r,i,o=(n=n||C).createElement("script");if(o.text=e,t)for(r in u)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function x(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[i.call(e)]||"object":typeof e}var t="3.7.1",l=/HTML$/i,ce=function(e,t){return new ce.fn.init(e,t)};function c(e){var t=!!e&&"length"in e&&e.length,n=x(e);return!v(e)&&!y(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+ge+")"+ge+"*"),x=new RegExp(ge+"|>"),j=new RegExp(g),A=new RegExp("^"+t+"$"),D={ID:new RegExp("^#("+t+")"),CLASS:new RegExp("^\\.("+t+")"),TAG:new RegExp("^("+t+"|[*])"),ATTR:new RegExp("^"+p),PSEUDO:new RegExp("^"+g),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+ge+"*(even|odd|(([+-]|)(\\d*)n|)"+ge+"*(?:([+-]|)"+ge+"*(\\d+)|))"+ge+"*\\)|)","i"),bool:new RegExp("^(?:"+f+")$","i"),needsContext:new RegExp("^"+ge+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+ge+"*((?:-\\d)?\\d*)"+ge+"*\\)|)(?=[^-]|$)","i")},N=/^(?:input|select|textarea|button)$/i,q=/^h\d$/i,L=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,H=/[+~]/,O=new RegExp("\\\\[\\da-fA-F]{1,6}"+ge+"?|\\\\([^\\r\\n\\f])","g"),P=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},M=function(){V()},R=J(function(e){return!0===e.disabled&&fe(e,"fieldset")},{dir:"parentNode",next:"legend"});try{k.apply(oe=ae.call(ye.childNodes),ye.childNodes),oe[ye.childNodes.length].nodeType}catch(e){k={apply:function(e,t){me.apply(e,ae.call(t))},call:function(e){me.apply(e,ae.call(arguments,1))}}}function I(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(V(e),e=e||T,C)){if(11!==p&&(u=L.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return k.call(n,a),n}else if(f&&(a=f.getElementById(i))&&I.contains(e,a)&&a.id===i)return k.call(n,a),n}else{if(u[2])return k.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&e.getElementsByClassName)return k.apply(n,e.getElementsByClassName(i)),n}if(!(h[t+" "]||d&&d.test(t))){if(c=t,f=e,1===p&&(x.test(t)||m.test(t))){(f=H.test(t)&&U(e.parentNode)||e)==e&&le.scope||((s=e.getAttribute("id"))?s=ce.escapeSelector(s):e.setAttribute("id",s=S)),o=(l=Y(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+Q(l[o]);c=l.join(",")}try{return k.apply(n,f.querySelectorAll(c)),n}catch(e){h(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return re(t.replace(ve,"$1"),e,n,r)}function W(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function F(e){return e[S]=!0,e}function $(e){var t=T.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function B(t){return function(e){return fe(e,"input")&&e.type===t}}function _(t){return function(e){return(fe(e,"input")||fe(e,"button"))&&e.type===t}}function z(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&R(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function X(a){return F(function(o){return o=+o,F(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function U(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}function V(e){var t,n=e?e.ownerDocument||e:ye;return n!=T&&9===n.nodeType&&n.documentElement&&(r=(T=n).documentElement,C=!ce.isXMLDoc(T),i=r.matches||r.webkitMatchesSelector||r.msMatchesSelector,r.msMatchesSelector&&ye!=T&&(t=T.defaultView)&&t.top!==t&&t.addEventListener("unload",M),le.getById=$(function(e){return r.appendChild(e).id=ce.expando,!T.getElementsByName||!T.getElementsByName(ce.expando).length}),le.disconnectedMatch=$(function(e){return i.call(e,"*")}),le.scope=$(function(){return T.querySelectorAll(":scope")}),le.cssHas=$(function(){try{return T.querySelector(":has(*,:jqfake)"),!1}catch(e){return!0}}),le.getById?(b.filter.ID=function(e){var t=e.replace(O,P);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&C){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(O,P);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&C){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):t.querySelectorAll(e)},b.find.CLASS=function(e,t){if("undefined"!=typeof t.getElementsByClassName&&C)return t.getElementsByClassName(e)},d=[],$(function(e){var t;r.appendChild(e).innerHTML="
",e.querySelectorAll("[selected]").length||d.push("\\["+ge+"*(?:value|"+f+")"),e.querySelectorAll("[id~="+S+"-]").length||d.push("~="),e.querySelectorAll("a#"+S+"+*").length||d.push(".#.+[+~]"),e.querySelectorAll(":checked").length||d.push(":checked"),(t=T.createElement("input")).setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),r.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&d.push(":enabled",":disabled"),(t=T.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||d.push("\\["+ge+"*name"+ge+"*="+ge+"*(?:''|\"\")")}),le.cssHas||d.push(":has"),d=d.length&&new RegExp(d.join("|")),l=function(e,t){if(e===t)return a=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!le.sortDetached&&t.compareDocumentPosition(e)===n?e===T||e.ownerDocument==ye&&I.contains(ye,e)?-1:t===T||t.ownerDocument==ye&&I.contains(ye,t)?1:o?se.call(o,e)-se.call(o,t):0:4&n?-1:1)}),T}for(e in I.matches=function(e,t){return I(e,null,null,t)},I.matchesSelector=function(e,t){if(V(e),C&&!h[t+" "]&&(!d||!d.test(t)))try{var n=i.call(e,t);if(n||le.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){h(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(O,P),e[3]=(e[3]||e[4]||e[5]||"").replace(O,P),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||I.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&I.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return D.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&j.test(n)&&(t=Y(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(O,P).toLowerCase();return"*"===e?function(){return!0}:function(e){return fe(e,t)}},CLASS:function(e){var t=s[e+" "];return t||(t=new RegExp("(^|"+ge+")"+e+"("+ge+"|$)"))&&s(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=I.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function T(e,n,r){return v(n)?ce.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?ce.grep(e,function(e){return e===n!==r}):"string"!=typeof n?ce.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(ce.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||k,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:S.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof ce?t[0]:t,ce.merge(this,ce.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:C,!0)),w.test(r[1])&&ce.isPlainObject(t))for(r in t)v(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=C.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):v(e)?void 0!==n.ready?n.ready(e):e(ce):ce.makeArray(e,this)}).prototype=ce.fn,k=ce(C);var E=/^(?:parents|prev(?:Until|All))/,j={children:!0,contents:!0,next:!0,prev:!0};function A(e,t){while((e=e[t])&&1!==e.nodeType);return e}ce.fn.extend({has:function(e){var t=ce(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,Ce=/^$|^module$|\/(?:java|ecma)script/i;xe=C.createDocumentFragment().appendChild(C.createElement("div")),(be=C.createElement("input")).setAttribute("type","radio"),be.setAttribute("checked","checked"),be.setAttribute("name","t"),xe.appendChild(be),le.checkClone=xe.cloneNode(!0).cloneNode(!0).lastChild.checked,xe.innerHTML="",le.noCloneChecked=!!xe.cloneNode(!0).lastChild.defaultValue,xe.innerHTML="",le.option=!!xe.lastChild;var ke={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function Se(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&fe(e,t)?ce.merge([e],n):n}function Ee(e,t){for(var n=0,r=e.length;n",""]);var je=/<|&#?\w+;/;function Ae(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function Re(e,t){return fe(e,"table")&&fe(11!==t.nodeType?t:t.firstChild,"tr")&&ce(e).children("tbody")[0]||e}function Ie(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function We(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Fe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(_.hasData(e)&&(s=_.get(e).events))for(i in _.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),C.head.appendChild(r[0])},abort:function(){i&&i()}}});var Jt,Kt=[],Zt=/(=)\?(?=&|$)|\?\?/;ce.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Kt.pop()||ce.expando+"_"+jt.guid++;return this[e]=!0,e}}),ce.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Zt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Zt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=v(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Zt,"$1"+r):!1!==e.jsonp&&(e.url+=(At.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||ce.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=ie[r],ie[r]=function(){o=arguments},n.always(function(){void 0===i?ce(ie).removeProp(r):ie[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Kt.push(r)),o&&v(i)&&i(o[0]),o=i=void 0}),"script"}),le.createHTMLDocument=((Jt=C.implementation.createHTMLDocument("").body).innerHTML="
",2===Jt.childNodes.length),ce.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(le.createHTMLDocument?((r=(t=C.implementation.createHTMLDocument("")).createElement("base")).href=C.location.href,t.head.appendChild(r)):t=C),o=!n&&[],(i=w.exec(e))?[t.createElement(i[1])]:(i=Ae([e],t,o),o&&o.length&&ce(o).remove(),ce.merge([],i.childNodes)));var r,i,o},ce.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(ce.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},ce.expr.pseudos.animated=function(t){return ce.grep(ce.timers,function(e){return t===e.elem}).length},ce.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=ce.css(e,"position"),c=ce(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=ce.css(e,"top"),u=ce.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),v(t)&&(t=t.call(e,n,ce.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},ce.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){ce.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===ce.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===ce.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=ce(e).offset()).top+=ce.css(e,"borderTopWidth",!0),i.left+=ce.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-ce.css(r,"marginTop",!0),left:t.left-i.left-ce.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===ce.css(e,"position"))e=e.offsetParent;return e||J})}}),ce.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;ce.fn[t]=function(e){return M(this,function(e,t,n){var r;if(y(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),ce.each(["top","left"],function(e,n){ce.cssHooks[n]=Ye(le.pixelPosition,function(e,t){if(t)return t=Ge(e,n),_e.test(t)?ce(e).position()[n]+"px":t})}),ce.each({Height:"height",Width:"width"},function(a,s){ce.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){ce.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return M(this,function(e,t,n){var r;return y(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?ce.css(e,t,i):ce.style(e,t,n,i)},s,n?e:void 0,n)}})}),ce.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){ce.fn[t]=function(e){return this.on(t,e)}}),ce.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.on("mouseenter",e).on("mouseleave",t||e)}}),ce.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){ce.fn[n]=function(e,t){return 0 + + + + Dashboard for /var/www/html/packages/image/src/Charcoal/Image + + + + + + + +
+
+
+ +
+
+
+
+
+
+

Classes

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

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

Methods

+
+
+
+
+

Coverage Distribution

+
+ +
+
+
+

Complexity

+
+ +
+
+
+
+ +
+

Project Risks

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MethodCoverageComplexityCRAP
process40.5%38342
process0.0%11132
doCrop0.0%10110
cmd0.0%872
process0.0%756
doCrop0.0%530
findCmd33.3%826
setGeometry0.0%420
process0.0%420
process0.0%312
ratio0.0%312
orientation0.0%312
setWidth0.0%312
setHeight0.0%312
setX0.0%312
setY0.0%312
process0.0%312
setMinWidth0.0%312
setMinHeight0.0%312
setMaxWidth0.0%312
setMaxHeight0.0%312
process0.0%312
save45.5%59
createEffect57.9%56
setGravity0.0%26
setFormat0.0%26
process0.0%26
process0.0%26
process0.0%26
process0.0%26
compositeCmd0.0%26
imagemagickGravity0.0%26
process0.0%26
process0.0%26
process75.0%55
create66.7%55
__construct40.0%34
save66.7%44
applyCmd57.1%33
image40.0%22
setEffects75.0%22
process75.0%22
width75.0%22
height75.0%22
convertCmd75.0%22
process75.0%22
doResize66.7%22
process75.0%22
process75.0%22
+
+
+
+ +
+ + + + + + diff --git a/packages/image/build/report/index.html b/packages/image/build/report/index.html new file mode 100644 index 000000000..ad9a0a45a --- /dev/null +++ b/packages/image/build/report/index.html @@ -0,0 +1,297 @@ + + + + + Code Coverage for /var/www/html/packages/image/src/Charcoal/Image + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
+
+ 62.21% covered (warning) +
+
+
62.21%
805 / 1294
+
+ 57.87% covered (warning) +
+
+
57.87%
125 / 216
+
+ 20.69% covered (danger) +
+
+
20.69%
12 / 58
Effect
+
+ 71.32% covered (warning) +
+
+
71.32%
363 / 509
+
+ 74.77% covered (warning) +
+
+
74.77%
80 / 107
+
+ 64.71% covered (warning) +
+
+
64.71%
11 / 17
Imagemagick
+
+ 51.81% covered (warning) +
+
+
51.81%
172 / 332
+
+ 34.78% covered (danger) +
+
+
34.78%
16 / 46
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 19
Imagick
+
+ 59.11% covered (warning) +
+
+
59.11%
185 / 313
+
+ 40.00% covered (danger) +
+
+
40.00%
14 / 35
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 19
AbstractEffect.php
+
+ 80.00% covered (success) +
+
+
80.00%
12 / 15
+
+ 80.00% covered (success) +
+
+
80.00%
4 / 5
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
AbstractImage.php
+
+ 56.30% covered (warning) +
+
+
56.30%
67 / 119
+
+ 42.86% covered (warning) +
+
+
42.86%
9 / 21
+
+ 0.00% covered (danger) +
+
+
0.00%
0 / 1
EffectFactory.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
EffectInterface.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
ImageFactory.php
+
+ 100.00% covered (success) +
+
+
100.00%
6 / 6
+
+ 100.00% covered (success) +
+
+
100.00%
2 / 2
+
+ 100.00% covered (success) +
+
+
100.00%
1 / 1
ImageInterface.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
+
+ +
+ + diff --git a/packages/image/phpunit.xml.dist b/packages/image/phpunit.xml.dist index 4f5ad010e..d4a419050 100644 --- a/packages/image/phpunit.xml.dist +++ b/packages/image/phpunit.xml.dist @@ -1,32 +1,32 @@ - -> + cacheDirectory=".phpunit.cache" + backupStaticProperties="false"> ./tests/Charcoal - - + + ./src/Charcoal - - + + + + + + + + - + - - - - diff --git a/packages/object/phpunit.xml.dist b/packages/object/phpunit.xml.dist index 5c94aaf08..afe4579ca 100644 --- a/packages/object/phpunit.xml.dist +++ b/packages/object/phpunit.xml.dist @@ -1,16 +1,13 @@ + cacheDirectory=".phpunit.cache" + backupStaticProperties="false"> ./tests/Charcoal diff --git a/packages/object/tests/bootstrap.php b/packages/object/tests/bootstrap.php new file mode 100644 index 000000000..ac8e0b55a --- /dev/null +++ b/packages/object/tests/bootstrap.php @@ -0,0 +1,14 @@ + - -> + cacheDirectory=".phpunit.cache" + backupStaticProperties="false"> ./tests/Charcoal - - + + ./src/Charcoal - - + + + + + + + + - + - - - - diff --git a/packages/property/tests/bootstrap.php b/packages/property/tests/bootstrap.php new file mode 100644 index 000000000..ac8e0b55a --- /dev/null +++ b/packages/property/tests/bootstrap.php @@ -0,0 +1,14 @@ + - -> + cacheDirectory=".phpunit.cache" + backupStaticProperties="false"> ./tests/Charcoal - - + + ./src/Charcoal - - + + + + + + + + + - + - - - - - diff --git a/packages/queue/tests/bootstrap.php b/packages/queue/tests/bootstrap.php new file mode 100644 index 000000000..ac8e0b55a --- /dev/null +++ b/packages/queue/tests/bootstrap.php @@ -0,0 +1,14 @@ + - -> + cacheDirectory=".phpunit.cache" + backupStaticProperties="false"> ./tests/Charcoal - - + + ./src/Charcoal - - + + + + + + + + - + - - - - diff --git a/packages/translator/tests/bootstrap.php b/packages/translator/tests/bootstrap.php new file mode 100644 index 000000000..ac8e0b55a --- /dev/null +++ b/packages/translator/tests/bootstrap.php @@ -0,0 +1,14 @@ + - + cacheDirectory=".phpunit.cache" + backupStaticProperties="false"> ./tests/Charcoal - - + + ./src/Charcoal - - + + + + + + + + - + - - - - diff --git a/packages/ui/tests/bootstrap.php b/packages/ui/tests/bootstrap.php new file mode 100644 index 000000000..ac8e0b55a --- /dev/null +++ b/packages/ui/tests/bootstrap.php @@ -0,0 +1,14 @@ + - + cacheDirectory=".phpunit.cache" + backupStaticProperties="false"> ./tests/Charcoal - - + + ./src/Charcoal - - + + + + + + + + - + - - - - diff --git a/packages/user/tests/bootstrap.php b/packages/user/tests/bootstrap.php new file mode 100644 index 000000000..ac8e0b55a --- /dev/null +++ b/packages/user/tests/bootstrap.php @@ -0,0 +1,14 @@ + - + cacheDirectory=".phpunit.cache" + backupStaticProperties="false"> ./tests/Charcoal - - + + ./src/Charcoal - - + + + + + + + + - + - - - - diff --git a/packages/view/tests/bootstrap.php b/packages/view/tests/bootstrap.php new file mode 100644 index 000000000..ac8e0b55a --- /dev/null +++ b/packages/view/tests/bootstrap.php @@ -0,0 +1,14 @@ + Date: Mon, 1 Jun 2026 12:11:20 -0400 Subject: [PATCH 81/94] tests(workflow): add TEST_MODE environment variable for PHPUnit runs in GitHub Actions --- .github/workflows/php.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 1b9920091..9a3f2a034 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -90,4 +90,6 @@ jobs: run: tests/script/phpcs --ci ${{ matrix.package }} - name: Run PHPunit for all packages + env: + TEST_MODE: PACKAGE run: tests/script/phpunit --ci ${{ matrix.package }} From 3d7d577b6f167625fe62f10084f99d2d3734fb72 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Mon, 1 Jun 2026 12:16:10 -0400 Subject: [PATCH 82/94] tests(user): switch from $_ENV to getenv for TEST_MODE detection in bootstrap --- packages/user/tests/bootstrap.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/user/tests/bootstrap.php b/packages/user/tests/bootstrap.php index ac8e0b55a..90929e3b5 100644 --- a/packages/user/tests/bootstrap.php +++ b/packages/user/tests/bootstrap.php @@ -6,7 +6,7 @@ mb_internal_encoding('UTF-8'); date_default_timezone_set('UTC'); -if (($_ENV['TEST_MODE'] ?? '') === 'PACKAGE') { +if ((getenv('TEST_MODE') ?? '') === 'PACKAGE') { require getcwd().'/tests/bootstrap.php'; } else { /** @var \Composer\Autoload\ClassLoader $autoloader */ From c6101684ec10063350109af8bc0e72253e736859 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Mon, 1 Jun 2026 12:26:52 -0400 Subject: [PATCH 83/94] tests(workflow): set TEST_MODE environment variable in GitHub Actions for package testing --- .github/workflows/php.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 9a3f2a034..79136c595 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -86,6 +86,9 @@ jobs: with: redis-version: 6 + - name: Set the testing variable + run: echo "TEST_MODE=PACKAGE" >> $GITHUB_ENV + - name: Run PHPCS for all packages run: tests/script/phpcs --ci ${{ matrix.package }} From 37b56826b750887df9811b70e7c53468f76aa0f5 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Mon, 1 Jun 2026 12:35:52 -0400 Subject: [PATCH 84/94] tests(script): set TEST_MODE variable inline for package testing and clean up unused GitHub Actions step --- .github/workflows/php.yml | 3 --- tests/script/phpunit | 5 ++--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 79136c595..9a3f2a034 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -86,9 +86,6 @@ jobs: with: redis-version: 6 - - name: Set the testing variable - run: echo "TEST_MODE=PACKAGE" >> $GITHUB_ENV - - name: Run PHPCS for all packages run: tests/script/phpcs --ci ${{ matrix.package }} diff --git a/tests/script/phpunit b/tests/script/phpunit index 462032926..47df69756 100755 --- a/tests/script/phpunit +++ b/tests/script/phpunit @@ -78,18 +78,17 @@ if [[ "$1" ]]; then package=$1; fi # testing a single package test_package() { - export TEST_MODE="PACKAGE" echo -e "${GREEN}Testing the [${NC} $1 ${GREEN}] package..." if [[ -z "$ci" ]]; then - php vendor/bin/phpunit -c packages/"$1"/phpunit.xml.dist --coverage-text + env TEST_MODE="PACKAGE" php vendor/bin/phpunit -c packages/"$1"/phpunit.xml.dist --coverage-text if [[ -n "$interactive" ]]; then read -p 'Continue ?:[Y/n/r](Y) ' continue if [[ "$continue" == 'n' ]]; then exit 0; fi if [[ "$continue" == 'r' ]]; then test_package $1; fi fi else - php vendor/bin/phpunit -c packages/"$1"/phpunit.xml.dist \ + env TEST_MODE="PACKAGE" php vendor/bin/phpunit -c packages/"$1"/phpunit.xml.dist \ --coverage-clover "build/logs/clover.xml" \ --coverage-text exit $? From 5f1558134c07a3acd0cb3f12f976e18dff6c7e2b Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Mon, 1 Jun 2026 12:38:24 -0400 Subject: [PATCH 85/94] tests(bootstrap): replace $_ENV with getenv for TEST_MODE detection across packages --- packages/admin/tests/bootstrap.php | 2 +- packages/app/tests/bootstrap.php | 2 +- packages/attachment/tests/bootstrap.php | 2 +- packages/cache/tests/bootstrap.php | 2 +- packages/cms/tests/bootstrap.php | 2 +- packages/config/tests/bootstrap.php | 2 +- packages/core/tests/bootstrap.php | 2 +- packages/email/tests/bootstrap.php | 2 +- packages/factory/tests/bootstrap.php | 2 +- packages/image/tests/bootstrap.php | 2 +- packages/object/tests/bootstrap.php | 2 +- packages/property/tests/bootstrap.php | 2 +- packages/queue/tests/bootstrap.php | 2 +- packages/translator/tests/bootstrap.php | 2 +- packages/ui/tests/bootstrap.php | 2 +- packages/view/tests/bootstrap.php | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/admin/tests/bootstrap.php b/packages/admin/tests/bootstrap.php index ac8e0b55a..90929e3b5 100644 --- a/packages/admin/tests/bootstrap.php +++ b/packages/admin/tests/bootstrap.php @@ -6,7 +6,7 @@ mb_internal_encoding('UTF-8'); date_default_timezone_set('UTC'); -if (($_ENV['TEST_MODE'] ?? '') === 'PACKAGE') { +if ((getenv('TEST_MODE') ?? '') === 'PACKAGE') { require getcwd().'/tests/bootstrap.php'; } else { /** @var \Composer\Autoload\ClassLoader $autoloader */ diff --git a/packages/app/tests/bootstrap.php b/packages/app/tests/bootstrap.php index ac8e0b55a..90929e3b5 100644 --- a/packages/app/tests/bootstrap.php +++ b/packages/app/tests/bootstrap.php @@ -6,7 +6,7 @@ mb_internal_encoding('UTF-8'); date_default_timezone_set('UTC'); -if (($_ENV['TEST_MODE'] ?? '') === 'PACKAGE') { +if ((getenv('TEST_MODE') ?? '') === 'PACKAGE') { require getcwd().'/tests/bootstrap.php'; } else { /** @var \Composer\Autoload\ClassLoader $autoloader */ diff --git a/packages/attachment/tests/bootstrap.php b/packages/attachment/tests/bootstrap.php index ac8e0b55a..90929e3b5 100644 --- a/packages/attachment/tests/bootstrap.php +++ b/packages/attachment/tests/bootstrap.php @@ -6,7 +6,7 @@ mb_internal_encoding('UTF-8'); date_default_timezone_set('UTC'); -if (($_ENV['TEST_MODE'] ?? '') === 'PACKAGE') { +if ((getenv('TEST_MODE') ?? '') === 'PACKAGE') { require getcwd().'/tests/bootstrap.php'; } else { /** @var \Composer\Autoload\ClassLoader $autoloader */ diff --git a/packages/cache/tests/bootstrap.php b/packages/cache/tests/bootstrap.php index ac8e0b55a..90929e3b5 100644 --- a/packages/cache/tests/bootstrap.php +++ b/packages/cache/tests/bootstrap.php @@ -6,7 +6,7 @@ mb_internal_encoding('UTF-8'); date_default_timezone_set('UTC'); -if (($_ENV['TEST_MODE'] ?? '') === 'PACKAGE') { +if ((getenv('TEST_MODE') ?? '') === 'PACKAGE') { require getcwd().'/tests/bootstrap.php'; } else { /** @var \Composer\Autoload\ClassLoader $autoloader */ diff --git a/packages/cms/tests/bootstrap.php b/packages/cms/tests/bootstrap.php index ac8e0b55a..90929e3b5 100644 --- a/packages/cms/tests/bootstrap.php +++ b/packages/cms/tests/bootstrap.php @@ -6,7 +6,7 @@ mb_internal_encoding('UTF-8'); date_default_timezone_set('UTC'); -if (($_ENV['TEST_MODE'] ?? '') === 'PACKAGE') { +if ((getenv('TEST_MODE') ?? '') === 'PACKAGE') { require getcwd().'/tests/bootstrap.php'; } else { /** @var \Composer\Autoload\ClassLoader $autoloader */ diff --git a/packages/config/tests/bootstrap.php b/packages/config/tests/bootstrap.php index 3269fb5b7..70ded9041 100644 --- a/packages/config/tests/bootstrap.php +++ b/packages/config/tests/bootstrap.php @@ -2,7 +2,7 @@ declare(strict_types=1); -if (($_ENV['TEST_MODE'] ?? '') === 'PACKAGE') { +if ((getenv('TEST_MODE') ?? '') === 'PACKAGE') { require getcwd().'/tests/bootstrap.php'; } else { /** diff --git a/packages/core/tests/bootstrap.php b/packages/core/tests/bootstrap.php index ac8e0b55a..90929e3b5 100644 --- a/packages/core/tests/bootstrap.php +++ b/packages/core/tests/bootstrap.php @@ -6,7 +6,7 @@ mb_internal_encoding('UTF-8'); date_default_timezone_set('UTC'); -if (($_ENV['TEST_MODE'] ?? '') === 'PACKAGE') { +if ((getenv('TEST_MODE') ?? '') === 'PACKAGE') { require getcwd().'/tests/bootstrap.php'; } else { /** @var \Composer\Autoload\ClassLoader $autoloader */ diff --git a/packages/email/tests/bootstrap.php b/packages/email/tests/bootstrap.php index 57358fc7d..e60dd9ae2 100644 --- a/packages/email/tests/bootstrap.php +++ b/packages/email/tests/bootstrap.php @@ -6,7 +6,7 @@ use Charcoal\App\AppContainer; use Charcoal\Config\GenericConfig; -if (($_ENV['TEST_MODE'] ?? '') === 'PACKAGE') { +if ((getenv('TEST_MODE') ?? '') === 'PACKAGE') { require getcwd().'/tests/bootstrap.php'; } else { $autoloader = require __DIR__.'/../vendor/autoload.php'; diff --git a/packages/factory/tests/bootstrap.php b/packages/factory/tests/bootstrap.php index ac8e0b55a..90929e3b5 100644 --- a/packages/factory/tests/bootstrap.php +++ b/packages/factory/tests/bootstrap.php @@ -6,7 +6,7 @@ mb_internal_encoding('UTF-8'); date_default_timezone_set('UTC'); -if (($_ENV['TEST_MODE'] ?? '') === 'PACKAGE') { +if ((getenv('TEST_MODE') ?? '') === 'PACKAGE') { require getcwd().'/tests/bootstrap.php'; } else { /** @var \Composer\Autoload\ClassLoader $autoloader */ diff --git a/packages/image/tests/bootstrap.php b/packages/image/tests/bootstrap.php index 64b6efd7c..137c6462a 100644 --- a/packages/image/tests/bootstrap.php +++ b/packages/image/tests/bootstrap.php @@ -5,7 +5,7 @@ date_default_timezone_set('UTC'); -if (($_ENV['TEST_MODE'] ?? '') === 'PACKAGE') { +if ((getenv('TEST_MODE') ?? '') === 'PACKAGE') { require getcwd().'/tests/bootstrap.php'; } else { /** @var \Composer\Autoload\ClassLoader $autoloader */ diff --git a/packages/object/tests/bootstrap.php b/packages/object/tests/bootstrap.php index ac8e0b55a..90929e3b5 100644 --- a/packages/object/tests/bootstrap.php +++ b/packages/object/tests/bootstrap.php @@ -6,7 +6,7 @@ mb_internal_encoding('UTF-8'); date_default_timezone_set('UTC'); -if (($_ENV['TEST_MODE'] ?? '') === 'PACKAGE') { +if ((getenv('TEST_MODE') ?? '') === 'PACKAGE') { require getcwd().'/tests/bootstrap.php'; } else { /** @var \Composer\Autoload\ClassLoader $autoloader */ diff --git a/packages/property/tests/bootstrap.php b/packages/property/tests/bootstrap.php index ac8e0b55a..90929e3b5 100644 --- a/packages/property/tests/bootstrap.php +++ b/packages/property/tests/bootstrap.php @@ -6,7 +6,7 @@ mb_internal_encoding('UTF-8'); date_default_timezone_set('UTC'); -if (($_ENV['TEST_MODE'] ?? '') === 'PACKAGE') { +if ((getenv('TEST_MODE') ?? '') === 'PACKAGE') { require getcwd().'/tests/bootstrap.php'; } else { /** @var \Composer\Autoload\ClassLoader $autoloader */ diff --git a/packages/queue/tests/bootstrap.php b/packages/queue/tests/bootstrap.php index ac8e0b55a..90929e3b5 100644 --- a/packages/queue/tests/bootstrap.php +++ b/packages/queue/tests/bootstrap.php @@ -6,7 +6,7 @@ mb_internal_encoding('UTF-8'); date_default_timezone_set('UTC'); -if (($_ENV['TEST_MODE'] ?? '') === 'PACKAGE') { +if ((getenv('TEST_MODE') ?? '') === 'PACKAGE') { require getcwd().'/tests/bootstrap.php'; } else { /** @var \Composer\Autoload\ClassLoader $autoloader */ diff --git a/packages/translator/tests/bootstrap.php b/packages/translator/tests/bootstrap.php index ac8e0b55a..90929e3b5 100644 --- a/packages/translator/tests/bootstrap.php +++ b/packages/translator/tests/bootstrap.php @@ -6,7 +6,7 @@ mb_internal_encoding('UTF-8'); date_default_timezone_set('UTC'); -if (($_ENV['TEST_MODE'] ?? '') === 'PACKAGE') { +if ((getenv('TEST_MODE') ?? '') === 'PACKAGE') { require getcwd().'/tests/bootstrap.php'; } else { /** @var \Composer\Autoload\ClassLoader $autoloader */ diff --git a/packages/ui/tests/bootstrap.php b/packages/ui/tests/bootstrap.php index ac8e0b55a..90929e3b5 100644 --- a/packages/ui/tests/bootstrap.php +++ b/packages/ui/tests/bootstrap.php @@ -6,7 +6,7 @@ mb_internal_encoding('UTF-8'); date_default_timezone_set('UTC'); -if (($_ENV['TEST_MODE'] ?? '') === 'PACKAGE') { +if ((getenv('TEST_MODE') ?? '') === 'PACKAGE') { require getcwd().'/tests/bootstrap.php'; } else { /** @var \Composer\Autoload\ClassLoader $autoloader */ diff --git a/packages/view/tests/bootstrap.php b/packages/view/tests/bootstrap.php index ac8e0b55a..90929e3b5 100644 --- a/packages/view/tests/bootstrap.php +++ b/packages/view/tests/bootstrap.php @@ -6,7 +6,7 @@ mb_internal_encoding('UTF-8'); date_default_timezone_set('UTC'); -if (($_ENV['TEST_MODE'] ?? '') === 'PACKAGE') { +if ((getenv('TEST_MODE') ?? '') === 'PACKAGE') { require getcwd().'/tests/bootstrap.php'; } else { /** @var \Composer\Autoload\ClassLoader $autoloader */ From 00139fad2b2f224c58d5c6e9018ce0aaded1980d Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Mon, 1 Jun 2026 12:43:20 -0400 Subject: [PATCH 86/94] tests(config): disable PHPUnit warnings and notices across all package configurations --- packages/admin/phpunit.xml.dist | 4 +++- packages/app/phpunit.xml.dist | 4 +++- packages/attachment/phpunit.xml.dist | 4 +++- packages/cache/phpunit.xml.dist | 4 +++- packages/cms/phpunit.xml.dist | 4 +++- packages/config/phpunit.xml.dist | 4 +++- packages/core/phpunit.xml.dist | 4 +++- packages/email/phpunit.xml.dist | 4 +++- packages/factory/phpunit.xml.dist | 4 +++- packages/image/phpunit.xml.dist | 4 +++- packages/object/phpunit.xml.dist | 4 +++- packages/property/phpunit.xml.dist | 4 +++- packages/queue/phpunit.xml.dist | 4 +++- packages/translator/phpunit.xml.dist | 4 +++- packages/ui/phpunit.xml.dist | 4 +++- packages/user/phpunit.xml.dist | 4 +++- packages/view/phpunit.xml.dist | 4 +++- 17 files changed, 51 insertions(+), 17 deletions(-) diff --git a/packages/admin/phpunit.xml.dist b/packages/admin/phpunit.xml.dist index 52fb73231..9797e5ccf 100644 --- a/packages/admin/phpunit.xml.dist +++ b/packages/admin/phpunit.xml.dist @@ -7,7 +7,9 @@ processIsolation="false" stopOnFailure="false" cacheDirectory=".phpunit.cache" - backupStaticProperties="false"> + backupStaticProperties="false" + failOnWarning="false" + failOnNotice="false"> ./tests/Charcoal diff --git a/packages/app/phpunit.xml.dist b/packages/app/phpunit.xml.dist index eb9f75e66..5d32205ed 100644 --- a/packages/app/phpunit.xml.dist +++ b/packages/app/phpunit.xml.dist @@ -7,7 +7,9 @@ processIsolation="false" stopOnFailure="false" cacheDirectory=".phpunit.cache" - backupStaticProperties="false">> + backupStaticProperties="false" + failOnWarning="false" + failOnNotice="false"> ./tests/Charcoal diff --git a/packages/attachment/phpunit.xml.dist b/packages/attachment/phpunit.xml.dist index d4a419050..5d32205ed 100644 --- a/packages/attachment/phpunit.xml.dist +++ b/packages/attachment/phpunit.xml.dist @@ -7,7 +7,9 @@ processIsolation="false" stopOnFailure="false" cacheDirectory=".phpunit.cache" - backupStaticProperties="false"> + backupStaticProperties="false" + failOnWarning="false" + failOnNotice="false"> ./tests/Charcoal diff --git a/packages/cache/phpunit.xml.dist b/packages/cache/phpunit.xml.dist index d4a419050..5d32205ed 100644 --- a/packages/cache/phpunit.xml.dist +++ b/packages/cache/phpunit.xml.dist @@ -7,7 +7,9 @@ processIsolation="false" stopOnFailure="false" cacheDirectory=".phpunit.cache" - backupStaticProperties="false"> + backupStaticProperties="false" + failOnWarning="false" + failOnNotice="false"> ./tests/Charcoal diff --git a/packages/cms/phpunit.xml.dist b/packages/cms/phpunit.xml.dist index d4a419050..5d32205ed 100644 --- a/packages/cms/phpunit.xml.dist +++ b/packages/cms/phpunit.xml.dist @@ -7,7 +7,9 @@ processIsolation="false" stopOnFailure="false" cacheDirectory=".phpunit.cache" - backupStaticProperties="false"> + backupStaticProperties="false" + failOnWarning="false" + failOnNotice="false"> ./tests/Charcoal diff --git a/packages/config/phpunit.xml.dist b/packages/config/phpunit.xml.dist index bc4dd7cdb..c91f4c66f 100644 --- a/packages/config/phpunit.xml.dist +++ b/packages/config/phpunit.xml.dist @@ -7,7 +7,9 @@ processIsolation="false" stopOnFailure="false" cacheDirectory=".phpunit.cache" - backupStaticProperties="false"> + backupStaticProperties="false" + failOnWarning="false" + failOnNotice="false"> ./tests/Charcoal diff --git a/packages/core/phpunit.xml.dist b/packages/core/phpunit.xml.dist index d4a419050..5d32205ed 100644 --- a/packages/core/phpunit.xml.dist +++ b/packages/core/phpunit.xml.dist @@ -7,7 +7,9 @@ processIsolation="false" stopOnFailure="false" cacheDirectory=".phpunit.cache" - backupStaticProperties="false"> + backupStaticProperties="false" + failOnWarning="false" + failOnNotice="false"> ./tests/Charcoal diff --git a/packages/email/phpunit.xml.dist b/packages/email/phpunit.xml.dist index d4a419050..5d32205ed 100644 --- a/packages/email/phpunit.xml.dist +++ b/packages/email/phpunit.xml.dist @@ -7,7 +7,9 @@ processIsolation="false" stopOnFailure="false" cacheDirectory=".phpunit.cache" - backupStaticProperties="false"> + backupStaticProperties="false" + failOnWarning="false" + failOnNotice="false"> ./tests/Charcoal diff --git a/packages/factory/phpunit.xml.dist b/packages/factory/phpunit.xml.dist index cb35cce75..0c23d4743 100644 --- a/packages/factory/phpunit.xml.dist +++ b/packages/factory/phpunit.xml.dist @@ -7,7 +7,9 @@ processIsolation="false" stopOnFailure="false" cacheDirectory=".phpunit.cache" - backupStaticProperties="false"> + backupStaticProperties="false" + failOnWarning="false" + failOnNotice="false"> ./tests/Charcoal/Factory diff --git a/packages/image/phpunit.xml.dist b/packages/image/phpunit.xml.dist index d4a419050..5d32205ed 100644 --- a/packages/image/phpunit.xml.dist +++ b/packages/image/phpunit.xml.dist @@ -7,7 +7,9 @@ processIsolation="false" stopOnFailure="false" cacheDirectory=".phpunit.cache" - backupStaticProperties="false"> + backupStaticProperties="false" + failOnWarning="false" + failOnNotice="false"> ./tests/Charcoal diff --git a/packages/object/phpunit.xml.dist b/packages/object/phpunit.xml.dist index afe4579ca..37fe1b388 100644 --- a/packages/object/phpunit.xml.dist +++ b/packages/object/phpunit.xml.dist @@ -7,7 +7,9 @@ processIsolation="false" stopOnFailure="false" cacheDirectory=".phpunit.cache" - backupStaticProperties="false"> + backupStaticProperties="false" + failOnWarning="false" + failOnNotice="false"> ./tests/Charcoal diff --git a/packages/property/phpunit.xml.dist b/packages/property/phpunit.xml.dist index d4a419050..5d32205ed 100644 --- a/packages/property/phpunit.xml.dist +++ b/packages/property/phpunit.xml.dist @@ -7,7 +7,9 @@ processIsolation="false" stopOnFailure="false" cacheDirectory=".phpunit.cache" - backupStaticProperties="false"> + backupStaticProperties="false" + failOnWarning="false" + failOnNotice="false"> ./tests/Charcoal diff --git a/packages/queue/phpunit.xml.dist b/packages/queue/phpunit.xml.dist index 2781f4243..02c2d7bd7 100644 --- a/packages/queue/phpunit.xml.dist +++ b/packages/queue/phpunit.xml.dist @@ -7,7 +7,9 @@ processIsolation="false" stopOnFailure="false" cacheDirectory=".phpunit.cache" - backupStaticProperties="false"> + backupStaticProperties="false" + failOnWarning="false" + failOnNotice="false"> ./tests/Charcoal diff --git a/packages/translator/phpunit.xml.dist b/packages/translator/phpunit.xml.dist index d4a419050..5d32205ed 100644 --- a/packages/translator/phpunit.xml.dist +++ b/packages/translator/phpunit.xml.dist @@ -7,7 +7,9 @@ processIsolation="false" stopOnFailure="false" cacheDirectory=".phpunit.cache" - backupStaticProperties="false"> + backupStaticProperties="false" + failOnWarning="false" + failOnNotice="false"> ./tests/Charcoal diff --git a/packages/ui/phpunit.xml.dist b/packages/ui/phpunit.xml.dist index d4a419050..5d32205ed 100644 --- a/packages/ui/phpunit.xml.dist +++ b/packages/ui/phpunit.xml.dist @@ -7,7 +7,9 @@ processIsolation="false" stopOnFailure="false" cacheDirectory=".phpunit.cache" - backupStaticProperties="false"> + backupStaticProperties="false" + failOnWarning="false" + failOnNotice="false"> ./tests/Charcoal diff --git a/packages/user/phpunit.xml.dist b/packages/user/phpunit.xml.dist index d4a419050..5d32205ed 100644 --- a/packages/user/phpunit.xml.dist +++ b/packages/user/phpunit.xml.dist @@ -7,7 +7,9 @@ processIsolation="false" stopOnFailure="false" cacheDirectory=".phpunit.cache" - backupStaticProperties="false"> + backupStaticProperties="false" + failOnWarning="false" + failOnNotice="false"> ./tests/Charcoal diff --git a/packages/view/phpunit.xml.dist b/packages/view/phpunit.xml.dist index d4a419050..5d32205ed 100644 --- a/packages/view/phpunit.xml.dist +++ b/packages/view/phpunit.xml.dist @@ -7,7 +7,9 @@ processIsolation="false" stopOnFailure="false" cacheDirectory=".phpunit.cache" - backupStaticProperties="false"> + backupStaticProperties="false" + failOnWarning="false" + failOnNotice="false"> ./tests/Charcoal From 9245ddd68d990dfdf74436cdecc6ba3527b935cd Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Mon, 1 Jun 2026 12:46:45 -0400 Subject: [PATCH 87/94] style(config): fix PSR-12 formatting for __serialize() and __unserialize() methods --- packages/config/src/Charcoal/Config/AbstractEntity.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/config/src/Charcoal/Config/AbstractEntity.php b/packages/config/src/Charcoal/Config/AbstractEntity.php index 23e537511..1c59dce18 100644 --- a/packages/config/src/Charcoal/Config/AbstractEntity.php +++ b/packages/config/src/Charcoal/Config/AbstractEntity.php @@ -323,11 +323,13 @@ public function jsonSerialize(): mixed return $this->data(); } - public function __serialize(): array { + public function __serialize(): array + { return $this->data(); } - public function __unserialize(array $data): void { + public function __unserialize(array $data): void + { $this->setData($data); } From f15eb63295fb41e519f3a05a4c0cc1fdf0650a97 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Mon, 1 Jun 2026 13:04:47 -0400 Subject: [PATCH 88/94] chore(build): remove obsolete code coverage reports for image effect classes --- .../build/report/AbstractEffect.php.html | 335 ----- .../image/build/report/AbstractImage.php.html | 1060 --------------- .../AbstractAutoorientationEffect.php.html | 115 -- .../report/Effect/AbstractBlurEffect.php.html | 656 ---------- .../Effect/AbstractCompressionEffect.php.html | 208 --- .../report/Effect/AbstractCropEffect.php.html | 706 ---------- .../Effect/AbstractDitherEffect.php.html | 306 ----- .../Effect/AbstractFormatEffect.php.html | 216 ---- .../Effect/AbstractGrayscaleEffect.php.html | 115 -- .../report/Effect/AbstractMaskEffect.php.html | 222 ---- .../Effect/AbstractMirrorEffect.php.html | 218 ---- .../Effect/AbstractModulateEffect.php.html | 366 ------ .../Effect/AbstractResizeEffect.php.html | 1142 ----------------- .../Effect/AbstractRevertEffect.php.html | 215 ---- .../Effect/AbstractRotateEffect.php.html | 292 ----- .../Effect/AbstractSepiaEffect.php.html | 219 ---- .../Effect/AbstractSharpenEffect.php.html | 672 ---------- .../Effect/AbstractThresholdEffect.php.html | 214 --- .../report/Effect/AbstractTintEffect.php.html | 349 ----- .../Effect/AbstractWatermarkEffect.php.html | 224 ---- .../Effect/LayerEffectInterface.php.html | 150 --- .../report/Effect/LayerEffectTrait.php.html | 456 ------- .../image/build/report/Effect/dashboard.html | 348 ----- packages/image/build/report/Effect/index.html | 606 --------- .../image/build/report/EffectFactory.php.html | 114 -- .../build/report/EffectInterface.php.html | 116 -- .../image/build/report/ImageFactory.php.html | 203 --- .../build/report/ImageInterface.php.html | 220 ---- .../ImagemagickAutoorientationEffect.php.html | 178 --- .../Effect/ImagemagickBlurEffect.php.html | 321 ----- .../ImagemagickCompressionEffect.php.html | 178 --- .../Effect/ImagemagickCropEffect.php.html | 196 --- .../Effect/ImagemagickDitherEffect.php.html | 182 --- .../Effect/ImagemagickFormatEffect.php.html | 181 --- .../ImagemagickGrayscaleEffect.php.html | 178 --- .../Effect/ImagemagickMaskEffect.php.html | 182 --- .../Effect/ImagemagickMirrorEffect.php.html | 179 --- .../Effect/ImagemagickModulateEffect.php.html | 182 --- .../Effect/ImagemagickResizeEffect.php.html | 217 ---- .../Effect/ImagemagickRevertEffect.php.html | 179 --- .../Effect/ImagemagickRotateEffect.php.html | 178 --- .../Effect/ImagemagickSepiaEffect.php.html | 179 --- .../Effect/ImagemagickSharpenEffect.php.html | 241 ---- .../ImagemagickThresholdEffect.php.html | 179 --- .../Effect/ImagemagickTintEffect.php.html | 181 --- .../ImagemagickWatermarkEffect.php.html | 207 --- .../report/Imagemagick/Effect/dashboard.html | 339 ----- .../report/Imagemagick/Effect/index.html | 596 --------- .../Imagemagick/ImagemagickImage.php.html | 1036 --------------- .../build/report/Imagemagick/dashboard.html | 360 ------ .../image/build/report/Imagemagick/index.html | 147 --- .../ImagickAutoorientationEffect.php.html | 212 --- .../Imagick/Effect/ImagickBlurEffect.php.html | 336 ----- .../Effect/ImagickCompressionEffect.php.html | 209 --- .../Imagick/Effect/ImagickCropEffect.php.html | 220 ---- .../Effect/ImagickDitherEffect.php.html | 190 --- .../Effect/ImagickFormatEffect.php.html | 178 --- .../Effect/ImagickGrayscaleEffect.php.html | 180 --- .../Imagick/Effect/ImagickMaskEffect.php.html | 182 --- .../Effect/ImagickMirrorEffect.php.html | 183 --- .../Effect/ImagickModulateEffect.php.html | 183 --- .../Effect/ImagickResizeEffect.php.html | 180 --- .../Effect/ImagickRevertEffect.php.html | 180 --- .../Effect/ImagickRotateEffect.php.html | 178 --- .../Effect/ImagickSepiaEffect.php.html | 179 --- .../Effect/ImagickSharpenEffect.php.html | 244 ---- .../Effect/ImagickThresholdEffect.php.html | 181 --- .../Imagick/Effect/ImagickTintEffect.php.html | 185 --- .../Effect/ImagickWatermarkEffect.php.html | 246 ---- .../report/Imagick/Effect/dashboard.html | 342 ----- .../build/report/Imagick/Effect/index.html | 596 --------- .../report/Imagick/ImagickImage.php.html | 537 -------- .../image/build/report/Imagick/dashboard.html | 345 ----- .../image/build/report/Imagick/index.html | 147 --- packages/image/build/report/dashboard.html | 469 ------- packages/image/build/report/index.html | 297 ----- 76 files changed, 22438 deletions(-) delete mode 100644 packages/image/build/report/AbstractEffect.php.html delete mode 100644 packages/image/build/report/AbstractImage.php.html delete mode 100644 packages/image/build/report/Effect/AbstractAutoorientationEffect.php.html delete mode 100644 packages/image/build/report/Effect/AbstractBlurEffect.php.html delete mode 100644 packages/image/build/report/Effect/AbstractCompressionEffect.php.html delete mode 100644 packages/image/build/report/Effect/AbstractCropEffect.php.html delete mode 100644 packages/image/build/report/Effect/AbstractDitherEffect.php.html delete mode 100644 packages/image/build/report/Effect/AbstractFormatEffect.php.html delete mode 100644 packages/image/build/report/Effect/AbstractGrayscaleEffect.php.html delete mode 100644 packages/image/build/report/Effect/AbstractMaskEffect.php.html delete mode 100644 packages/image/build/report/Effect/AbstractMirrorEffect.php.html delete mode 100644 packages/image/build/report/Effect/AbstractModulateEffect.php.html delete mode 100644 packages/image/build/report/Effect/AbstractResizeEffect.php.html delete mode 100644 packages/image/build/report/Effect/AbstractRevertEffect.php.html delete mode 100644 packages/image/build/report/Effect/AbstractRotateEffect.php.html delete mode 100644 packages/image/build/report/Effect/AbstractSepiaEffect.php.html delete mode 100644 packages/image/build/report/Effect/AbstractSharpenEffect.php.html delete mode 100644 packages/image/build/report/Effect/AbstractThresholdEffect.php.html delete mode 100644 packages/image/build/report/Effect/AbstractTintEffect.php.html delete mode 100644 packages/image/build/report/Effect/AbstractWatermarkEffect.php.html delete mode 100644 packages/image/build/report/Effect/LayerEffectInterface.php.html delete mode 100644 packages/image/build/report/Effect/LayerEffectTrait.php.html delete mode 100644 packages/image/build/report/Effect/dashboard.html delete mode 100644 packages/image/build/report/Effect/index.html delete mode 100644 packages/image/build/report/EffectFactory.php.html delete mode 100644 packages/image/build/report/EffectInterface.php.html delete mode 100644 packages/image/build/report/ImageFactory.php.html delete mode 100644 packages/image/build/report/ImageInterface.php.html delete mode 100644 packages/image/build/report/Imagemagick/Effect/ImagemagickAutoorientationEffect.php.html delete mode 100644 packages/image/build/report/Imagemagick/Effect/ImagemagickBlurEffect.php.html delete mode 100644 packages/image/build/report/Imagemagick/Effect/ImagemagickCompressionEffect.php.html delete mode 100644 packages/image/build/report/Imagemagick/Effect/ImagemagickCropEffect.php.html delete mode 100644 packages/image/build/report/Imagemagick/Effect/ImagemagickDitherEffect.php.html delete mode 100644 packages/image/build/report/Imagemagick/Effect/ImagemagickFormatEffect.php.html delete mode 100644 packages/image/build/report/Imagemagick/Effect/ImagemagickGrayscaleEffect.php.html delete mode 100644 packages/image/build/report/Imagemagick/Effect/ImagemagickMaskEffect.php.html delete mode 100644 packages/image/build/report/Imagemagick/Effect/ImagemagickMirrorEffect.php.html delete mode 100644 packages/image/build/report/Imagemagick/Effect/ImagemagickModulateEffect.php.html delete mode 100644 packages/image/build/report/Imagemagick/Effect/ImagemagickResizeEffect.php.html delete mode 100644 packages/image/build/report/Imagemagick/Effect/ImagemagickRevertEffect.php.html delete mode 100644 packages/image/build/report/Imagemagick/Effect/ImagemagickRotateEffect.php.html delete mode 100644 packages/image/build/report/Imagemagick/Effect/ImagemagickSepiaEffect.php.html delete mode 100644 packages/image/build/report/Imagemagick/Effect/ImagemagickSharpenEffect.php.html delete mode 100644 packages/image/build/report/Imagemagick/Effect/ImagemagickThresholdEffect.php.html delete mode 100644 packages/image/build/report/Imagemagick/Effect/ImagemagickTintEffect.php.html delete mode 100644 packages/image/build/report/Imagemagick/Effect/ImagemagickWatermarkEffect.php.html delete mode 100644 packages/image/build/report/Imagemagick/Effect/dashboard.html delete mode 100644 packages/image/build/report/Imagemagick/Effect/index.html delete mode 100644 packages/image/build/report/Imagemagick/ImagemagickImage.php.html delete mode 100644 packages/image/build/report/Imagemagick/dashboard.html delete mode 100644 packages/image/build/report/Imagemagick/index.html delete mode 100644 packages/image/build/report/Imagick/Effect/ImagickAutoorientationEffect.php.html delete mode 100644 packages/image/build/report/Imagick/Effect/ImagickBlurEffect.php.html delete mode 100644 packages/image/build/report/Imagick/Effect/ImagickCompressionEffect.php.html delete mode 100644 packages/image/build/report/Imagick/Effect/ImagickCropEffect.php.html delete mode 100644 packages/image/build/report/Imagick/Effect/ImagickDitherEffect.php.html delete mode 100644 packages/image/build/report/Imagick/Effect/ImagickFormatEffect.php.html delete mode 100644 packages/image/build/report/Imagick/Effect/ImagickGrayscaleEffect.php.html delete mode 100644 packages/image/build/report/Imagick/Effect/ImagickMaskEffect.php.html delete mode 100644 packages/image/build/report/Imagick/Effect/ImagickMirrorEffect.php.html delete mode 100644 packages/image/build/report/Imagick/Effect/ImagickModulateEffect.php.html delete mode 100644 packages/image/build/report/Imagick/Effect/ImagickResizeEffect.php.html delete mode 100644 packages/image/build/report/Imagick/Effect/ImagickRevertEffect.php.html delete mode 100644 packages/image/build/report/Imagick/Effect/ImagickRotateEffect.php.html delete mode 100644 packages/image/build/report/Imagick/Effect/ImagickSepiaEffect.php.html delete mode 100644 packages/image/build/report/Imagick/Effect/ImagickSharpenEffect.php.html delete mode 100644 packages/image/build/report/Imagick/Effect/ImagickThresholdEffect.php.html delete mode 100644 packages/image/build/report/Imagick/Effect/ImagickTintEffect.php.html delete mode 100644 packages/image/build/report/Imagick/Effect/ImagickWatermarkEffect.php.html delete mode 100644 packages/image/build/report/Imagick/Effect/dashboard.html delete mode 100644 packages/image/build/report/Imagick/Effect/index.html delete mode 100644 packages/image/build/report/Imagick/ImagickImage.php.html delete mode 100644 packages/image/build/report/Imagick/dashboard.html delete mode 100644 packages/image/build/report/Imagick/index.html delete mode 100644 packages/image/build/report/dashboard.html delete mode 100644 packages/image/build/report/index.html diff --git a/packages/image/build/report/AbstractEffect.php.html b/packages/image/build/report/AbstractEffect.php.html deleted file mode 100644 index cf9249907..000000000 --- a/packages/image/build/report/AbstractEffect.php.html +++ /dev/null @@ -1,335 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/AbstractEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 80.00% covered (success) -
-
-
80.00%
12 / 15
-
- 80.00% covered (success) -
-
-
80.00%
4 / 5
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
AbstractEffect
-
- 80.00% covered (success) -
-
-
80.00%
12 / 15
-
- 80.00% covered (success) -
-
-
80.00%
4 / 5
8.51
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 setImage
-
- 100.00% covered (success) -
-
-
100.00%
2 / 2
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 image
-
- 40.00% covered (danger) -
-
-
40.00%
2 / 5
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2.86
 setData
-
- 100.00% covered (success) -
-
-
100.00%
5 / 5
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
3
 process
n/a
0 / 0
n/a
0 / 0
0
 setter
-
- 100.00% covered (success) -
-
-
100.00%
2 / 2
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 camelize
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image;
4
5use Charcoal\Image\Imagemagick\ImagemagickImage;
6use Charcoal\Image\Imagick\ImagickImage;
7use Exception;
8use Charcoal\Image\ImageInterface;
9
10/**
11 * Base Effect
12 */
13abstract class AbstractEffect implements EffectInterface
14{
15    private ?\Charcoal\Image\ImageInterface $image = null;
16
17    /**
18     * @param ImageInterface $image The parent image.
19     * @return AbstractEffect Chainable
20     */
21    public function setImage(ImageInterface $image)
22    {
23        $this->image = $image;
24        return $this;
25    }
26
27    /**
28     * @throws Exception If the parent image was not set before being accessed.
29     * @return ImageInterface|ImagickImage|ImagemagickImage
30     */
31    public function image()
32    {
33        if (!$this->image instanceof \Charcoal\Image\ImageInterface) {
34            throw new Exception(
35                'Can not get effect\'s image: Trying to access an unset image'
36            );
37        }
38        return $this->image;
39    }
40
41    /**
42     * @param array $data The effect data.
43     * @return AbstractEffect Chainable
44     */
45    public function setData(array $data)
46    {
47        foreach ($data as $key => $val) {
48            $method = [ $this, $this->setter($key) ];
49            if (is_callable($method)) {
50                call_user_func($method, $val);
51            }
52        }
53        return $this;
54    }
55
56    /**
57     * @param array $data Optional effect data. If null, use the currently set properties.
58     * @return AbstractEffect Chainable
59     */
60    abstract public function process(?array $data = null);
61
62    /**
63     * Allow an object to define how the key setter are called.
64     *
65     * @param string $key The key to get the setter from.
66     * @return string The setter method name, for a given key.
67     */
68    protected function setter(string $key)
69    {
70        $setter = 'set_' . $key;
71        return $this->camelize($setter);
72    }
73
74    /**
75     * Transform a snake_case string to camelCase.
76     *
77     * @param string $str The snake_case string to camelize.
78     * @return string The camelcase'd string.
79     */
80    protected function camelize($str)
81    {
82        return lcfirst(implode('', array_map(ucfirst(...), explode('_', $str))));
83    }
84}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/AbstractImage.php.html b/packages/image/build/report/AbstractImage.php.html deleted file mode 100644 index 5aebe35ee..000000000 --- a/packages/image/build/report/AbstractImage.php.html +++ /dev/null @@ -1,1060 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/AbstractImage.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 56.30% covered (warning) -
-
-
56.30%
67 / 119
-
- 42.86% covered (warning) -
-
-
42.86%
9 / 21
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
AbstractImage
-
- 56.30% covered (warning) -
-
-
56.30%
67 / 119
-
- 42.86% covered (warning) -
-
-
42.86%
9 / 21
181.26
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 __call
-
- 50.00% covered (warning) -
-
-
50.00%
2 / 4
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
1.12
 effectFactory
-
- 100.00% covered (success) -
-
-
100.00%
3 / 3
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
2
 driverType
n/a
0 / 0
n/a
0 / 0
0
 setData
-
- 100.00% covered (success) -
-
-
100.00%
7 / 7
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
7
 setSource
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
2
 source
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 setTarget
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
2
 target
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 setEffects
-
- 75.00% covered (warning) -
-
-
75.00%
3 / 4
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2.06
 effects
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2
 addEffect
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 3
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2
 process
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 6
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
12
 processEffect
-
- 100.00% covered (success) -
-
-
100.00%
3 / 3
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 create
n/a
0 / 0
n/a
0 / 0
0
 open
n/a
0 / 0
n/a
0 / 0
0
 save
n/a
0 / 0
n/a
0 / 0
0
 width
n/a
0 / 0
n/a
0 / 0
0
 height
n/a
0 / 0
n/a
0 / 0
0
 ratio
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 7
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
12
 orientation
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 6
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
12
 isHorizontal
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2
 isVertical
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2
 isSquare
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2
 createEffect
-
- 57.89% covered (warning) -
-
-
57.89%
11 / 19
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
6.87
 availableChannels
-
- 100.00% covered (success) -
-
-
100.00%
13 / 13
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 availableGravities
-
- 100.00% covered (success) -
-
-
100.00%
11 / 11
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 availableFilters
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 15
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image;
4
5use Exception;
6use InvalidArgumentException;
7use Charcoal\Image\ImageInterface;
8use Charcoal\Image\EffectInterface;
9use Charcoal\Image\EffectFactory;
10
11/**
12 * Base Image class
13 */
14abstract class AbstractImage implements ImageInterface
15{
16    /**
17     * @var string $source
18     */
19    protected $source;
20
21    /**
22     * @var string $target
23     */
24    protected $target;
25
26    /**
27     * @var array $Effects
28     */
29    protected $effects = [];
30
31    private ?\Charcoal\Image\EffectFactory $effectFactory = null;
32
33
34    /**
35     * Magic: Attempt to load the effect of the called method name.
36     *
37     * Example, `$img->blur([ 'sigma' => 15 ]);` would create a "Blur" effect.
38     *
39     * @param  string $fxType The effect type.
40     * @param  array  $data   The effect options.
41     * @return ImageInterface Chainable
42     */
43    public function __call(string $fxType, array $data)
44    {
45        $data['type'] = $fxType;
46
47        $fx = $this->createEffect($data);
48        $fx->process();
49
50        return $this;
51    }
52
53    /**
54     * Safe effect factory getter.
55     * If the factory doesn't exist, create it.
56     *
57     * @return EffectFactory
58     */
59    protected function effectFactory()
60    {
61        if (!$this->effectFactory instanceof \Charcoal\Image\EffectFactory) {
62            $this->effectFactory = new EffectFactory();
63        }
64        return $this->effectFactory;
65    }
66
67    /**
68     * @return string
69     */
70    abstract public function driverType();
71
72
73    /**
74     * @param array $data The image data (source, target and effects).
75     * @return ImageInterface Chainable
76     */
77    public function setData(array $data)
78    {
79        if (isset($data['source']) && $data['source'] !== null) {
80            $this->setSource($data['source']);
81        }
82        if (isset($data['target']) && $data['target'] !== null) {
83            $this->setTarget($data['target']);
84        }
85        if (isset($data['effects']) && $data['effects'] !== null) {
86            $this->setEffects($data['effects']);
87        }
88        return $this;
89    }
90
91    /**
92     * @param string $source The image source.
93     * @throws InvalidArgumentException If the source argument is not a string.
94     * @return ImageInterface Chainable
95     */
96    public function setSource($source)
97    {
98        if (!is_string($source)) {
99            throw new InvalidArgumentException(
100                'Source must be a string'
101            );
102        }
103        $this->source = $source;
104        return $this;
105    }
106
107    /**
108     * @return string
109     */
110    public function source()
111    {
112        return $this->source;
113    }
114
115    /**
116     * @param string $target The image target.
117     * @throws InvalidArgumentException If the target argument is not a string.
118     * @return ImageInterface Chainable
119     */
120    public function setTarget($target)
121    {
122        if (!is_string($target)) {
123            throw new InvalidArgumentException(
124                'Target must be a string'
125            );
126        }
127        $this->target = $target;
128        return $this;
129    }
130
131    /**
132     * @return string
133     */
134    public function target()
135    {
136        return $this->target;
137    }
138
139    /**
140     * @param array $effects The effects to apply.
141     * @return ImageInterface Chainable
142     */
143    public function setEffects(array $effects)
144    {
145        $this->effects = [];
146        foreach ($effects as $effect) {
147            $this->addEffect($effect);
148        }
149        return $this;
150    }
151
152    /**
153     * @return EffectInterface[] The array of `EffectInterface` effects.
154     */
155    public function effects()
156    {
157        return $this->effects;
158    }
159
160    /**
161     * @param array|EffectInterface $effect The effect to add.
162     * @return ImageInterface Chainable
163     */
164    public function addEffect($effect)
165    {
166        $fx = $this->createEffect($effect);
167        $this->effects[] = $fx;
168        return $this;
169    }
170
171    /**
172     * @param array $effects Optional. The effects to process. If null, use in-memory's.
173     * @return ImageInterface Chainable
174     */
175    public function process(?array $effects = null)
176    {
177        if ($effects !== null) {
178            $this->setEffects($effects);
179        }
180
181        $effects = $this->effects();
182        foreach ($effects as $fx) {
183            $fx->process();
184        }
185        return $this;
186    }
187
188    /**
189     * @param array|EffectInterface $effect The effect to process.
190     * @return ImageInterface Chainable
191     */
192    public function processEffect($effect)
193    {
194        $fx = $this->createEffect($effect);
195        $fx->process();
196        return $this;
197    }
198
199    /**
200     * Create a blank canvas of a given size, with a given background color.
201     *
202     * @param integer $width  Image width, in pixels.
203     * @param integer $height Image height, in pixels.
204     * @param string  $color  Default to transparent.
205     * @return ImageInterface Chainable
206     */
207    abstract public function create($width, $height, $color = 'rgb(100%, 100%, 100%, 0)');
208
209    /**
210     * Open an image file
211     *
212     * @param string $source The source path / filename.
213     * @return ImageInterface Chainable
214     */
215    abstract public function open($source = null);
216
217    /**
218     * Save an image to a target.
219     * If no target is set, the original source will be owerwritten
220     *
221     * @param string $target The target path / filename.
222     * @return ImageInterface Chainable
223     */
224    abstract public function save($target = null);
225
226    /**
227     * Get the image's width, in pixels
228     *
229     * @return integer
230     */
231    abstract public function width();
232
233    /**
234     * Get the image's height, in pixels
235     *
236     * @return integer
237     */
238    abstract public function height();
239
240    /**
241     * Get the image's ratio (width / height)
242     *
243     * @return float
244     * @throws Exception If the width or height has not been set.
245     */
246    public function ratio()
247    {
248        $width = $this->width();
249        $height = $this->height();
250        if (!$width || !$height) {
251            throw new Exception(
252                'Ratio can not be calculated. Invalid image dimensions'
253            );
254        }
255        return ($width / $height);
256    }
257
258    /**
259     * Orientation can be "horizontal", "vertical" or "square"
260     *
261     * @return string
262     */
263    public function orientation()
264    {
265        $ratio = $this->ratio();
266        if ($ratio > 1) {
267            return 'horizontal';
268        } elseif ($ratio < 1) {
269            return 'vertical';
270        } else {
271            return 'square';
272        }
273    }
274
275    /**
276     * @return boolean
277     */
278    public function isHorizontal()
279    {
280        return ($this->orientation() == 'horizontal');
281    }
282
283    /**
284     * @return boolean
285     */
286    public function isVertical()
287    {
288        return ($this->orientation() == 'vertical');
289    }
290
291    /**
292     * @return boolean
293     */
294    public function isSquare()
295    {
296        return ($this->orientation() == 'square');
297    }
298
299    /**
300     * Ensure an EffectInterface object
301     *
302     * @param array|EffectInterface $effect The effect to create.
303     * @throws InvalidArgumentException If the argument is not an array or Effect.
304     * @return EffectInterface
305     */
306    protected function createEffect($effect)
307    {
308        if ($effect instanceof EffectInterface) {
309            $effect->setImage($this);
310            return $effect;
311        } elseif (is_array($effect)) {
312            if (!isset($effect['type'])) {
313                throw new InvalidArgumentException(
314                    'Effect parameter must define effect type'
315                );
316            }
317            $fxType = $effect['type'];
318            if (!str_contains((string)$fxType, '/')) {
319                // Core effects do not need to be namespaced
320                $driver = $this->driverType();
321                $fxType = 'charcoal/image/' . $driver . '/effect/' . $driver . '-' . $fxType . '-effect';
322            }
323            $imageEffect = $this->effectFactory()->create($fxType);
324            $imageEffect->setImage($this);
325            $imageEffect->setData($effect);
326            return $imageEffect;
327        } else {
328            throw new InvalidArgumentException(
329                'Effect must be an array or effect object'
330            );
331        }
332    }
333
334    /**
335     * @return array
336     */
337    public function availableChannels()
338    {
339        return [
340            // RGB
341            'red',
342            'green',
343            'blue',
344            // CMYK
345            'cyan',
346            'magenta',
347            'yellow',
348            'black',
349            // Others
350            'all',
351            'alpha',
352            'opacity',
353            'gray'
354        ];
355    }
356
357    /**
358     * @return array
359     */
360    public function availableGravities()
361    {
362        return [
363            'center',
364            'n',
365            's',
366            'e',
367            'w',
368            'ne',
369            'nw',
370            'se',
371            'sw'
372        ];
373    }
374
375    /**
376     * @return array
377     */
378    public function availableFilters()
379    {
380        // Unsupported: bartlett, bohman, kaiser, parzen and welsh.
381        return [
382            'blackman',
383            'box',
384            'catrom',
385            'cubic',
386            'hamming',
387            'hanning',
388            'hermite',
389            'gaussian',
390            'lanczos',
391            'mitchell',
392            'point',
393            'quadratic',
394            'triangle'
395        ];
396    }
397}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Effect/AbstractAutoorientationEffect.php.html b/packages/image/build/report/Effect/AbstractAutoorientationEffect.php.html deleted file mode 100644 index 71f5e85ca..000000000 --- a/packages/image/build/report/Effect/AbstractAutoorientationEffect.php.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/AbstractAutoorientationEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
AbstractAutoorientationEffect
n/a
0 / 0
n/a
0 / 0
0
n/a
0 / 0
-
- - - - - - - - - - - - - - - - - - - -
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image\Effect;
6
7use Charcoal\Image\AbstractEffect;
8
9/**
10 * Reads image EXIF data to automatically rotate it to the proper orientation
11 */
12abstract class AbstractAutoorientationEffect extends AbstractEffect
13{
14    // This effect does not have any options.
15}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Effect/AbstractBlurEffect.php.html b/packages/image/build/report/Effect/AbstractBlurEffect.php.html deleted file mode 100644 index abc7ad6e3..000000000 --- a/packages/image/build/report/Effect/AbstractBlurEffect.php.html +++ /dev/null @@ -1,656 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/AbstractBlurEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 96.30% covered (success) -
-
-
96.30%
52 / 54
-
- 90.91% covered (success) -
-
-
90.91%
10 / 11
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
AbstractBlurEffect
-
- 96.30% covered (success) -
-
-
96.30%
52 / 54
-
- 90.91% covered (success) -
-
-
90.91%
10 / 11
25
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 setRadius
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
3
 radius
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 setSigma
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
3
 sigma
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 setMode
-
- 100.00% covered (success) -
-
-
100.00%
14 / 14
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
2
 mode
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 setChannel
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
2
 channel
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 setAngle
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
2
 angle
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 process
-
- 81.82% covered (success) -
-
-
81.82%
9 / 11
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
8.38
 processAdaptive
n/a
0 / 0
n/a
0 / 0
0
 processGaussian
n/a
0 / 0
n/a
0 / 0
0
 processMotion
n/a
0 / 0
n/a
0 / 0
0
 processRadial
n/a
0 / 0
n/a
0 / 0
0
 processSoft
n/a
0 / 0
n/a
0 / 0
0
 processStandard
n/a
0 / 0
n/a
0 / 0
0
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Effect;
4
5use InvalidArgumentException;
6use Charcoal\Image\AbstractEffect;
7
8/**
9 * Blur the image
10 */
11abstract class AbstractBlurEffect extends AbstractEffect
12{
13    /**
14     * @var float $radius
15     */
16    private $radius = 0;
17    /**
18     * @var float $sigma
19     */
20    private $sigma = 1;
21    private string $mode = 'standard';
22
23    /**
24     * @var string $channel
25     */
26    private $channel = 'all';
27
28    /**
29     * The angle is only used for "motion" and "radial" modes
30     * @var float $angle
31     */
32    private $angle = 0;
33
34    /**
35     * @param float $radius The blur radius value.
36     * @throws InvalidArgumentException If the argument is not a valid number.
37     * @return AbstractBlurEffect Chainable
38     */
39    public function setRadius($radius)
40    {
41        if (!is_numeric($radius) || ($radius < 0)) {
42            throw new InvalidArgumentException(
43                'Radius must be a float (greater than 0)'
44            );
45        }
46         $this->radius = (float)$radius;
47         return $this;
48    }
49
50    /**
51     * @return float
52     */
53    public function radius()
54    {
55        return $this->radius;
56    }
57
58    /**
59     * @param float $sigma The blur sigma value.
60     * @throws InvalidArgumentException If the argument is not a valid number.
61     * @return AbstractBlurEffect Chainable
62     */
63    public function setSigma($sigma)
64    {
65        if (!is_numeric($sigma) || ($sigma < 0)) {
66            throw new InvalidArgumentException(
67                'Sigma value must be a float (greater than 0)'
68            );
69        }
70        $this->sigma = (float)$sigma;
71        return $this;
72    }
73
74    /**
75     * @return float
76     */
77    public function sigma()
78    {
79        return $this->sigma;
80    }
81
82    /**
83     * @param string $mode The blur mode.
84     * @throws InvalidArgumentException If the argument is not a valid blur mode.
85     * @return AbstractBlurEffect Chainable
86     */
87    public function setMode($mode)
88    {
89        $allowedModes = [
90            'standard',
91            'adaptive',
92            'gaussian',
93            'motion',
94            'radial',
95            'soft'
96        ];
97        if (!in_array($mode, $allowedModes)) {
98            throw new InvalidArgumentException(
99                sprintf('Mode %s is not an allowed blur mode', $mode)
100            );
101        }
102        $this->mode = $mode;
103        return $this;
104    }
105
106    /**
107     * @return string
108     */
109    public function mode()
110    {
111        return $this->mode;
112    }
113
114    /**
115     * @param string $channel The blur channel.
116     * @throws InvalidArgumentException If the argument is not a valid image channel.
117     * @return AbstractBlurEffect Chainable
118     */
119    public function setChannel($channel)
120    {
121        if (!in_array($channel, $this->image()->availableChannels())) {
122            throw new InvalidArgumentException(
123                'Channel is not valid'
124            );
125        }
126        $this->channel = $channel;
127        return $this;
128    }
129
130    /**
131     * @return string
132     */
133    public function channel()
134    {
135        return $this->channel;
136    }
137
138    /**
139     * @param float $angle The blur angle.
140     * @throws InvalidArgumentException If the argument is not a valid number.
141     * @return AbstractBlurEffect Chainable
142     */
143    public function setAngle($angle)
144    {
145        if (!is_numeric($angle)) {
146            throw new InvalidArgumentException(
147                'Angle must be a numeric value, in degrees'
148            );
149        }
150        $this->angle = (float)$angle;
151        return $this;
152    }
153
154    /**
155     * @return float
156     */
157    public function angle()
158    {
159        return $this->angle;
160    }
161
162    /**
163     * @param array $data The effect data, if available.
164     * @return AbstractBlurEffect Chainable
165     */
166    public function process(?array $data = null)
167    {
168        if ($data !== null) {
169            $this->setData($data);
170        }
171
172        $mode = $this->mode();
173        return match ($mode) {
174            'adaptive' => $this->processAdaptive(),
175            'gaussian' => $this->processGaussian(),
176            'motion' => $this->processMotion(),
177            'radial' => $this->processRadial(),
178            'soft' => $this->processSoft(),
179            default => $this->processStandard(),
180        };
181    }
182
183    /**
184     * @return AbstractBlurEffect Chainable
185     */
186    abstract public function processAdaptive();
187
188    /**
189     * @return AbstractBlurEffect Chainable
190     */
191    abstract public function processGaussian();
192
193    /**
194     * @return AbstractBlurEffect Chainable
195     */
196    abstract public function processMotion();
197
198    /**
199     * @return AbstractBlurEffect Chainable
200     */
201    abstract public function processRadial();
202
203    /**
204     * @return AbstractBlurEffect Chainable
205     */
206    abstract public function processSoft();
207
208    /**
209     * @return AbstractBlurEffect Chainable
210     */
211    abstract public function processStandard();
212}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Effect/AbstractCompressionEffect.php.html b/packages/image/build/report/Effect/AbstractCompressionEffect.php.html deleted file mode 100644 index d1b2b36e5..000000000 --- a/packages/image/build/report/Effect/AbstractCompressionEffect.php.html +++ /dev/null @@ -1,208 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/AbstractCompressionEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 3
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 2
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
AbstractCompressionEffect
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 3
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 2
6
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 setQuality
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 2
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2
 quality
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image\Effect;
6
7use Charcoal\Image\AbstractEffect;
8
9/**
10 * Image compression effect to adapt for web.
11 * Defaults to 100% quality
12 */
13abstract class AbstractCompressionEffect extends AbstractEffect
14{
15    private int $quality = 100;
16
17    /**
18     * @param int $quality Image quality from 1 to 100
19     * @return AbstractEffect Chainable
20     */
21    public function setQuality(int $quality)
22    {
23        $this->quality = $quality;
24        return $this;
25    }
26
27    /**
28     * @return int
29     */
30    public function quality()
31    {
32        return $this->quality;
33    }
34}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Effect/AbstractCropEffect.php.html b/packages/image/build/report/Effect/AbstractCropEffect.php.html deleted file mode 100644 index 42f11bf4b..000000000 --- a/packages/image/build/report/Effect/AbstractCropEffect.php.html +++ /dev/null @@ -1,706 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/AbstractCropEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 56
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 15
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
AbstractCropEffect
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 56
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 15
870
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 setWidth
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 6
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
12
 width
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2
 setHeight
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 6
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
12
 height
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2
 setX
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 6
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
12
 x
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2
 setY
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 6
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
12
 y
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2
 setGeometry
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 6
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
20
 geometry
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2
 setGravity
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 6
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
6
 gravity
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2
 setRepage
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 2
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2
 repage
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2
 process
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 11
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
12
 doCrop
n/a
0 / 0
n/a
0 / 0
0
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Effect;
4
5use Exception;
6use InvalidArgumentException;
7use Charcoal\Image\AbstractEffect;
8
9/**
10 * Resize an image to given dimensions
11 */
12abstract class AbstractCropEffect extends AbstractEffect
13{
14    private int $x = 0;
15
16    private int $y = 0;
17
18    private int $width = 0;
19
20    private int $height = 0;
21
22    private string|float|int|null $geometry = null;
23
24    /**
25     * @var string $gravity
26     */
27    private $gravity = 'center';
28
29    private bool $repage = false;
30
31    /**
32     * @param  integer $width The crop width.
33     * @throws InvalidArgumentException If the width argument is not valid.
34     * @return AbstractCropEffect
35     */
36    public function setWidth($width)
37    {
38        if (!is_int($width) || ($width < 0)) {
39            throw new InvalidArgumentException(
40                'Width must be a a positive integer'
41            );
42        }
43        $this->width = $width;
44        return $this;
45    }
46
47    /**
48     * @return integer
49     */
50    public function width()
51    {
52        return $this->width;
53    }
54
55    /**
56     * @param  integer $height The crop height.
57     * @throws InvalidArgumentException If the height argument is not valid.
58     * @return AbstractCropEffect
59     */
60    public function setHeight($height)
61    {
62        if (!is_int($height) || ($height < 0)) {
63            throw new InvalidArgumentException(
64                'Height must be a positive integer'
65            );
66        }
67        $this->height = $height;
68        return $this;
69    }
70
71    /**
72     * @return integer
73     */
74    public function height()
75    {
76        return $this->height;
77    }
78
79    /**
80     * The X coordinate of the cropped region's top left corner
81     *
82     * @param  integer $x The x-position (in pixel) of the crop.
83     * @throws InvalidArgumentException If the x argument is not valid.
84     * @return AbstractCropEffect
85     */
86    public function setX($x)
87    {
88        if (!is_int($x) || ($x < 0)) {
89            throw new InvalidArgumentException(
90                'Height must be a positive integer'
91            );
92        }
93        $this->x = $x;
94        return $this;
95    }
96
97    /**
98     * @return integer
99     */
100    public function x()
101    {
102        return $this->x;
103    }
104
105    /**
106     * The Y coordinate of the cropped region's top left corner
107     *
108     * @param  integer $y The y-position (in pixel) of the crop.
109     * @throws InvalidArgumentException If the y argumnet is not valid.
110     * @return AbstractCropEffect
111     */
112    public function setY($y)
113    {
114        if (!is_int($y) || ($y < 0)) {
115            throw new InvalidArgumentException(
116                'Height must be a positive integer'
117            );
118        }
119        $this->y = $y;
120        return $this;
121    }
122
123    /**
124     * @return integer
125     */
126    public function y()
127    {
128        return $this->y;
129    }
130
131    /**
132     * Set a complex geometry value.
133     *
134     * @param  mixed $geometry The image geometry.
135     * @throws InvalidArgumentException If the geometry argument is not valid.
136     * @return AbstractCropEffect
137     */
138    public function setGeometry($geometry)
139    {
140        if ($geometry !== null && !is_string($geometry) && !is_numeric($geometry)) {
141            throw new InvalidArgumentException(
142                'Geometry must be a valid crop'
143            );
144        }
145        $this->geometry = $geometry;
146        return $this;
147    }
148
149    /**
150     * Retrieve the complex geometry value.
151     *
152     * @return mixed
153     */
154    public function geometry()
155    {
156        return $this->geometry;
157    }
158
159    /**
160     * @param  string $gravity The crop gravity.
161     * @throws InvalidArgumentException If the argument is not a valid gravity name.
162     * @return AbstractCropEffect
163     */
164    public function setGravity($gravity)
165    {
166        if (!in_array($gravity, $this->image()->availableGravities())) {
167            throw new InvalidArgumentException(
168                'Gravity is not valid'
169            );
170        }
171        $this->gravity = $gravity;
172        return $this;
173    }
174
175    /**
176     * @return string
177     */
178    public function gravity()
179    {
180        return $this->gravity;
181    }
182
183    /**
184     * @param  boolean $repage The repage image flag.
185     * @return AbstractCropEffect
186     */
187    public function setRepage($repage)
188    {
189        $this->repage = (bool)$repage;
190        return $this;
191    }
192
193    /**
194     * @return boolean
195     */
196    public function repage()
197    {
198        return $this->repage;
199    }
200
201    /**
202     * @param  array $data The effect data.
203     * @return AbstractCropEffect
204     */
205    public function process(?array $data = null)
206    {
207        if ($data !== null) {
208            $this->setData($data);
209        }
210
211        if ($this->geometry()) {
212            $this->doCrop(0, 0, 0, 0);
213            return $this;
214        }
215
216        $y = $this->y();
217        $x = $this->x();
218        $width  = $this->width();
219        $height = $this->height();
220
221        $this->doCrop($width, $height, $x, $y);
222
223        return $this;
224    }
225
226    /**
227     * @param  integer $width  The crop width.
228     * @param  integer $height The crop height.
229     * @param  integer $x      The x-position (in pixel) of the crop.
230     * @param  integer $y      The y-position (in pixel) of the crop.
231     * @return void
232     */
233    abstract protected function doCrop($width, $height, $x, $y);
234}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Effect/AbstractDitherEffect.php.html b/packages/image/build/report/Effect/AbstractDitherEffect.php.html deleted file mode 100644 index df99f67ff..000000000 --- a/packages/image/build/report/Effect/AbstractDitherEffect.php.html +++ /dev/null @@ -1,306 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/AbstractDitherEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 100.00% covered (success) -
-
-
100.00%
36 / 36
-
- 100.00% covered (success) -
-
-
100.00%
4 / 4
CRAP
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
AbstractDitherEffect
-
- 100.00% covered (success) -
-
-
100.00%
36 / 36
-
- 100.00% covered (success) -
-
-
100.00%
4 / 4
6
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
 setColors
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
2
 colors
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 setMode
-
- 100.00% covered (success) -
-
-
100.00%
28 / 28
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
2
 mode
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image\Effect;
6
7use InvalidArgumentException;
8use Charcoal\Image\AbstractEffect;
9
10/**
11 * AbstractDitherEffect an image to a reduced number of colors
12 */
13abstract class AbstractDitherEffect extends AbstractEffect
14{
15    private int $colors = 16;
16    private string $mode = '';
17
18    /**
19     * @param integer $colors The numver of dither colors.
20     * @throws InvalidArgumentException If the argument is not numeric.
21     * @return AbstractDitherEffect Chainable
22     */
23    public function setColors($colors)
24    {
25        if (!is_numeric($colors)) {
26            throw new InvalidArgumentException(
27                'Colors must be an integer'
28            );
29        }
30        $this->colors = (int)$colors;
31        return $this;
32    }
33
34    /**
35     * @return integer
36     */
37    public function colors()
38    {
39        return $this->colors;
40    }
41
42    /**
43     * @param string $mode The dither mode.
44     * @throws InvalidArgumentException If the argument is not a valid dither mode.
45     * @return AbstractDitherEffect Chainable
46     */
47    public function setMode($mode)
48    {
49        $allowedModes = [
50            '',
51// Quantize
52            'threshold',
53            'checks',
54            'o2x2',
55            'o3x3',
56            'o4x4',
57            'o8x8',
58            'h4x4a',
59            'h6x6a',
60            'h8x8a',
61            'h4x4o',
62            'h6x6o',
63            'h8x8o',
64            'h16x16o',
65            'c5x5b',
66            'c5x5w',
67            'c6x6b',
68            'c6x6w',
69            'c7x7b',
70            'c7x7w'
71        ];
72        if (!in_array($mode, $allowedModes)) {
73            throw new InvalidArgumentException(
74                'Invalid dither mode'
75            );
76        }
77        $this->mode = $mode;
78        return $this;
79    }
80
81    /**
82     * @return string
83     */
84    public function mode()
85    {
86        return $this->mode;
87    }
88}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Effect/AbstractFormatEffect.php.html b/packages/image/build/report/Effect/AbstractFormatEffect.php.html deleted file mode 100644 index 773a0d1da..000000000 --- a/packages/image/build/report/Effect/AbstractFormatEffect.php.html +++ /dev/null @@ -1,216 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/AbstractFormatEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 11
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 2
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
AbstractFormatEffect
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 11
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 2
12
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 setFormat
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 10
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
6
 format
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Effect;
4
5use InvalidArgumentException;
6use Charcoal\Image\AbstractEffect;
7
8/**
9 * Format (or colorize) the image with a certain color.
10 */
11abstract class AbstractFormatEffect extends AbstractEffect
12{
13    protected $format;
14
15    public const ACCEPTED_FORMAT = [ 'webp', 'jpg', 'jpeg' ];
16
17    /**
18     * Must be one of the accepted format
19     *
20     * @var string $format
21     * @throws InvalidArgumentException If the format is not supported
22     */
23    public function setFormat(string $format)
24    {
25        if (!in_array($format, static::ACCEPTED_FORMAT)) {
26            throw new InvalidArgumentException(
27                sprintf(
28                    'Invalid image format provided. Must be one of %s. %s provided.',
29                    implode(',', static::ACCEPTED_FORMAT),
30                    $format
31                )
32            );
33        }
34        $this->format = $format;
35        return $this;
36    }
37
38    public function format()
39    {
40        return $this->format;
41    }
42}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Effect/AbstractGrayscaleEffect.php.html b/packages/image/build/report/Effect/AbstractGrayscaleEffect.php.html deleted file mode 100644 index d6470c7d5..000000000 --- a/packages/image/build/report/Effect/AbstractGrayscaleEffect.php.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/AbstractGrayscaleEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
AbstractGrayscaleEffect
n/a
0 / 0
n/a
0 / 0
0
n/a
0 / 0
-
- - - - - - - - - - - - - - - - - - - -
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image\Effect;
6
7use Charcoal\Image\AbstractEffect;
8
9/**
10 * Convert an image to grayscale colorspace
11 */
12abstract class AbstractGrayscaleEffect extends AbstractEffect
13{
14    // This effect does not have any options.
15}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Effect/AbstractMaskEffect.php.html b/packages/image/build/report/Effect/AbstractMaskEffect.php.html deleted file mode 100644 index 94eaa3cd8..000000000 --- a/packages/image/build/report/Effect/AbstractMaskEffect.php.html +++ /dev/null @@ -1,222 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/AbstractMaskEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 100.00% covered (success) -
-
-
100.00%
7 / 7
-
- 100.00% covered (success) -
-
-
100.00%
2 / 2
CRAP
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
AbstractMaskEffect
-
- 100.00% covered (success) -
-
-
100.00%
7 / 7
-
- 100.00% covered (success) -
-
-
100.00%
2 / 2
4
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
 setMask
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
3
 mask
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image\Effect;
6
7use InvalidArgumentException;
8use Charcoal\Image\AbstractEffect;
9use Charcoal\Image\ImageInterface;
10use Charcoal\Image\Effect\LayerEffectInterface;
11use Charcoal\Image\Effect\LayerEffectTrait;
12
13/**
14 * Composite an opacity mask on top of the image
15 */
16abstract class AbstractMaskEffect extends AbstractEffect implements LayerEffectInterface
17{
18    use LayerEffectTrait;
19
20    /**
21     * The mask image source
22     */
23    private ?string $mask = null;
24
25    /**
26     * @param string $mask The mask image source.
27     * @throws InvalidArgumentException If the mask source is not a string.
28     * @return AbstractMaskEffect Chainable
29     */
30    public function setMask($mask)
31    {
32        if (!is_string($mask) || ($mask instanceof ImageInterface)) {
33            throw new InvalidArgumentException(
34                'Mask must be a string'
35            );
36        }
37        $this->mask = $mask;
38        return $this;
39    }
40
41    /**
42     * @return string
43     */
44    public function mask()
45    {
46        return $this->mask;
47    }
48}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Effect/AbstractMirrorEffect.php.html b/packages/image/build/report/Effect/AbstractMirrorEffect.php.html deleted file mode 100644 index 21ee58308..000000000 --- a/packages/image/build/report/Effect/AbstractMirrorEffect.php.html +++ /dev/null @@ -1,218 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/AbstractMirrorEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 100.00% covered (success) -
-
-
100.00%
8 / 8
-
- 100.00% covered (success) -
-
-
100.00%
2 / 2
CRAP
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
AbstractMirrorEffect
-
- 100.00% covered (success) -
-
-
100.00%
8 / 8
-
- 100.00% covered (success) -
-
-
100.00%
2 / 2
4
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
 setAxis
-
- 100.00% covered (success) -
-
-
100.00%
7 / 7
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
3
 axis
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image\Effect;
6
7use InvalidArgumentException;
8use Charcoal\Image\AbstractEffect;
9
10/**
11 * Flip an image horizontally or vertically.
12 */
13abstract class AbstractMirrorEffect extends AbstractEffect
14{
15    /**
16     * Axis can be "x" (flip) or "y" (flop)
17     */
18    private string $axis = 'y';
19
20    /**
21     * @param string $axis The mirror axis.
22     * @throws InvalidArgumentException If the argument is not x or y.
23     * @return AbstractMirrorEffect Chainable
24     */
25    public function setAxis($axis)
26    {
27        $allowedVals = ['x', 'y'];
28        if (!is_string($axis) || !in_array($axis, $allowedVals)) {
29            throw new InvalidArgumentException(
30                'Axis must be "x" or "y"'
31            );
32        }
33        $this->axis = $axis;
34        return $this;
35    }
36
37    /**
38     * @return string
39     */
40    public function axis()
41    {
42        return $this->axis;
43    }
44}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Effect/AbstractModulateEffect.php.html b/packages/image/build/report/Effect/AbstractModulateEffect.php.html deleted file mode 100644 index 51bcab45a..000000000 --- a/packages/image/build/report/Effect/AbstractModulateEffect.php.html +++ /dev/null @@ -1,366 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/AbstractModulateEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 100.00% covered (success) -
-
-
100.00%
21 / 21
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
CRAP
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
AbstractModulateEffect
-
- 100.00% covered (success) -
-
-
100.00%
21 / 21
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
15
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
 setHue
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
4
 hue
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 setSaturation
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
4
 saturation
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 setLuminance
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
4
 luminance
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image\Effect;
6
7use InvalidArgumentException;
8use Charcoal\Image\AbstractEffect;
9
10/**
11 * Modifies an image's colors in the special HSL (hue-saturation-luminance) colorspace.
12 */
13abstract class AbstractModulateEffect extends AbstractEffect
14{
15    /**
16     * The color tint (-100 to 100)
17     * @var float $hue
18     */
19    private $hue = 0;
20
21    /**
22     * The color intensity (-100 to 100)
23     * @var float $saturation
24     */
25    private $saturation = 0;
26
27    /**
28     * The brightness (-100 to 100)
29     * @var float $luminance
30     */
31    private $luminance = 0;
32
33    /**
34     * @param float $hue The modulate hue.
35     * @throws InvalidArgumentException If the argument is not numeric or within valid range.
36     * @return AbstractModulateEffect Chainable
37     */
38    public function setHue($hue)
39    {
40        if (!is_numeric($hue) || ($hue < -100) || ($hue > 100)) {
41            throw new InvalidArgumentException(
42                'Hue (color tint) must be a float between 0 and 200'
43            );
44        }
45        $this->hue = (float)$hue;
46        return $this;
47    }
48
49    /**
50     * @return float
51     */
52    public function hue()
53    {
54        return $this->hue;
55    }
56
57    /**
58     * @param float $saturation The modulate saturation.
59     * @throws InvalidArgumentException If the argument is not numeric or within valid range.
60     * @return AbstractModulateEffect Chainable
61     */
62    public function setSaturation($saturation)
63    {
64        if (!is_numeric($saturation) || ($saturation < -100) || ($saturation > 100)) {
65            throw new InvalidArgumentException(
66                'Saturation (color intensity) must be a float between 0 and 200'
67            );
68        }
69        $this->saturation = (float)$saturation;
70        return $this;
71    }
72
73    /**
74     * @return float
75     */
76    public function saturation()
77    {
78        return $this->saturation;
79    }
80
81    /**
82     * @param float $luminance The modulate luminance.
83     * @throws InvalidArgumentException If the argument is not numeric or within valid range.
84     * @return AbstractModulateEffect Chainable
85     */
86    public function setLuminance($luminance)
87    {
88        if (!is_numeric($luminance) || ($luminance < -100) || ($luminance > 100)) {
89            throw new InvalidArgumentException(
90                'Luminance (brightness) must be a float between 0 and 200'
91            );
92        }
93        $this->luminance = (float)$luminance;
94        return $this;
95    }
96
97    /**
98     * @return float
99     */
100    public function luminance()
101    {
102        return $this->luminance;
103    }
104}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Effect/AbstractResizeEffect.php.html b/packages/image/build/report/Effect/AbstractResizeEffect.php.html deleted file mode 100644 index e045517e8..000000000 --- a/packages/image/build/report/Effect/AbstractResizeEffect.php.html +++ /dev/null @@ -1,1142 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/AbstractResizeEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 58.62% covered (warning) -
-
-
58.62%
102 / 174
-
- 75.00% covered (warning) -
-
-
75.00%
18 / 24
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
AbstractResizeEffect
-
- 58.62% covered (warning) -
-
-
58.62%
102 / 174
-
- 75.00% covered (warning) -
-
-
75.00%
18 / 24
650.22
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 setMode
-
- 100.00% covered (success) -
-
-
100.00%
17 / 17
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
3
 mode
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 setSize
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
5
 size
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 setWidth
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
3
 width
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 setHeight
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
3
 height
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 setMinWidth
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 6
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
12
 minWidth
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 setMinHeight
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 6
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
12
 minHeight
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 setMaxWidth
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 6
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
12
 maxWidth
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 setMaxHeight
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 6
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
12
 maxHeight
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 setGravity
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
2
 gravity
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 setBackgroundColor
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
2
 backgroundColor
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 setAdaptive
-
- 100.00% covered (success) -
-
-
100.00%
2 / 2
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 adaptive
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 autoMode
-
- 90.91% covered (success) -
-
-
90.91%
10 / 11
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
9.06
 process
-
- 40.51% covered (warning) -
-
-
40.51%
32 / 79
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
342.07
 doResize
n/a
0 / 0
n/a
0 / 0
0
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Effect;
4
5use Exception;
6use InvalidArgumentException;
7use Charcoal\Image\AbstractEffect;
8
9/**
10 * Resize an image to given dimensions
11 */
12abstract class AbstractResizeEffect extends AbstractEffect
13{
14    private string $mode = 'auto';
15
16    private string|float|int|null $size = null;
17
18    private int $width = 0;
19
20    private int $height = 0;
21
22    private int $minWidth = 0;
23
24    private int $minHeight = 0;
25
26    private int $maxWidth = 0;
27
28    private int $maxHeight = 0;
29
30    /**
31     * @var string $gravity
32     */
33    private $gravity = 'center';
34
35    private string $backgroundColor = 'rgba(100%, 100%, 100%, 0)';
36
37    private bool $adaptive = false;
38
39    /**
40     * @param string $mode The resize mode.
41     * @throws InvalidArgumentException If the mode argument is not a valid resize mode.
42     * @return self
43     */
44    public function setMode($mode)
45    {
46        $allowedModes = [
47            'auto',
48            'exact',
49            'width',
50            'height',
51            'best_fit',
52            'constraints',
53            'crop',
54            'fill',
55            'none'
56        ];
57        if (!is_string($mode) || (!in_array($mode, $allowedModes))) {
58            throw new InvalidArgumentException(
59                'Mode is not valid'
60            );
61        }
62        $this->mode = $mode;
63        return $this;
64    }
65
66    /**
67     * @return string
68     */
69    public function mode()
70    {
71        return $this->mode;
72    }
73
74    /**
75     * Set a complex resize value.
76     *
77     * @param  mixed $size The size.
78     * @throws InvalidArgumentException If the size argument is not valid.
79     * @return self
80     */
81    public function setSize($size)
82    {
83        if ($size !== null && !is_string($size) && (!is_numeric($size) || ($size < 0))) {
84            throw new InvalidArgumentException(
85                'Size must be a valid scale'
86            );
87        }
88
89        $this->size = $size;
90
91        return $this;
92    }
93
94    /**
95     * Retrieve the complex resize value.
96     *
97     * @return mixed
98     */
99    public function size()
100    {
101        return $this->size;
102    }
103
104    /**
105     * @param integer $width The target resize width.
106     * @throws InvalidArgumentException If the width argument is not numeric or lower than 0.
107     * @return self
108     */
109    public function setWidth($width)
110    {
111        if (!is_numeric($width) || ($width < 0)) {
112            throw new InvalidArgumentException(
113                'Width must be a positive integer'
114            );
115        }
116        $this->width = (int)$width;
117        return $this;
118    }
119
120    /**
121     * @return float
122     */
123    public function width()
124    {
125        return $this->width;
126    }
127
128    /**
129     * @param integer $height The target resize height.
130     * @throws InvalidArgumentException If the height argument is not numeric or lower than 0.
131     * @return self
132     */
133    public function setHeight($height)
134    {
135        if (!is_int($height) || ($height < 0)) {
136            throw new InvalidArgumentException(
137                'Height must be a positive integer'
138            );
139        }
140        $this->height = $height;
141        return $this;
142    }
143
144    /**
145     * @return float
146     */
147    public function height()
148    {
149        return $this->height;
150    }
151
152    /**
153     * @param integer $minWidth The resize minimal width.
154     * @throws InvalidArgumentException If the argument is not numeric or lower than 0.
155     * @return self
156     */
157    public function setMinWidth($minWidth)
158    {
159        if (!is_numeric($minWidth) || ($minWidth < 0)) {
160            throw new InvalidArgumentException(
161                'Min Width must be a positive integer'
162            );
163        }
164        $this->minWidth = (int)$minWidth;
165        return $this;
166    }
167
168    /**
169     * @return float
170     */
171    public function minWidth()
172    {
173        return $this->minWidth;
174    }
175
176    /**
177     * @param integer $minHeight The resize minimal height.
178     * @throws InvalidArgumentException If the argument is not numeric or lower than 0.
179     * @return self
180     */
181    public function setMinHeight($minHeight)
182    {
183        if (!is_numeric($minHeight) || ($minHeight < 0)) {
184            throw new InvalidArgumentException(
185                'Min Height must be a positive integer'
186            );
187        }
188        $this->minHeight = (int)$minHeight;
189        return $this;
190    }
191
192    /**
193     * @return float
194     */
195    public function minHeight()
196    {
197        return $this->minHeight;
198    }
199
200    /**
201     * @param integer $maxWidth The resize max width.
202     * @throws InvalidArgumentException If the argument is not numeric or lower than 0.
203     * @return self
204     */
205    public function setMaxWidth($maxWidth)
206    {
207        if (!is_numeric($maxWidth) || ($maxWidth < 0)) {
208            throw new InvalidArgumentException(
209                'Max Width must be a positive integer'
210            );
211        }
212        $this->maxWidth = (int)$maxWidth;
213        return $this;
214    }
215
216    /**
217     * @return float
218     */
219    public function maxWidth()
220    {
221        return $this->maxWidth;
222    }
223
224    /**
225     * @param integer $maxHeight The resize max height.
226     * @throws InvalidArgumentException If the argument is not numeric or lower than 0.
227     * @return self
228     */
229    public function setMaxHeight($maxHeight)
230    {
231        if (!is_numeric($maxHeight) || ($maxHeight < 0)) {
232            throw new InvalidArgumentException(
233                'Height must be a positive integer'
234            );
235        }
236        $this->maxHeight = (int)$maxHeight;
237        return $this;
238    }
239
240    /**
241     * @return float
242     */
243    public function maxHeight()
244    {
245        return $this->maxHeight;
246    }
247
248    /**
249     * @param string $gravity The resize gravity.
250     * @throws InvalidArgumentException If the argument is not a valid gravity name.
251     * @return self
252     */
253    public function setGravity($gravity)
254    {
255        if (!in_array($gravity, $this->image()->availableGravities())) {
256            throw new InvalidArgumentException(
257                'Gravity is not valid'
258            );
259        }
260        $this->gravity = $gravity;
261        return $this;
262    }
263
264    /**
265     * @return string
266     */
267    public function gravity()
268    {
269        return $this->gravity;
270    }
271
272    /**
273     * @param string $color The resize background color.
274     * @throws InvalidArgumentException If the color argument is not a string.
275     * @return self
276     */
277    public function setBackgroundColor($color)
278    {
279        if (!is_string($color)) {
280            throw new InvalidArgumentException(
281                'Color must be a string'
282            );
283        }
284        $this->backgroundColor = $color;
285        return $this;
286    }
287
288    /**
289     * @return string
290     */
291    public function backgroundColor()
292    {
293        return $this->backgroundColor;
294    }
295
296    /**
297     * @param boolean $adaptive The adaptative resize flag.
298     * @return self
299     */
300    public function setAdaptive($adaptive)
301    {
302        $this->adaptive = (bool)$adaptive;
303        return $this;
304    }
305
306    /**
307     * @return boolean
308     */
309    public function adaptive()
310    {
311        return $this->adaptive;
312    }
313
314    /**
315     * @return string
316     */
317    public function autoMode()
318    {
319        $width = $this->width();
320        $height = $this->height();
321
322        if ($width > 0 && $height > 0) {
323            return 'exact';
324        } elseif ($width > 0) {
325            return 'width';
326        } elseif ($height > 0) {
327            return 'height';
328        } elseif ($this->minWidth() || $this->minHeight() || $this->maxWidth() || $this->maxHeight()) {
329            return 'constraints';
330        } else {
331            return 'none';
332        }
333    }
334
335    /**
336     * @param array $data The effect data, if available.
337     * @throws Exception If the effect data is invalid for its resize mode.
338     * @return self
339     */
340    public function process(?array $data = null)
341    {
342        if ($data !== null) {
343            $this->setData($data);
344        }
345
346        $mode = $this->mode();
347        if ($mode == 'auto') {
348            $mode = $this->autoMode();
349        }
350
351        $size = $this->size();
352        if ($size) {
353            $this->doResize(0, 0, false);
354            return $this;
355        }
356
357        if ($mode == 'none') {
358            // Noting to do.
359            return $this;
360        }
361
362        $imageWidth  = $this->image()->width();
363        $imageHeight = $this->image()->height();
364
365        if ($imageWidth == 0 || $imageHeight == 0) {
366            throw new Exception(
367                'Can not process image; invalid image (0 width or height)'
368            );
369        }
370
371        switch ($mode) {
372            case 'exact':
373                if (($this->width() <= 0) || ($this->height() <= 0)) {
374                    throw new Exception(
375                        'Missing parameters to perform exact resize'
376                    );
377                }
378                if ($imageWidth != $this->width() || $imageHeight != $this->height()) {
379                    $this->doResize($this->width(), $this->height(), false);
380                }
381                break;
382
383            case 'width':
384                if ($this->width() <= 0) {
385                    throw new Exception(
386                        'Missing parameters to perform exact width resize'
387                    );
388                }
389                if ($imageWidth != $this->width()) {
390                    $this->doResize($this->width(), 0, false);
391                }
392                break;
393
394            case 'height':
395                if ($this->height() <= 0) {
396                    throw new Exception(
397                        'Missing parameters to perform exact height resize'
398                    );
399                }
400                if ($imageHeight != $this->height()) {
401                    $this->doResize(0, $this->height(), false);
402                }
403                break;
404
405            case 'best_fit':
406                if (($this->width() <= 0) || ($this->height() <= 0)) {
407                    throw new Exception(
408                        'Missing parameters to perform "best fit" resize'
409                    );
410                }
411                if ($imageWidth != $this->width() || $imageHeight != $this->height()) {
412                    $this->doResize($this->width(), $this->height(), true);
413                }
414                break;
415
416            case 'constraints':
417                $minW = $this->minWidth();
418                $minH = $this->minHeight();
419                $maxW = $this->maxWidth();
420                $maxH = $this->maxHeight();
421
422                if (array_sum([$minW, $minH, $maxW, $maxH]) == 0) {
423                    throw new Exception(
424                        'Missing parameter(s) to perform "constraints" resize'
425                    );
426                }
427
428                if (($minW && ($minW > $imageWidth)) || ($minH && ($minH > $imageHeight))) {
429                    // Must scale up, keeping ratio
430                    $this->doResize($minW, $minH, true);
431                    break;
432                }
433
434                if (($maxW && ($maxW < $imageWidth)) || ($maxH && ($maxH < $imageHeight))) {
435                    // Must scale down. keeping ratio
436                    $this->doResize($maxW, $maxH, true);
437                    break;
438                }
439                break;
440
441            case 'crop':
442                throw new Exception(
443                    'Crop resize mode is not (yet) supported'
444                );
445
446            case 'fill':
447                $newWidth = $this->width();
448                $newHeight = $this->height();
449
450                $oldRatio = $imageHeight ? ($imageWidth / $imageHeight) : 0;
451                $newRatio = $newHeight ? ($newWidth / $newHeight) : 0;
452
453                if ($newRatio > $oldRatio) {
454                    $newHeight = ($imageHeight * $this->width() / $imageWidth);
455                } else {
456                    $newWidth = ($imageWidth * $this->height() / $imageHeight);
457                }
458
459                $this->doResize($newWidth, $newHeight);
460        }
461
462        return $this;
463    }
464
465    /**
466     * @param integer $width   The target width.
467     * @param integer $height  The target height.
468     * @param boolean $bestFit The "best_fit" flag.
469     * @return void
470     */
471    abstract protected function doResize($width, $height, $bestFit = false);
472}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Effect/AbstractRevertEffect.php.html b/packages/image/build/report/Effect/AbstractRevertEffect.php.html deleted file mode 100644 index 338468aa8..000000000 --- a/packages/image/build/report/Effect/AbstractRevertEffect.php.html +++ /dev/null @@ -1,215 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/AbstractRevertEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 100.00% covered (success) -
-
-
100.00%
7 / 7
-
- 100.00% covered (success) -
-
-
100.00%
2 / 2
CRAP
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
AbstractRevertEffect
-
- 100.00% covered (success) -
-
-
100.00%
7 / 7
-
- 100.00% covered (success) -
-
-
100.00%
2 / 2
3
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
 setChannel
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
2
 channel
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Effect;
4
5use InvalidArgumentException;
6use Charcoal\Image\AbstractEffect;
7
8/**
9 * Revert (negate) the image's colors.
10 */
11abstract class AbstractRevertEffect extends AbstractEffect
12{
13    /**
14     * @var string $channel
15     */
16    private $channel = 'all';
17
18    /**
19     * @param string $channel The channel to revert.
20     * @throws InvalidArgumentException If the channel argument is not a valid channel.
21     * @return self
22     */
23    public function setChannel($channel)
24    {
25        if (!in_array($channel, $this->image()->availableChannels())) {
26            throw new InvalidArgumentException(
27                'Channel is not valid'
28            );
29        }
30        $this->channel = $channel;
31        return $this;
32    }
33
34    /**
35     * @return string
36     */
37    public function channel()
38    {
39        return $this->channel;
40    }
41}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Effect/AbstractRotateEffect.php.html b/packages/image/build/report/Effect/AbstractRotateEffect.php.html deleted file mode 100644 index 5c6159bb4..000000000 --- a/packages/image/build/report/Effect/AbstractRotateEffect.php.html +++ /dev/null @@ -1,292 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/AbstractRotateEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 100.00% covered (success) -
-
-
100.00%
14 / 14
-
- 100.00% covered (success) -
-
-
100.00%
4 / 4
CRAP
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
AbstractRotateEffect
-
- 100.00% covered (success) -
-
-
100.00%
14 / 14
-
- 100.00% covered (success) -
-
-
100.00%
4 / 4
6
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
 setAngle
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
2
 angle
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 setBackgroundColor
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
2
 backgroundColor
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image\Effect;
6
7use InvalidArgumentException;
8use Charcoal\Image\AbstractEffect;
9
10/**
11 * Rotate the image by a certain angle
12 */
13abstract class AbstractRotateEffect extends AbstractEffect
14{
15    /**
16     * The angle of rotation, in degrees, clockwise
17     * @var float $Angle
18     */
19    private $angle = 0;
20
21    /**
22     * The background color, for non-90-multiple rotation
23     * Defaults to transparent
24     */
25    private string $backgroundColor = 'rgb(100%, 100%, 100%, 0)';
26
27    /**
28     * @param float $angle The rotation angle.
29     * @throws InvalidArgumentException If the angle argument is not numeric.
30     * @return AbstractRotateEffect Chainable
31     */
32    public function setAngle($angle)
33    {
34        if (!is_numeric($angle)) {
35            throw new InvalidArgumentException(
36                'Angle must be a float'
37            );
38        }
39        $this->angle = (float)$angle;
40        return $this;
41    }
42
43    /**
44     * @return float
45     */
46    public function angle()
47    {
48        return $this->angle;
49    }
50
51    /**
52     * @param string $color The background color, for non-90 rotations.
53     * @throws InvalidArgumentException If the color argument is not a string.
54     * @return AbstractRotateEffect Chainable
55     */
56    public function setBackgroundColor($color)
57    {
58        if (!is_string($color)) {
59            throw new InvalidArgumentException(
60                'Color must be a string'
61            );
62        }
63        $this->backgroundColor = $color;
64        return $this;
65    }
66
67    /**
68     * @return string
69     */
70    public function backgroundColor()
71    {
72        return $this->backgroundColor;
73    }
74}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Effect/AbstractSepiaEffect.php.html b/packages/image/build/report/Effect/AbstractSepiaEffect.php.html deleted file mode 100644 index d53feece9..000000000 --- a/packages/image/build/report/Effect/AbstractSepiaEffect.php.html +++ /dev/null @@ -1,219 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/AbstractSepiaEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 100.00% covered (success) -
-
-
100.00%
8 / 8
-
- 100.00% covered (success) -
-
-
100.00%
2 / 2
CRAP
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
AbstractSepiaEffect
-
- 100.00% covered (success) -
-
-
100.00%
8 / 8
-
- 100.00% covered (success) -
-
-
100.00%
2 / 2
5
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
 setThreshold
-
- 100.00% covered (success) -
-
-
100.00%
7 / 7
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
4
 threshold
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image\Effect;
6
7use InvalidArgumentException;
8use Charcoal\Image\AbstractEffect;
9
10/**
11 * Convert an image to grayscale colorspace
12 */
13abstract class AbstractSepiaEffect extends AbstractEffect
14{
15    /**
16     * @var float $threshold
17     */
18    private $threshold = 75;
19
20
21    /**
22     * @param float $threshold The sepia threshold value.
23     * @throws InvalidArgumentException If the threshold argument is not numeric, lower than 0 or greater than max.
24     * @return AbstractSepiaEffect Chainable
25     */
26    public function setThreshold($threshold)
27    {
28        $max = 255;
29        if (!is_numeric($threshold) || ($threshold < 0) || ($threshold > $max)) {
30            throw new InvalidArgumentException(
31                sprintf('Threshold must be a number between 0 and %s', $max)
32            );
33        }
34        $this->threshold = (float)$threshold;
35        return $this;
36    }
37
38    /**
39     * @return float
40     */
41    public function threshold()
42    {
43        return $this->threshold;
44    }
45}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Effect/AbstractSharpenEffect.php.html b/packages/image/build/report/Effect/AbstractSharpenEffect.php.html deleted file mode 100644 index 9ce6b7ce4..000000000 --- a/packages/image/build/report/Effect/AbstractSharpenEffect.php.html +++ /dev/null @@ -1,672 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/AbstractSharpenEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 96.08% covered (success) -
-
-
96.08%
49 / 51
-
- 92.31% covered (success) -
-
-
92.31%
12 / 13
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
AbstractSharpenEffect
-
- 96.08% covered (success) -
-
-
96.08%
49 / 51
-
- 92.31% covered (success) -
-
-
92.31%
12 / 13
27
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 setRadius
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
3
 radius
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 setSigma
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
3
 sigma
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 setAmount
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
3
 amount
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 setThreshold
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
3
 threshold
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 setMode
-
- 100.00% covered (success) -
-
-
100.00%
7 / 7
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
2
 mode
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 setChannel
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
2
 channel
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 process
-
- 75.00% covered (warning) -
-
-
75.00%
6 / 8
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
5.39
 processAdaptive
n/a
0 / 0
n/a
0 / 0
0
 processUnsharp
n/a
0 / 0
n/a
0 / 0
0
 processStandard
n/a
0 / 0
n/a
0 / 0
0
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Effect;
4
5use InvalidArgumentException;
6use Charcoal\Image\AbstractEffect;
7
8/**
9 * Sharpen an image, with a simple sharpen algorithm or unsharp mask options
10 */
11abstract class AbstractSharpenEffect extends AbstractEffect
12{
13    /**
14     * @var float $radius
15     */
16    private $radius = 0;
17
18    /**
19     * @var float $sigma
20     */
21    private $sigma = 1;
22
23    /**
24     * Amount (or _gain_) to unsharp. Only used in `unsharp` mode
25     * @var float $amount
26     */
27    private $amount = 1;
28
29    /**
30     * Threshold. Ony used in `unsharp` mode
31     */
32    private float $threshold = 0.05;
33
34    private string $mode = 'standard';
35
36    /**
37     * @var string $channel
38     */
39    private $channel = 'all';
40
41
42    /**
43     * @param float $radius The sharpen radius value.
44     * @throws InvalidArgumentException If the radius argument is not numeric or lower than 0.
45     * @return self
46     */
47    public function setRadius($radius)
48    {
49        if (!is_numeric($radius) || ($radius < 0)) {
50            throw new InvalidArgumentException(
51                'Radius must be a float (greater than 0)'
52            );
53        }
54         $this->radius = (float)$radius;
55         return $this;
56    }
57
58    /**
59     * @return float
60     */
61    public function radius()
62    {
63        return $this->radius;
64    }
65
66    /**
67     * @param float $sigma The sharpen sigma value.
68     * @throws InvalidArgumentException If the ssigma value is not numeric or lower than 0.
69     * @return self
70     */
71    public function setSigma($sigma)
72    {
73        if (!is_numeric($sigma) || ($sigma < 0)) {
74            throw new InvalidArgumentException(
75                'Sigma value must be a float (greater than 0)'
76            );
77        }
78        $this->sigma = (float)$sigma;
79        return $this;
80    }
81
82    /**
83     * @return float
84     */
85    public function sigma()
86    {
87        return $this->sigma;
88    }
89
90    /**
91     * @param float $amount The sharpen amount.
92     * @throws InvalidArgumentException If the amount argument is not numeric or lower than 0.
93     * @return self
94     */
95    public function setAmount($amount)
96    {
97        if (!is_numeric($amount) || ($amount < 0)) {
98            throw new InvalidArgumentException(
99                'Threshold must be a float (greater than 0)'
100            );
101        }
102         $this->amount = (float)$amount;
103         return $this;
104    }
105
106    /**
107     * @return float
108     */
109    public function amount()
110    {
111        return $this->amount;
112    }
113
114    /**
115     * @param float $threshold The sharpen threshold value.
116     * @throws InvalidArgumentException If the threshold argumnet is not numeric or lower than 0.
117     * @return self
118     */
119    public function setThreshold($threshold)
120    {
121        if (!is_numeric($threshold) || ($threshold < 0)) {
122            throw new InvalidArgumentException(
123                'Threshold must be a float (greater than 0)'
124            );
125        }
126         $this->threshold = (float)$threshold;
127         return $this;
128    }
129
130    /**
131     * @return float
132     */
133    public function threshold()
134    {
135        return $this->threshold;
136    }
137
138    /**
139     * @param string $mode The sharpen mode.
140     * @throws InvalidArgumentException If the mode argument is not a valid sharpen mode.
141     * @return self
142     */
143    public function setMode($mode)
144    {
145        $allowedModes = ['standard', 'adaptive', 'unsharp'];
146        if (!in_array($mode, $allowedModes)) {
147            throw new InvalidArgumentException(
148                sprintf('Mode %s is not an allowed blur mode', $mode)
149            );
150        }
151        $this->mode = $mode;
152        return $this;
153    }
154
155    /**
156     * @return string
157     */
158    public function mode()
159    {
160        return $this->mode;
161    }
162
163    /**
164     * @param string $channel The sharpen channel.
165     * @throws InvalidArgumentException If the channel argument is not a valid channel.
166     * @return self
167     */
168    public function setChannel($channel)
169    {
170        if (!in_array($channel, $this->image()->availableChannels())) {
171            throw new InvalidArgumentException(
172                'Channel is not valid'
173            );
174        }
175        $this->channel = $channel;
176        return $this;
177    }
178
179    /**
180     * @return string
181     */
182    public function channel()
183    {
184        return $this->channel;
185    }
186
187
188    /**
189     * @param array $data The effect data, if available.
190     * @return self
191     */
192    public function process(?array $data = null)
193    {
194        if ($data !== null) {
195            $this->setData($data);
196        }
197
198        $mode = $this->mode();
199        return match ($mode) {
200            'adaptive' => $this->processAdaptive(),
201            'unsharp' => $this->processUnsharp(),
202            default => $this->processStandard(),
203        };
204    }
205
206    /**
207     * @return self
208     */
209    abstract public function processAdaptive();
210
211    /**
212     * @return self
213     */
214    abstract public function processUnsharp();
215
216    /**
217     * @return self
218     */
219    abstract public function processStandard();
220}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Effect/AbstractThresholdEffect.php.html b/packages/image/build/report/Effect/AbstractThresholdEffect.php.html deleted file mode 100644 index 9b63a9ed7..000000000 --- a/packages/image/build/report/Effect/AbstractThresholdEffect.php.html +++ /dev/null @@ -1,214 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/AbstractThresholdEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 100.00% covered (success) -
-
-
100.00%
7 / 7
-
- 100.00% covered (success) -
-
-
100.00%
2 / 2
CRAP
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
AbstractThresholdEffect
-
- 100.00% covered (success) -
-
-
100.00%
7 / 7
-
- 100.00% covered (success) -
-
-
100.00%
2 / 2
5
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
 setThreshold
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
4
 threshold
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image\Effect;
6
7use InvalidArgumentException;
8use Charcoal\Image\AbstractEffect;
9
10/**
11 * Convert the image to monochrome (black and white)
12 */
13abstract class AbstractThresholdEffect extends AbstractEffect
14{
15    private float $threshold = 0.5;
16
17    /**
18     * @param float $threshold The threshold value.
19     * @throws InvalidArgumentException If the threshold is not numeric or lower than zero / greater than 1.
20     * @return AbstractThresholdEffect Chainable
21     */
22    public function setThreshold($threshold)
23    {
24        if (!is_numeric($threshold) || ($threshold < 0) || ($threshold > 1)) {
25            throw new InvalidArgumentException(
26                'Threshold must be a float between 0 and 1.'
27            );
28        }
29         $this->threshold = (float)$threshold;
30         return $this;
31    }
32
33    /**
34     * @return float
35     */
36    public function threshold()
37    {
38        return $this->threshold;
39    }
40}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Effect/AbstractTintEffect.php.html b/packages/image/build/report/Effect/AbstractTintEffect.php.html deleted file mode 100644 index 3bf1c60ce..000000000 --- a/packages/image/build/report/Effect/AbstractTintEffect.php.html +++ /dev/null @@ -1,349 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/AbstractTintEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 100.00% covered (success) -
-
-
100.00%
17 / 17
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
CRAP
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
AbstractTintEffect
-
- 100.00% covered (success) -
-
-
100.00%
17 / 17
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
10
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
 setColor
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
2
 color
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 setOpacity
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
4
 opacity
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 setMidtone
-
- 100.00% covered (success) -
-
-
100.00%
2 / 2
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 midtone
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image\Effect;
6
7use InvalidArgumentException;
8use Charcoal\Image\AbstractEffect;
9
10/**
11 * Tint (or colorize) the image with a certain color.
12 */
13abstract class AbstractTintEffect extends AbstractEffect
14{
15    private string $color = 'rgb(0,0,0)';
16    /**
17     * @var float $opacity;
18     */
19    private float $opacity = 0.5;
20    private bool $midtone = true;
21
22    /**
23     * @param string $color The tint color value.
24     * @throws InvalidArgumentException If the color is not a string.
25     * @return AbstractTintEffect Chainable
26     */
27    public function setColor($color)
28    {
29        if (!is_string($color)) {
30            throw new InvalidArgumentException(
31                'Color must be a string'
32            );
33        }
34        $this->color = $color;
35        return $this;
36    }
37
38    /**
39     * @return string
40     */
41    public function color()
42    {
43        return $this->color;
44    }
45
46    /**
47     * @param float $opacity The tint opacity value.
48     * @throws InvalidArgumentException If the tint value is not numeric or lower than 0 / greater than 1.
49     * @return AbstractTintEffect Chainable
50     */
51    public function setOpacity($opacity)
52    {
53        if (!is_numeric($opacity) || ($opacity < 0) || ( $opacity > 1)) {
54            throw new InvalidArgumentException(
55                'Opacity must be a float between 0.0 and 1.0'
56            );
57        }
58        $this->opacity = (float)$opacity;
59        return $this;
60    }
61
62    /**
63     * @return float
64     */
65    public function opacity()
66    {
67        return $this->opacity;
68    }
69
70    /**
71     * @param boolean $midtone The tint midtone flag.
72     * @return AbstractTintEffect Chainable
73     */
74    public function setMidtone($midtone)
75    {
76        $this->midtone = (bool)$midtone;
77        return $this;
78    }
79
80    /**
81     * @return boolean
82     */
83    public function midtone()
84    {
85        return $this->midtone;
86    }
87}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Effect/AbstractWatermarkEffect.php.html b/packages/image/build/report/Effect/AbstractWatermarkEffect.php.html deleted file mode 100644 index a1c6fb9af..000000000 --- a/packages/image/build/report/Effect/AbstractWatermarkEffect.php.html +++ /dev/null @@ -1,224 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/AbstractWatermarkEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 100.00% covered (success) -
-
-
100.00%
7 / 7
-
- 100.00% covered (success) -
-
-
100.00%
2 / 2
CRAP
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
AbstractWatermarkEffect
-
- 100.00% covered (success) -
-
-
100.00%
7 / 7
-
- 100.00% covered (success) -
-
-
100.00%
2 / 2
4
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
 setWatermark
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
3
 watermark
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image\Effect;
6
7use InvalidArgumentException;
8use Charcoal\Image\AbstractEffect;
9use Charcoal\Image\ImageInterface;
10use Charcoal\Image\Effect\LayerEffectInterface;
11use Charcoal\Image\Effect\LayerEffectTrait;
12
13/**
14 * Composite a watermark on top of the image.
15 */
16abstract class AbstractWatermarkEffect extends AbstractEffect implements LayerEffectInterface
17{
18    use LayerEffectTrait;
19
20    /**
21     * The watermark image source (path or Image).
22     * @var string|ImageInterface $watermark
23     */
24    private string|\Charcoal\Image\ImageInterface|null $watermark = null;
25
26    /**
27     * @param string|ImageInterface $watermark The watermark (path or Image).
28     * @throws InvalidArgumentException If the watermark value is not a string or an Image.
29     * @return self
30     */
31    public function setWatermark($watermark)
32    {
33        if (is_string($watermark) || ($watermark instanceof ImageInterface)) {
34            $this->watermark = $watermark;
35            return $this;
36        } else {
37            throw new InvalidArgumentException(
38                'Watermark must be a string'
39            );
40        }
41    }
42
43    /**
44     * @return string
45     */
46    public function watermark()
47    {
48        return $this->watermark;
49    }
50}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Effect/LayerEffectInterface.php.html b/packages/image/build/report/Effect/LayerEffectInterface.php.html deleted file mode 100644 index 69e5ab1c6..000000000 --- a/packages/image/build/report/Effect/LayerEffectInterface.php.html +++ /dev/null @@ -1,150 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/LayerEffectInterface.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image\Effect;
6
7/**
8 * Layer effect defines image effects that stack an image on top of another.
9 *
10 * The "layer" can therefore have the following properties:
11 * - opacity
12 * - gravity
13 * - x
14 * - y
15 */
16interface LayerEffectInterface
17{
18    /**
19     * @param float $opacity The mask opacity.
20     * @throws InvalidArgumentException If the mask opacity is not a numeric value or not between 0.0 and 1.0.
21     * @return AbstractMaskEffect Chainable
22     */
23    public function setOpacity($opacity);
24
25    /**
26     * @return float
27     */
28    public function opacity();
29
30    /**
31     * @param string $gravity The mask gravity.
32     * @throws InvalidArgumentException If the argument is not a valid gravity name.
33     * @return AbstractMaskEffect Chainable
34     */
35    public function setGravity($gravity);
36
37    /**
38     * @return string
39     */
40    public function gravity();
41
42    /**
43     * @param integer $x The mask X position.
44     * @throws InvalidArgumentException If the position is not a numeric value.
45     * @return AbstractMaskEffect Chainable
46     */
47    public function setX($x);
48
49    /**
50     * @return float
51     */
52    public function x();
53
54    /**
55     * @param integer $y The Y position.
56     * @return AbstractMaskEffect Chainable
57     */
58    public function setY($y);
59
60    /**
61     * @return float
62     */
63    public function y();
64}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Effect/LayerEffectTrait.php.html b/packages/image/build/report/Effect/LayerEffectTrait.php.html deleted file mode 100644 index 9d9b9f6f0..000000000 --- a/packages/image/build/report/Effect/LayerEffectTrait.php.html +++ /dev/null @@ -1,456 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect/LayerEffectTrait.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 100.00% covered (success) -
-
-
100.00%
28 / 28
-
- 100.00% covered (success) -
-
-
100.00%
8 / 8
CRAP
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
LayerEffectTrait
-
- 100.00% covered (success) -
-
-
100.00%
28 / 28
-
- 100.00% covered (success) -
-
-
100.00%
8 / 8
14
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
 setOpacity
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
4
 opacity
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 setGravity
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
2
 gravity
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 setX
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
2
 x
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 setY
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
2
 y
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 image
n/a
0 / 0
n/a
0 / 0
0
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Effect;
4
5use InvalidArgumentException;
6
7/**
8 * Full implementation, as a Trait, of the Layer Effect Interface.
9 */
10trait LayerEffectTrait
11{
12    /**
13     * @var float
14     */
15    private $opacity = 1.0;
16
17    /**
18     * @var string
19     */
20    private $gravity = 'nw';
21
22    /**
23     * Horizontal adjustment, in pixels.
24     * Negative values will move mask to the left, positive values to the right.
25     * Depends on the gravity setting
26     * @var integer
27     */
28    private $x = 0;
29
30    /**
31     * Vertical adjustment, in pixels.
32     * Negative values will move mask to the top, positive values to the bottom.
33     * Depends on the gravity setting
34     * @var integer
35     */
36    private $y = 0;
37
38    /**
39     * @param float $opacity The mask opacity.
40     * @throws InvalidArgumentException If the mask opacity is not a numeric value or not between 0.0 and 1.0.
41     * @return self
42     */
43    public function setOpacity($opacity)
44    {
45        if (!is_numeric($opacity) || ($opacity < 0) || ( $opacity > 1)) {
46            throw new InvalidArgumentException(
47                'Opacity must be a float between 0.0 and 1.0'
48            );
49        }
50        $this->opacity = (float)$opacity;
51        return $this;
52    }
53
54    /**
55     * @return float
56     */
57    public function opacity()
58    {
59        return $this->opacity;
60    }
61
62    /**
63     * @param string $gravity The mask gravity.
64     * @throws InvalidArgumentException If the argument is not a valid gravity name.
65     * @return self
66     */
67    public function setGravity($gravity)
68    {
69        if (!in_array($gravity, $this->image()->availableGravities())) {
70            throw new InvalidArgumentException(
71                'Gravity is not valid'
72            );
73        }
74        $this->gravity = $gravity;
75        return $this;
76    }
77
78    /**
79     * @return string
80     */
81    public function gravity()
82    {
83        return $this->gravity;
84    }
85
86    /**
87     * @param integer $x The mask X position.
88     * @throws InvalidArgumentException If the position is not a numeric value.
89     * @return self
90     */
91    public function setX($x)
92    {
93        if (!is_numeric($x)) {
94            throw new InvalidArgumentException(
95                'X must be a an integer (in pixel)'
96            );
97        }
98        $this->x = (int)$x;
99        return $this;
100    }
101
102    /**
103     * @return float
104     */
105    public function x()
106    {
107        return $this->x;
108    }
109
110    /**
111     * @param integer $y The Y position.
112     * @throws InvalidArgumentException If the position is not a numeric value.
113     * @return self
114     */
115    public function setY($y)
116    {
117        if (!is_numeric($y)) {
118            throw new InvalidArgumentException(
119                'Y must be a an integer (in pixel)'
120            );
121        }
122        $this->y = (int)$y;
123        return $this;
124    }
125
126    /**
127     * @return float
128     */
129    public function y()
130    {
131        return $this->y;
132    }
133
134    /**
135     * @return \Charcoal\Image\ImageInterface
136     */
137    abstract public function image();
138}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Effect/dashboard.html b/packages/image/build/report/Effect/dashboard.html deleted file mode 100644 index d3fafc8a9..000000000 --- a/packages/image/build/report/Effect/dashboard.html +++ /dev/null @@ -1,348 +0,0 @@ - - - - - Dashboard for /var/www/html/packages/image/src/Charcoal/Image/Effect - - - - - - - -
-
-
- -
-
-
-
-
-
-

Classes

-
-
-
-
-

Coverage Distribution

-
- -
-
-
-

Complexity

-
- -
-
-
-
- -
-

Project Risks

-
- - - - - - - - - - - - - - - -
ClassCoverageComplexityCRAP
Charcoal\Image\Effect\AbstractCropEffect0.0%29870
Charcoal\Image\Effect\AbstractResizeEffect58.6%89650
Charcoal\Image\Effect\AbstractFormatEffect0.0%312
-
-
-
-
-
-

Methods

-
-
-
-
-

Coverage Distribution

-
- -
-
-
-

Complexity

-
- -
-
-
-
-
-

Insufficient Coverage

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MethodCoverage
setQuality0%
quality0%
setWidth0%
width0%
setHeight0%
height0%
setX0%
x0%
setY0%
y0%
setGeometry0%
geometry0%
setGravity0%
gravity0%
setRepage0%
repage0%
process0%
setFormat0%
format0%
setMinWidth0%
setMinHeight0%
setMaxWidth0%
setMaxHeight0%
process40%
process75%
-
-
-
-

Project Risks

-
- - - - - - - - - - - - - - - - - - - - - - - - - - -
MethodCoverageComplexityCRAP
process40.5%38342
setGeometry0.0%420
setWidth0.0%312
setHeight0.0%312
setX0.0%312
setY0.0%312
process0.0%312
setMinWidth0.0%312
setMinHeight0.0%312
setMaxWidth0.0%312
setMaxHeight0.0%312
setGravity0.0%26
setFormat0.0%26
process75.0%55
-
-
-
- -
- - - - - - diff --git a/packages/image/build/report/Effect/index.html b/packages/image/build/report/Effect/index.html deleted file mode 100644 index a9bd913c6..000000000 --- a/packages/image/build/report/Effect/index.html +++ /dev/null @@ -1,606 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Effect - - - - - - - -
-
- -
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 71.32% covered (warning) -
-
-
71.32%
363 / 509
-
- 74.77% covered (warning) -
-
-
74.77%
80 / 107
-
- 64.71% covered (warning) -
-
-
64.71%
11 / 17
AbstractAutoorientationEffect.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
AbstractBlurEffect.php
-
- 96.30% covered (success) -
-
-
96.30%
52 / 54
-
- 90.91% covered (success) -
-
-
90.91%
10 / 11
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
AbstractCompressionEffect.php
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 3
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 2
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
AbstractCropEffect.php
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 56
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 15
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
AbstractDitherEffect.php
-
- 100.00% covered (success) -
-
-
100.00%
36 / 36
-
- 100.00% covered (success) -
-
-
100.00%
4 / 4
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
AbstractFormatEffect.php
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 11
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 2
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
AbstractGrayscaleEffect.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
AbstractMaskEffect.php
-
- 100.00% covered (success) -
-
-
100.00%
7 / 7
-
- 100.00% covered (success) -
-
-
100.00%
2 / 2
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
AbstractMirrorEffect.php
-
- 100.00% covered (success) -
-
-
100.00%
8 / 8
-
- 100.00% covered (success) -
-
-
100.00%
2 / 2
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
AbstractModulateEffect.php
-
- 100.00% covered (success) -
-
-
100.00%
21 / 21
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
AbstractResizeEffect.php
-
- 58.62% covered (warning) -
-
-
58.62%
102 / 174
-
- 75.00% covered (warning) -
-
-
75.00%
18 / 24
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
AbstractRevertEffect.php
-
- 100.00% covered (success) -
-
-
100.00%
7 / 7
-
- 100.00% covered (success) -
-
-
100.00%
2 / 2
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
AbstractRotateEffect.php
-
- 100.00% covered (success) -
-
-
100.00%
14 / 14
-
- 100.00% covered (success) -
-
-
100.00%
4 / 4
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
AbstractSepiaEffect.php
-
- 100.00% covered (success) -
-
-
100.00%
8 / 8
-
- 100.00% covered (success) -
-
-
100.00%
2 / 2
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
AbstractSharpenEffect.php
-
- 96.08% covered (success) -
-
-
96.08%
49 / 51
-
- 92.31% covered (success) -
-
-
92.31%
12 / 13
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
AbstractThresholdEffect.php
-
- 100.00% covered (success) -
-
-
100.00%
7 / 7
-
- 100.00% covered (success) -
-
-
100.00%
2 / 2
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
AbstractTintEffect.php
-
- 100.00% covered (success) -
-
-
100.00%
17 / 17
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
AbstractWatermarkEffect.php
-
- 100.00% covered (success) -
-
-
100.00%
7 / 7
-
- 100.00% covered (success) -
-
-
100.00%
2 / 2
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
LayerEffectInterface.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
LayerEffectTrait.php
-
- 100.00% covered (success) -
-
-
100.00%
28 / 28
-
- 100.00% covered (success) -
-
-
100.00%
8 / 8
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- -
- - diff --git a/packages/image/build/report/EffectFactory.php.html b/packages/image/build/report/EffectFactory.php.html deleted file mode 100644 index 34668035e..000000000 --- a/packages/image/build/report/EffectFactory.php.html +++ /dev/null @@ -1,114 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/EffectFactory.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
EffectFactory
n/a
0 / 0
n/a
0 / 0
0
n/a
0 / 0
-
- - - - - - - - - - - - - - - - - - - -
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image;
6
7// From 'charcoal-factory'
8use Charcoal\Factory\AbstractFactory;
9
10/**
11 *
12 */
13class EffectFactory extends AbstractFactory
14{
15}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/EffectInterface.php.html b/packages/image/build/report/EffectInterface.php.html deleted file mode 100644 index af8aa54bf..000000000 --- a/packages/image/build/report/EffectInterface.php.html +++ /dev/null @@ -1,116 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/EffectInterface.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image;
6
7use Charcoal\Image\ImageInterface;
8
9/**
10 * Image Effect Interface.
11 */
12interface EffectInterface
13{
14    /**
15     * @param ImageInterface $image The parent image.
16     * @return EffectInterface
17     */
18    public function setImage(ImageInterface $image);
19
20    /**
21     * @param array $data The effect data.
22     * @return EffectInterface
23     */
24    public function setData(array $data);
25
26    /**
27     * @param array $data The effect data, if available.
28     * @return EffectInterface
29     */
30    public function process(?array $data = null);
31}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/ImageFactory.php.html b/packages/image/build/report/ImageFactory.php.html deleted file mode 100644 index 6484f53fc..000000000 --- a/packages/image/build/report/ImageFactory.php.html +++ /dev/null @@ -1,203 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/ImageFactory.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
-
- 100.00% covered (success) -
-
-
100.00%
2 / 2
CRAP
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
ImageFactory
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
-
- 100.00% covered (success) -
-
-
100.00%
2 / 2
3
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
 __construct
-
- 100.00% covered (success) -
-
-
100.00%
2 / 2
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
2
 defaultMap
-
- 100.00% covered (success) -
-
-
100.00%
4 / 4
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image;
4
5// From 'charcoal-factory'
6use Charcoal\Factory\AbstractFactory;
7
8/**
9 * Create image class from image processor type.
10 */
11class ImageFactory extends AbstractFactory
12{
13    /**
14     * @param array $data Constructor dependencies.
15     */
16    public function __construct(?array $data = null)
17    {
18        $data['map'] = isset($data['map']) ? array_merge($this->defaultMap(), $data['map']) : $this->defaultMap();
19
20        parent::__construct($data);
21    }
22
23    protected function defaultMap(): array
24    {
25        return [
26            'imagick'     => \Charcoal\Image\Imagick\ImagickImage::class,
27            'imagemagick' => \Charcoal\Image\Imagemagick\ImagemagickImage::class
28        ];
29    }
30}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/ImageInterface.php.html b/packages/image/build/report/ImageInterface.php.html deleted file mode 100644 index 2c5d14f8e..000000000 --- a/packages/image/build/report/ImageInterface.php.html +++ /dev/null @@ -1,220 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/ImageInterface.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image;
6
7interface ImageInterface
8{
9    /**
10     * @param array $data The image data.
11     * @return ImageInterface Chainable
12     */
13    public function setData(array $data);
14
15    /**
16     * @param string $source The source.
17     * @return ImageInterface Chainable
18     */
19    public function setSource($source);
20
21    /**
22     * @return string
23     */
24    public function source();
25
26    /**
27     * @param string $target The target.
28     * @return ImageInterface Chainable
29     */
30    public function setTarget($target);
31
32    /**
33     * @return string
34     */
35    public function target();
36
37    /**
38     * @param array $effects The list of effects.
39     * @return ImageInterface Chainable
40     */
41    public function setEffects(array $effects);
42
43    /**
44     * @return EffectInterface[] The array of `EffectInterface` effects
45     */
46    public function effects();
47
48    /**
49     * @param array|EffectInterface $effect The effect, or effect options, to add.
50     * @return ImageInterface Chainable
51     */
52    public function addEffect($effect);
53
54    /**
55     * @param array $effects Extra effects to process.
56     * @return ImageInterface Chainable
57     */
58    public function process(?array $effects = null);
59
60    /**
61     * @param array|EffectInterface $effect An effect to process.
62     * @return ImageInterface Chainable
63     */
64    public function processEffect($effect);
65
66    /**
67     * Create a blank canvas of a given size, with a given background color.
68     *
69     * @param integer $width  Image size, in pixels.
70     * @param integer $height Image height, in pixels.
71     * @param string  $color  Default to transparent.
72     * @return ImageInterface Chainable
73     */
74    public function create($width, $height, $color = 'rgb(100%, 100%, 100%, 0)');
75
76    /**
77     * Open an image file
78     *
79     * @param string $source The source path / filename.
80     * @return ImageInterface Chainable
81     */
82    public function open($source = null);
83
84    /**
85     * Save an image to a target.
86     * If no target is set, the original source will be owerwritten
87     *
88     * @param string $target The target path / filename.
89     * @return ImageInterface Chainable
90     */
91    public function save($target = null);
92
93    /**
94     * Get the image's width, in pixels
95     *
96     * @return integer
97     */
98    public function width();
99
100    /**
101     * Get the image's height, in pixels
102     *
103     * @return integer
104     */
105    public function height();
106
107    /**
108     * Get the image's ratio (width / height)
109     *
110     * @return float
111     */
112    public function ratio();
113
114    /**
115     * Orientation can be "horizontal", "vertical" or "square"
116     *
117     * @return string
118     */
119    public function orientation();
120
121    /**
122     * @return boolean
123     */
124    public function isHorizontal();
125
126    /**
127     * @return boolean
128     */
129    public function isVertical();
130
131    /**
132     * @return boolean
133     */
134    public function isSquare();
135}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Imagemagick/Effect/ImagemagickAutoorientationEffect.php.html b/packages/image/build/report/Imagemagick/Effect/ImagemagickAutoorientationEffect.php.html deleted file mode 100644 index 50df53239..000000000 --- a/packages/image/build/report/Imagemagick/Effect/ImagemagickAutoorientationEffect.php.html +++ /dev/null @@ -1,178 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickAutoorientationEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 4
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagemagickAutoorientationEffect
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 4
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
6
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 process
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 4
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
6
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Imagemagick\Effect;
4
5use Charcoal\Image\Effect\AbstractAutoorientationEffect;
6
7/**
8 * Auto-orientate Effect for the Imagemagick driver.
9 */
10class ImagemagickAutoorientationEffect extends AbstractAutoorientationEffect
11{
12    /**
13     * @param array $data The effect data, if available.
14     * @return self
15     */
16    public function process(?array $data = null)
17    {
18        if ($data !== null) {
19            $this->setData($data);
20        }
21
22        $cmd = '-auto-orient';
23        return $this->image()->applyCmd($cmd);
24    }
25}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Imagemagick/Effect/ImagemagickBlurEffect.php.html b/packages/image/build/report/Imagemagick/Effect/ImagemagickBlurEffect.php.html deleted file mode 100644 index 564e20bae..000000000 --- a/packages/image/build/report/Imagemagick/Effect/ImagemagickBlurEffect.php.html +++ /dev/null @@ -1,321 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickBlurEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 86.96% covered (success) -
-
-
86.96%
20 / 23
-
- 83.33% covered (success) -
-
-
83.33%
5 / 6
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagemagickBlurEffect
-
- 86.96% covered (success) -
-
-
86.96%
20 / 23
-
- 83.33% covered (success) -
-
-
83.33%
5 / 6
6.08
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 processAdaptive
-
- 100.00% covered (success) -
-
-
100.00%
4 / 4
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 processGaussian
-
- 100.00% covered (success) -
-
-
100.00%
4 / 4
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 processMotion
-
- 100.00% covered (success) -
-
-
100.00%
4 / 4
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 processRadial
-
- 100.00% covered (success) -
-
-
100.00%
4 / 4
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 processSoft
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 3
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2
 processStandard
-
- 100.00% covered (success) -
-
-
100.00%
4 / 4
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Imagemagick\Effect;
4
5use Charcoal\Image\Effect\AbstractBlurEffect;
6
7/**
8 * Blur Effect for the Imagemagick driver.
9 */
10class ImagemagickBlurEffect extends AbstractBlurEffect
11{
12    public function processAdaptive(): static
13    {
14        $channel = $this->image()->convertChannel($this->channel());
15        $cmd = '-channel ' . $channel . ' -adaptive-blur ' . $this->radius() . 'x' . $this->sigma();
16        $this->image()->applyCmd($cmd);
17        return $this;
18    }
19
20    public function processGaussian(): static
21    {
22        $channel = $this->image()->convertChannel($this->channel());
23        $cmd = '-channel ' . $channel . ' -gaussian-blur ' . $this->radius() . 'x' . $this->sigma();
24        $this->image()->applyCmd($cmd);
25        return $this;
26    }
27
28    public function processMotion(): static
29    {
30        $channel = $this->image()->convertChannel($this->channel());
31        $cmd = '-channel ' . $channel . ' -motion-blur ' . $this->radius() . 'x' . $this->sigma() . '+' . $this->angle();
32        $this->image()->applyCmd($cmd);
33        return $this;
34    }
35
36    public function processRadial(): static
37    {
38        $channel = $this->image()->convertChannel($this->channel());
39        $cmd = '-channel ' . $channel . ' -rotational-blur ' . $this->angle();
40        $this->image()->applyCmd($cmd);
41        return $this;
42    }
43
44    public function processSoft(): static
45    {
46        $cmd = '-define convolve:scale=60,40% -morphology Convolve \'Gaussian:' . $this->radius() . 'x' . $this->sigma() . '\'';
47        $this->image()->applyCmd($cmd);
48        return $this;
49    }
50
51    public function processStandard(): static
52    {
53        $channel = $this->image()->convertChannel($this->channel());
54        $cmd = '-channel ' . $channel . ' -blur ' . $this->radius() . 'x' . $this->sigma();
55        $this->image()->applyCmd($cmd);
56        return $this;
57    }
58}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Imagemagick/Effect/ImagemagickCompressionEffect.php.html b/packages/image/build/report/Imagemagick/Effect/ImagemagickCompressionEffect.php.html deleted file mode 100644 index 343ca5401..000000000 --- a/packages/image/build/report/Imagemagick/Effect/ImagemagickCompressionEffect.php.html +++ /dev/null @@ -1,178 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickCompressionEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 5
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagemagickCompressionEffect
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 5
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
6
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 process
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 5
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
6
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Imagemagick\Effect;
4
5use Charcoal\Image\Effect\AbstractCompressionEffect;
6
7/**
8 * Compression Effect for the Imagemagick driver.
9 */
10class ImagemagickCompressionEffect extends AbstractCompressionEffect
11{
12    /**
13     * @param array $data The effect data, if available.
14     */
15    public function process(?array $data = null): static
16    {
17        if ($data !== null) {
18            $this->setData($data);
19        }
20
21        $cmd = sprintf('-quality %s%', $this->quality());
22        $this->image()->applyCmd($cmd);
23        return $this;
24    }
25}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Imagemagick/Effect/ImagemagickCropEffect.php.html b/packages/image/build/report/Imagemagick/Effect/ImagemagickCropEffect.php.html deleted file mode 100644 index ab4a1b6d5..000000000 --- a/packages/image/build/report/Imagemagick/Effect/ImagemagickCropEffect.php.html +++ /dev/null @@ -1,196 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickCropEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 10
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagemagickCropEffect
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 10
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
30
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 doCrop
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 10
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
30
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Imagemagick\Effect;
4
5use Exception;
6use Charcoal\Image\Effect\AbstractCropEffect;
7
8/**
9 * Reisze Effect for the Imagemagick driver.
10 *
11 * See {@link http://www.imagemagick.org/script/command-line-processing.php#geometry Image Geometry}
12 * for complete details about the geometry argument.
13 */
14class ImagemagickCropEffect extends AbstractCropEffect
15{
16    /**
17     * @param  integer $width  The crop width.
18     * @param  integer $height The crop height.
19     * @param  integer $x      The x-position (in pixel) of the crop.
20     * @param  integer $y      The y-position (in pixel) of the crop.
21     * @return void
22     */
23    protected function doCrop($width, $height, $x, $y)
24    {
25        $option = '-crop';
26
27        $geometry = $this->geometry();
28        if ($geometry) {
29            $params = [ $option . ' "' . $geometry . '"' ];
30        } else {
31            $params = [ '-gravity "' . $this->gravity() . '"' ];
32
33            $size = $width . 'x' . $height . ($x >= 0 ? '+' : '') . $x . ($y >= 0 ? '+' : '') . $y;
34            $params[] = $option . ' ' . $size;
35        }
36
37        if ($this->repage()) {
38            $params[] = '+repage';
39        }
40
41        $this->image()->applyCmd(implode(' ', $params));
42    }
43}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Imagemagick/Effect/ImagemagickDitherEffect.php.html b/packages/image/build/report/Imagemagick/Effect/ImagemagickDitherEffect.php.html deleted file mode 100644 index 1ee3c5239..000000000 --- a/packages/image/build/report/Imagemagick/Effect/ImagemagickDitherEffect.php.html +++ /dev/null @@ -1,182 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickDitherEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 5
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagemagickDitherEffect
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 5
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
6
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 process
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 5
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
6
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image\Imagemagick\Effect;
6
7use Exception;
8use Charcoal\Image\Effect\AbstractDitherEffect;
9
10/**
11 * Dither Effect for the Imagemagick driver.
12 */
13class ImagemagickDitherEffect extends AbstractDitherEffect
14{
15    /**
16     * @param array $data The effect data, if available.
17     * @throws Exception This effect is not supported for Imagemagick driver.
18     */
19    public function process(?array $data = null): void
20    {
21        if ($data !== null) {
22            $this->setData($data);
23        }
24
25        throw new Exception(
26            'Dither Effect is not (yet) supported with imagemagick driver.'
27        );
28    }
29}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Imagemagick/Effect/ImagemagickFormatEffect.php.html b/packages/image/build/report/Imagemagick/Effect/ImagemagickFormatEffect.php.html deleted file mode 100644 index fcb734659..000000000 --- a/packages/image/build/report/Imagemagick/Effect/ImagemagickFormatEffect.php.html +++ /dev/null @@ -1,181 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickFormatEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 4
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagemagickFormatEffect
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 4
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
12
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 process
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 4
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
12
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image\Imagemagick\Effect;
6
7use Charcoal\Image\Effect\AbstractFormatEffect;
8
9/**
10 * Format Effect for the Imagemagick driver.
11 */
12class ImagemagickFormatEffect extends AbstractFormatEffect
13{
14    /**
15     * @param array $data The effect data, if available.
16     */
17    public function process(?array $data = null): static
18    {
19        if ($data !== null) {
20            $this->setData($data);
21        }
22
23        if ($this->format()) {
24            // Currently not supported
25        }
26        return $this;
27    }
28}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Imagemagick/Effect/ImagemagickGrayscaleEffect.php.html b/packages/image/build/report/Imagemagick/Effect/ImagemagickGrayscaleEffect.php.html deleted file mode 100644 index 172a58558..000000000 --- a/packages/image/build/report/Imagemagick/Effect/ImagemagickGrayscaleEffect.php.html +++ /dev/null @@ -1,178 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickGrayscaleEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 75.00% covered (warning) -
-
-
75.00%
3 / 4
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagemagickGrayscaleEffect
-
- 75.00% covered (warning) -
-
-
75.00%
3 / 4
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2.06
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 process
-
- 75.00% covered (warning) -
-
-
75.00%
3 / 4
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2.06
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Imagemagick\Effect;
4
5use Charcoal\Image\Effect\AbstractGrayscaleEffect;
6
7/**
8 * Grayscale Effect for the Imagemagick driver.
9 */
10class ImagemagickGrayscaleEffect extends AbstractGrayscaleEffect
11{
12    /**
13     * @param array $data The effect data, if available.
14     * @return self
15     */
16    public function process(?array $data = null)
17    {
18        if ($data !== null) {
19            $this->setData($data);
20        }
21
22        $cmd = '-colorspace Gray';
23        return $this->image()->applyCmd($cmd);
24    }
25}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Imagemagick/Effect/ImagemagickMaskEffect.php.html b/packages/image/build/report/Imagemagick/Effect/ImagemagickMaskEffect.php.html deleted file mode 100644 index 8c3a74c7a..000000000 --- a/packages/image/build/report/Imagemagick/Effect/ImagemagickMaskEffect.php.html +++ /dev/null @@ -1,182 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickMaskEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 5
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagemagickMaskEffect
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 5
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
6
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 process
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 5
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
6
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image\Imagemagick\Effect;
6
7use Exception;
8use Charcoal\Image\Effect\AbstractMaskEffect;
9
10/**
11 * Mask Effect for the Imagemagick driver.
12 */
13class ImagemagickMaskEffect extends AbstractMaskEffect
14{
15    /**
16     * @param array $data The effect data, if available.
17     * @throws Exception This effect is not yet supported by Imagemagick driver.
18     */
19    public function process(?array $data = null): void
20    {
21        if ($data !== null) {
22            $this->setData($data);
23        }
24
25        throw new Exception(
26            'Mask Effect is not (yet) supported with imagemagick driver.'
27        );
28    }
29}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Imagemagick/Effect/ImagemagickMirrorEffect.php.html b/packages/image/build/report/Imagemagick/Effect/ImagemagickMirrorEffect.php.html deleted file mode 100644 index 253ea57ab..000000000 --- a/packages/image/build/report/Imagemagick/Effect/ImagemagickMirrorEffect.php.html +++ /dev/null @@ -1,179 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickMirrorEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 83.33% covered (success) -
-
-
83.33%
5 / 6
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagemagickMirrorEffect
-
- 83.33% covered (success) -
-
-
83.33%
5 / 6
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
3.04
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 process
-
- 83.33% covered (success) -
-
-
83.33%
5 / 6
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
3.04
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Imagemagick\Effect;
4
5use Charcoal\Image\Effect\AbstractMirrorEffect;
6
7/**
8 * Mirror Effect for the Imagemagick driver.
9 */
10class ImagemagickMirrorEffect extends AbstractMirrorEffect
11{
12    /**
13     * @param array $data The effect data, if available.
14     */
15    public function process(?array $data = null): static
16    {
17        if ($data !== null) {
18            $this->setData($data);
19        }
20
21        $axis = $this->axis();
22        $cmd = $axis == 'x' ? '-flip' : '-flop';
23        $this->image()->applyCmd($cmd);
24        return $this;
25    }
26}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Imagemagick/Effect/ImagemagickModulateEffect.php.html b/packages/image/build/report/Imagemagick/Effect/ImagemagickModulateEffect.php.html deleted file mode 100644 index 95df6c1be..000000000 --- a/packages/image/build/report/Imagemagick/Effect/ImagemagickModulateEffect.php.html +++ /dev/null @@ -1,182 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickModulateEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 87.50% covered (success) -
-
-
87.50%
7 / 8
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagemagickModulateEffect
-
- 87.50% covered (success) -
-
-
87.50%
7 / 8
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2.01
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 process
-
- 87.50% covered (success) -
-
-
87.50%
7 / 8
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2.01
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Imagemagick\Effect;
4
5use Charcoal\Image\Effect\AbstractModulateEffect;
6
7/**
8 * Modulate Effect for the Imagemagick driver.
9 */
10class ImagemagickModulateEffect extends AbstractModulateEffect
11{
12    /**
13     * @param array $data The effect data, if available.
14     */
15    public function process(?array $data = null): static
16    {
17        if ($data !== null) {
18            $this->setData($data);
19        }
20
21        $h = ($this->hue() + 100);
22        $s = ($this->saturation() + 100);
23        $l = ($this->luminance() + 100);
24
25        $cmd = '-modulate ' . $l . ',' . $s . ',' . $h;
26        $this->image()->applyCmd($cmd);
27        return $this;
28    }
29}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Imagemagick/Effect/ImagemagickResizeEffect.php.html b/packages/image/build/report/Imagemagick/Effect/ImagemagickResizeEffect.php.html deleted file mode 100644 index 35c32f05d..000000000 --- a/packages/image/build/report/Imagemagick/Effect/ImagemagickResizeEffect.php.html +++ /dev/null @@ -1,217 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickResizeEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 91.30% covered (success) -
-
-
91.30%
21 / 23
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagemagickResizeEffect
-
- 91.30% covered (success) -
-
-
91.30%
21 / 23
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
10.07
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 doResize
-
- 91.30% covered (success) -
-
-
91.30%
21 / 23
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
10.07
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Imagemagick\Effect;
4
5use Charcoal\Image\Effect\AbstractResizeEffect;
6
7/**
8 * Reisze Effect for the Imagemagick driver.
9 *
10 * See {@link http://www.imagemagick.org/script/command-line-processing.php#geometry Image Geometry}
11 * for complete details about the geometry argument.
12 */
13class ImagemagickResizeEffect extends AbstractResizeEffect
14{
15    /**
16     * @param  integer $width   The target width.
17     * @param  integer $height  The target height.
18     * @param  boolean $bestFit The "best_fit" flag.
19     * @return void
20     */
21    protected function doResize($width, $height, $bestFit = false)
22    {
23        $option = $this->adaptive() ? '-adaptive-resize' : '-resize';
24
25        $size = $this->size();
26        if ($size) {
27            $params = [ $option . ' "' . $size . '"' ];
28        } else {
29            if ($width === 0 && $height === 0) {
30                return;
31            }
32
33            $params = [
34                '-gravity "' . $this->gravity() . '"',
35                '-background "' . $this->backgroundColor() . '"'
36            ];
37
38            if ($width === 0) {
39                $width = '';
40            }
41
42            if ($height === 0) {
43                $height = '';
44            }
45
46            $size = $width . 'x' . $height;
47            if ($bestFit) {
48                if ($width && $height) {
49                    // Minimum values of width and height given, aspect ratio preserved.
50                    $operator = '^';
51                } else {
52                    $operator = '';
53                }
54
55                $params[] = $option . ' "' . $size . $operator . '"';
56                $params[] = '-extent ' . $size;
57            } else {
58                $params[] = $option . ' ' . $size;
59            }
60        }
61
62        $this->image()->applyCmd(implode(' ', $params));
63    }
64}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Imagemagick/Effect/ImagemagickRevertEffect.php.html b/packages/image/build/report/Imagemagick/Effect/ImagemagickRevertEffect.php.html deleted file mode 100644 index 7b0a3601c..000000000 --- a/packages/image/build/report/Imagemagick/Effect/ImagemagickRevertEffect.php.html +++ /dev/null @@ -1,179 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickRevertEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 83.33% covered (success) -
-
-
83.33%
5 / 6
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagemagickRevertEffect
-
- 83.33% covered (success) -
-
-
83.33%
5 / 6
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2.02
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 process
-
- 83.33% covered (success) -
-
-
83.33%
5 / 6
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2.02
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Imagemagick\Effect;
4
5use Charcoal\Image\Effect\AbstractRevertEffect;
6
7/**
8 * Rotate Effect for the Imagemagick driver.
9 */
10class ImagemagickRevertEffect extends AbstractRevertEffect
11{
12    /**
13     * @param array $data The effect data, if available.
14     */
15    public function process(?array $data = null): static
16    {
17        if ($data !== null) {
18            $this->setData($data);
19        }
20
21        $channel = $this->image()->convertChannel($this->channel());
22        $cmd = '-channel ' . $channel . ' -negate';
23        $this->image()->applyCmd($cmd);
24        return $this;
25    }
26}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Imagemagick/Effect/ImagemagickRotateEffect.php.html b/packages/image/build/report/Imagemagick/Effect/ImagemagickRotateEffect.php.html deleted file mode 100644 index fa5b3b039..000000000 --- a/packages/image/build/report/Imagemagick/Effect/ImagemagickRotateEffect.php.html +++ /dev/null @@ -1,178 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickRotateEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 80.00% covered (success) -
-
-
80.00%
4 / 5
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagemagickRotateEffect
-
- 80.00% covered (success) -
-
-
80.00%
4 / 5
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2.03
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 process
-
- 80.00% covered (success) -
-
-
80.00%
4 / 5
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2.03
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Imagemagick\Effect;
4
5use Charcoal\Image\Effect\AbstractRotateEffect;
6
7/**
8 * Rotate Effect for the Imagemagick driver.
9 */
10class ImagemagickRotateEffect extends AbstractRotateEffect
11{
12    /**
13     * @param array $data The effect data, if available.
14     */
15    public function process(?array $data = null): static
16    {
17        if ($data !== null) {
18            $this->setData($data);
19        }
20
21        $cmd = '-background "' . $this->backgroundColor() . '" -rotate ' . $this->angle();
22        $this->image()->applyCmd($cmd);
23        return $this;
24    }
25}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Imagemagick/Effect/ImagemagickSepiaEffect.php.html b/packages/image/build/report/Imagemagick/Effect/ImagemagickSepiaEffect.php.html deleted file mode 100644 index 9b363ae85..000000000 --- a/packages/image/build/report/Imagemagick/Effect/ImagemagickSepiaEffect.php.html +++ /dev/null @@ -1,179 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickSepiaEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 83.33% covered (success) -
-
-
83.33%
5 / 6
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagemagickSepiaEffect
-
- 83.33% covered (success) -
-
-
83.33%
5 / 6
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2.02
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 process
-
- 83.33% covered (success) -
-
-
83.33%
5 / 6
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2.02
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Imagemagick\Effect;
4
5use Charcoal\Image\Effect\AbstractSepiaEffect;
6
7/**
8 * Sepia Effect for the Imagemagick driver.
9 */
10class ImagemagickSepiaEffect extends AbstractSepiaEffect
11{
12    /**
13     * @param array $data The effect data, if available.
14     */
15    public function process(?array $data = null): static
16    {
17        if ($data !== null) {
18            $this->setData($data);
19        }
20
21        $value = (($this->threshold() / 255) * 100) . '%';
22        $cmd = '-sepia-tone ' . $value;
23        $this->image()->applyCmd($cmd);
24        return $this;
25    }
26}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Imagemagick/Effect/ImagemagickSharpenEffect.php.html b/packages/image/build/report/Imagemagick/Effect/ImagemagickSharpenEffect.php.html deleted file mode 100644 index 27c5fe1d3..000000000 --- a/packages/image/build/report/Imagemagick/Effect/ImagemagickSharpenEffect.php.html +++ /dev/null @@ -1,241 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickSharpenEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 30.00% covered (danger) -
-
-
30.00%
6 / 20
-
- 33.33% covered (danger) -
-
-
33.33%
1 / 3
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagemagickSharpenEffect
-
- 30.00% covered (danger) -
-
-
30.00%
6 / 20
-
- 33.33% covered (danger) -
-
-
33.33%
1 / 3
6.09
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 processAdaptive
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 6
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2
 processUnsharp
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 8
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2
 processStandard
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Imagemagick\Effect;
4
5use Charcoal\Image\Effect\AbstractSharpenEffect;
6
7/**
8 * Sharpen Effect for the Imagemagick driver.
9 */
10class ImagemagickSharpenEffect extends AbstractSharpenEffect
11{
12    public function processAdaptive(): static
13    {
14        $radius = $this->radius();
15        $sigma = $this->sigma();
16        $channel = $this->image()->convertChannel($this->channel());
17        $cmd = '-channel ' . $channel . ' -adaptive-sharpen ' . $radius . 'x' . $sigma;
18        $this->image()->applyCmd($cmd);
19        return $this;
20    }
21
22    public function processUnsharp(): static
23    {
24        $radius = $this->radius();
25        $sigma = $this->sigma();
26        $amount = $this->amount();
27        $threshold = $this->threshold();
28        $channel = $this->image()->convertChannel($this->channel());
29
30        $cmd = '-channel ' . $channel . ' -unsharp ' . $radius . 'x' . $sigma . '+' . $amount . '+' . $threshold;
31        $this->image()->applyCmd($cmd);
32        return $this;
33    }
34
35    public function processStandard(): static
36    {
37        $radius = $this->radius();
38        $sigma = $this->sigma();
39        $channel = $this->image()->convertChannel($this->channel());
40        $cmd = '-channel ' . $channel . ' -sharpen ' . $radius . 'x' . $sigma;
41        $this->image()->applyCmd($cmd);
42        return $this;
43    }
44}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Imagemagick/Effect/ImagemagickThresholdEffect.php.html b/packages/image/build/report/Imagemagick/Effect/ImagemagickThresholdEffect.php.html deleted file mode 100644 index 3d4a0a2c0..000000000 --- a/packages/image/build/report/Imagemagick/Effect/ImagemagickThresholdEffect.php.html +++ /dev/null @@ -1,179 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickThresholdEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 83.33% covered (success) -
-
-
83.33%
5 / 6
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagemagickThresholdEffect
-
- 83.33% covered (success) -
-
-
83.33%
5 / 6
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2.02
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 process
-
- 83.33% covered (success) -
-
-
83.33%
5 / 6
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2.02
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Imagemagick\Effect;
4
5use Charcoal\Image\Effect\AbstractThresholdEffect;
6
7/**
8 * Threshold Effect for the Imagemagick driver.
9 */
10class ImagemagickThresholdEffect extends AbstractThresholdEffect
11{
12    /**
13     * @param array $data The effect data, if available.
14     */
15    public function process(?array $data = null): static
16    {
17        if ($data !== null) {
18            $this->setData($data);
19        }
20
21        $value = ($this->threshold() * 100) . '%';
22        $cmd = '-threshold ' . $value;
23        $this->image()->applyCmd($cmd);
24        return $this;
25    }
26}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Imagemagick/Effect/ImagemagickTintEffect.php.html b/packages/image/build/report/Imagemagick/Effect/ImagemagickTintEffect.php.html deleted file mode 100644 index f2c90bfbb..000000000 --- a/packages/image/build/report/Imagemagick/Effect/ImagemagickTintEffect.php.html +++ /dev/null @@ -1,181 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickTintEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 87.50% covered (success) -
-
-
87.50%
7 / 8
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagemagickTintEffect
-
- 87.50% covered (success) -
-
-
87.50%
7 / 8
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
3.02
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 process
-
- 87.50% covered (success) -
-
-
87.50%
7 / 8
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
3.02
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Imagemagick\Effect;
4
5use Charcoal\Image\Effect\AbstractTintEffect;
6
7/**
8 * Tint Effect for the Imagemagick driver.
9 */
10class ImagemagickTintEffect extends AbstractTintEffect
11{
12    /**
13     * @param array $data The effect data, if available.
14     */
15    public function process(?array $data = null): static
16    {
17        if ($data !== null) {
18            $this->setData($data);
19        }
20
21        $tintCmd = $this->midtone() === true ? '-tint' : '-colorize';
22        $color = $this->color();
23        $value = ($this->opacity() * 100) . '%';
24        $cmd = '-fill "' . $color . '" ' . $tintCmd . ' ' . $value;
25        $this->image()->applyCmd($cmd);
26        return $this;
27    }
28}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Imagemagick/Effect/ImagemagickWatermarkEffect.php.html b/packages/image/build/report/Imagemagick/Effect/ImagemagickWatermarkEffect.php.html deleted file mode 100644 index 102d9f8d8..000000000 --- a/packages/image/build/report/Imagemagick/Effect/ImagemagickWatermarkEffect.php.html +++ /dev/null @@ -1,207 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect/ImagemagickWatermarkEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 25
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagemagickWatermarkEffect
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 25
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
20
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 process
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 25
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
20
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Imagemagick\Effect;
4
5use Charcoal\Image\Effect\AbstractWatermarkEffect;
6use Charcoal\Image\ImageInterface;
7
8/**
9 * Watermark Effect for the Imagemagick driver.
10 */
11class ImagemagickWatermarkEffect extends AbstractWatermarkEffect
12{
13    /**
14     * @param array $data The effect data, if available.
15     */
16    public function process(?array $data = null): static
17    {
18        if ($data !== null) {
19            $this->setData($data);
20        }
21
22        if ($this->watermark() instanceof ImageInterface) {
23            $out = '/tmp/_' . uniqid() . '.png';
24            $this->watermark()->save($out);
25            $width = $this->watermark()->width();
26            $height = $this->watermark()->height();
27            $watermark = $out;
28        } else {
29            $watermark = $this->watermark();
30            $c = $this->image()::class;
31            $w = new $c();
32            $w->open($watermark);
33            $width = $w->width();
34            $height = $w->height();
35        }
36
37        $gravity = $this->image()->imagemagickGravity($this->gravity());
38        $params  = [ '-gravity ' . $gravity ];
39
40        $cmd = null;
41        if ($this->image()->compositeCmd()) {
42            $cmd = 'composite';
43            $params[] = '-watermark 100% ' . $watermark . ' ' . $this->image()->tmp();
44        } else {
45            $params[] = '-geometry +' . $this->x() . '+' . $this->y();
46            $params[] = '-draw "image Multiply 0,0 ' . $width . ',' . $height;
47            $params[] = '\'' . $watermark . '\'"';
48        }
49
50        $this->image()->applyCmd(implode(' ', $params), $cmd);
51
52        return $this;
53    }
54}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Imagemagick/Effect/dashboard.html b/packages/image/build/report/Imagemagick/Effect/dashboard.html deleted file mode 100644 index 9dfdaab38..000000000 --- a/packages/image/build/report/Imagemagick/Effect/dashboard.html +++ /dev/null @@ -1,339 +0,0 @@ - - - - - Dashboard for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect - - - - - - - -
-
- -
-
-
-
-
-

Classes

-
-
-
-
-

Coverage Distribution

-
- -
-
-
-

Complexity

-
- -
-
-
- -
-
-

Methods

-
-
-
-
-

Coverage Distribution

-
- -
-
-
-

Complexity

-
- -
-
-
-
-
-

Insufficient Coverage

-
- - - - - - - - - - - - - - - - - - - - - -
MethodCoverage
process0%
processSoft0%
process0%
doCrop0%
process0%
process0%
process0%
processAdaptive0%
processUnsharp0%
process0%
process75%
-
-
-
-

Project Risks

-
- - - - - - - - - - - - - - - - - - - - -
MethodCoverageComplexityCRAP
doCrop0.0%530
process0.0%420
process0.0%312
process0.0%26
process0.0%26
process0.0%26
process0.0%26
process75.0%22
-
-
-
- -
- - - - - - diff --git a/packages/image/build/report/Imagemagick/Effect/index.html b/packages/image/build/report/Imagemagick/Effect/index.html deleted file mode 100644 index fb4ab376b..000000000 --- a/packages/image/build/report/Imagemagick/Effect/index.html +++ /dev/null @@ -1,596 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/Effect - - - - - - - -
- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 50.87% covered (warning) -
-
-
50.87%
88 / 173
-
- 24.00% covered (danger) -
-
-
24.00%
6 / 25
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 18
ImagemagickAutoorientationEffect.php
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 4
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagemagickBlurEffect.php
-
- 86.96% covered (success) -
-
-
86.96%
20 / 23
-
- 83.33% covered (success) -
-
-
83.33%
5 / 6
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagemagickCompressionEffect.php
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 5
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagemagickCropEffect.php
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 10
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagemagickDitherEffect.php
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 5
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagemagickFormatEffect.php
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 4
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagemagickGrayscaleEffect.php
-
- 75.00% covered (warning) -
-
-
75.00%
3 / 4
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagemagickMaskEffect.php
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 5
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagemagickMirrorEffect.php
-
- 83.33% covered (success) -
-
-
83.33%
5 / 6
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagemagickModulateEffect.php
-
- 87.50% covered (success) -
-
-
87.50%
7 / 8
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagemagickResizeEffect.php
-
- 91.30% covered (success) -
-
-
91.30%
21 / 23
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagemagickRevertEffect.php
-
- 83.33% covered (success) -
-
-
83.33%
5 / 6
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagemagickRotateEffect.php
-
- 80.00% covered (success) -
-
-
80.00%
4 / 5
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagemagickSepiaEffect.php
-
- 83.33% covered (success) -
-
-
83.33%
5 / 6
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagemagickSharpenEffect.php
-
- 30.00% covered (danger) -
-
-
30.00%
6 / 20
-
- 33.33% covered (danger) -
-
-
33.33%
1 / 3
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagemagickThresholdEffect.php
-
- 83.33% covered (success) -
-
-
83.33%
5 / 6
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagemagickTintEffect.php
-
- 87.50% covered (success) -
-
-
87.50%
7 / 8
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagemagickWatermarkEffect.php
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 25
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- -
- - diff --git a/packages/image/build/report/Imagemagick/ImagemagickImage.php.html b/packages/image/build/report/Imagemagick/ImagemagickImage.php.html deleted file mode 100644 index 2c1713bd9..000000000 --- a/packages/image/build/report/Imagemagick/ImagemagickImage.php.html +++ /dev/null @@ -1,1036 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick/ImagemagickImage.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 52.83% covered (warning) -
-
-
52.83%
84 / 159
-
- 47.62% covered (warning) -
-
-
47.62%
10 / 21
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagemagickImage
-
- 52.83% covered (warning) -
-
-
52.83%
84 / 159
-
- 47.62% covered (warning) -
-
-
47.62%
10 / 21
451.53
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 __construct
-
- 100.00% covered (success) -
-
-
100.00%
2 / 2
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 __destruct
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 driverType
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 create
-
- 66.67% covered (warning) -
-
-
66.67%
8 / 12
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
5.93
 open
-
- 100.00% covered (success) -
-
-
100.00%
12 / 12
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
6
 save
-
- 45.45% covered (warning) -
-
-
45.45%
5 / 11
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
9.06
 width
-
- 75.00% covered (warning) -
-
-
75.00%
3 / 4
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2.06
 height
-
- 75.00% covered (warning) -
-
-
75.00%
3 / 4
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2.06
 convertChannel
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 findCmd
-
- 33.33% covered (danger) -
-
-
33.33%
8 / 24
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
26.96
 availableCommands
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 cmd
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 19
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
72
 mogrifyCmd
-
- 100.00% covered (success) -
-
-
100.00%
4 / 4
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
2
 convertCmd
-
- 75.00% covered (warning) -
-
-
75.00%
3 / 4
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2.06
 compositeCmd
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 4
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
6
 identifyCmd
-
- 100.00% covered (success) -
-
-
100.00%
4 / 4
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
2
 tmp
-
- 100.00% covered (success) -
-
-
100.00%
4 / 4
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
2
 resetTmp
-
- 100.00% covered (success) -
-
-
100.00%
4 / 4
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
2
 exec
-
- 80.00% covered (success) -
-
-
80.00%
16 / 20
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
3.07
 applyCmd
-
- 57.14% covered (warning) -
-
-
57.14%
4 / 7
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
3.71
 imagemagickGravity
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 16
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
6
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Imagemagick;
4
5use Exception;
6use InvalidArgumentException;
7use OutOfBoundsException;
8use Charcoal\Image\AbstractImage;
9
10/**
11 * The Imagemagick image driver.
12 *
13 * Run from the binary imagemagick scripts.
14 * (`mogrify`, `convert` and `identify`)
15 */
16class ImagemagickImage extends AbstractImage
17{
18    /**
19     * The temporary file location
20     */
21    private ?string $tmpFile = null;
22
23    /**
24     * @var string $mogrifyCmd
25     */
26    private $mogrifyCmd;
27
28    /**
29     * @var string $convertCmd
30     */
31    private $convertCmd;
32
33    /**
34     * @var string $compositeCmd
35     */
36    private $compositeCmd;
37
38    /**
39     * @var string $identifyCmd
40     */
41    private $identifyCmd;
42
43    /**
44     * Set up the commands.
45     */
46    public function __construct()
47    {
48        // This will throw exception if the binaris are not found.
49        $this->mogrifyCmd = $this->mogrifyCmd();
50        $this->convertCmd = $this->convertCmd();
51    }
52
53    /**
54     * Clean up the tmp file, if necessary
55     */
56    public function __destruct()
57    {
58        $this->resetTmp();
59    }
60
61    public function driverType(): string
62    {
63        return 'imagemagick';
64    }
65
66    /**
67     * Create a blank canvas of a given size, with a given background color.
68     *
69     * @param integer $width  Image size, in pixels.
70     * @param integer $height Image height, in pixels.
71     * @param string  $color  Default to transparent.
72     * @throws InvalidArgumentException If the size arguments are not valid.
73     */
74    public function create($width, $height, $color = 'rgb(100%, 100%, 100%, 0)'): static
75    {
76        if (!is_numeric($width) || $width < 1) {
77            throw new InvalidArgumentException(
78                'Width must be an integer of at least 1 pixel'
79            );
80        }
81        if (!is_numeric($height) || $height < 1) {
82            throw new InvalidArgumentException(
83                'Height must be an integer of at least 1 pixel'
84            );
85        }
86
87        $this->resetTmp();
88        touch($this->tmp());
89        $this->exec($this->convertCmd() . ' -size ' . (int)$width . 'x' . (int)$height . ' canvas:"' . $color . '" ' . $this->tmp());
90        return $this;
91    }
92
93    /**
94     * Open an image file
95     *
96     * @param string $source The source path / filename.
97     * @throws Exception If the source file does not exist.
98     * @throws InvalidArgumentException If the source argument is not a string.
99     */
100    public function open($source = null): static
101    {
102        if ($source !== null && !is_string($source)) {
103            throw new InvalidArgumentException(
104                'Source must be a string (file path)'
105            );
106        }
107
108        $source = $source ?: $this->source();
109        $this->resetTmp();
110        if (!file_exists($source) && null === parse_url($source, PHP_URL_HOST)) {
111            throw new Exception(
112                sprintf('File "%s" does not exist', $source)
113            );
114        }
115
116        copy($source, $this->tmp());
117
118        return $this;
119    }
120
121    /**
122     * Save an image to a target.
123     * If no target is set, the original source will be owerwritten
124     *
125     * @param string $target The target path / filename.
126     * @throws Exception If the target file does not exist or is not writeable.
127     * @throws InvalidArgumentException If the target argument is not a string.
128     */
129    public function save($target = null): static
130    {
131        if ($target !== null && !is_string($target)) {
132            throw new InvalidArgumentException(
133                'Target must be a string (file path)'
134            );
135        }
136
137        $target = $target ?: $this->target();
138        if (!is_writable(dirname($target))) {
139            throw new Exception(
140                sprintf('Target "%s" is not writable', $target)
141            );
142        }
143
144        copy($this->tmp(), $target);
145
146        return $this;
147    }
148
149    /**
150     * Get the image's width, in pixels
151     */
152    public function width(): int
153    {
154        if (!file_exists($this->tmp())) {
155            return 0;
156        }
157        $cmd = $this->identifyCmd() . ' -format "%w" ' . $this->tmp();
158        return (int)trim($this->exec($cmd));
159    }
160
161    /**
162     * Get the image's height, in pixels
163     */
164    public function height(): int
165    {
166        if (!file_exists($this->tmp())) {
167            return 0;
168        }
169        $cmd = $this->identifyCmd() . ' -format "%h" ' . $this->tmp();
170        return (int)trim($this->exec($cmd));
171    }
172
173    /**
174     * @param string $channel The channel name to convert.
175     */
176    public function convertChannel($channel): string
177    {
178        return ucfirst($channel);
179    }
180
181    /**
182     * Find a command.
183     *
184     * Try (as best as possible) to find a command name:
185     *
186     * - With `type -p`
187     * - Or else, with `where`
188     * - Or else, with `which`
189     *
190     * @param  string $cmdName The command name to find.
191     * @throws InvalidArgumentException If the given command name is not a string.
192     * @throws OutOfBoundsException If the command is unsupported or can not be found.
193     * @return string
194     */
195    protected function findCmd($cmdName)
196    {
197        if (!is_string($cmdName)) {
198            throw new InvalidArgumentException(sprintf(
199                'Target image must be a string, received %s',
200                (get_debug_type($cmdName))
201            ));
202        }
203
204        if (!in_array($cmdName, $this->availableCommands())) {
205            if (!is_string($cmdName)) {
206                $cmdName = (get_debug_type($cmdName));
207            }
208            throw new OutOfBoundsException(sprintf(
209                'Unsupported command "%s" provided',
210                $cmdName
211            ));
212        }
213
214        $cmd = exec('type -p ' . $cmdName);
215        $cmd = str_replace($cmdName . ' is ', '', $cmd);
216
217        if ($cmd === '' || $cmd === '0') {
218            $cmd = exec('where ' . $cmdName);
219        }
220
221        if (!$cmd) {
222            $cmd = exec('which ' . $cmdName);
223        }
224
225        if (!$cmd) {
226            throw new OutOfBoundsException(sprintf(
227                'Can not find ImageMagick\'s "%s" command.',
228                $cmdName
229            ));
230        }
231
232        return $cmd;
233    }
234
235    /**
236     * Retrieve the list of available commands.
237     */
238    public function availableCommands(): array
239    {
240        return [ 'mogrify', 'convert', 'composite', 'identify' ];
241    }
242
243    /**
244     * Retrieve the list of available commands.
245     *
246     * @param  string $name The name of an available command (mogrify, convert, composite or identify).
247     * @throws InvalidArgumentException If the name is not a string.
248     * @throws OutOfBoundsException If the name is unsupported.
249     * @return string
250     */
251    public function cmd($name)
252    {
253        if (!is_string($name)) {
254            throw new InvalidArgumentException(sprintf(
255                'Command name must be a string, received %s',
256                (get_debug_type($name))
257            ));
258        }
259
260        switch ($name) {
261            case 'mogrify':
262                return $this->mogrifyCmd();
263
264            case 'convert':
265                return $this->convertCmd();
266
267            case 'composite':
268                return $this->compositeCmd();
269
270            case 'identify':
271                return $this->identifyCmd();
272
273            default:
274                if (!is_string($name)) {
275                    $name = (get_debug_type($name));
276                }
277                throw new OutOfBoundsException(sprintf(
278                    'Unsupported command "%s" provided',
279                    $name
280                ));
281        }
282    }
283
284    /**
285     * @return string The full path of the mogrify command.
286     */
287    public function mogrifyCmd()
288    {
289        if ($this->mogrifyCmd !== null) {
290            return $this->mogrifyCmd;
291        }
292        $this->mogrifyCmd = $this->findCmd('mogrify');
293        return $this->mogrifyCmd;
294    }
295
296    /**
297     * @return string The full path of the convert command.
298     */
299    public function convertCmd()
300    {
301        if ($this->convertCmd !== null) {
302            return $this->convertCmd;
303        }
304        $this->convertCmd = $this->findCmd('convert');
305        return $this->convertCmd;
306    }
307
308    /**
309     * @return string The full path of the composite command.
310     */
311    public function compositeCmd()
312    {
313        if ($this->compositeCmd !== null) {
314            return $this->compositeCmd;
315        }
316        $this->compositeCmd = $this->findCmd('composite');
317        return $this->compositeCmd;
318    }
319
320    /**
321     * @return string The full path of the identify comand.
322     */
323    public function identifyCmd()
324    {
325        if ($this->identifyCmd !== null) {
326            return $this->identifyCmd;
327        }
328        $this->identifyCmd = $this->findCmd('identify');
329        return $this->identifyCmd;
330    }
331
332    /**
333     * Generate a temporary file, to apply effects on.
334     */
335    public function tmp(): string
336    {
337        if ($this->tmpFile !== null) {
338            return $this->tmpFile;
339        }
340
341        $this->tmpFile = rtrim(sys_get_temp_dir(), '/\\') . DIRECTORY_SEPARATOR . 'charcoal_image_' . uniqid();
342        return $this->tmpFile;
343    }
344
345    /**
346     * @return ImagemagickImage Chainable
347     */
348    public function resetTmp(): static
349    {
350        if (file_exists($this->tmpFile)) {
351            unlink($this->tmpFile);
352        }
353        $this->tmpFile = null;
354        return $this;
355    }
356
357    /**
358     * Exec a command, either with `proc_open()` or `shell_exec()`
359     *
360     * The `proc_open()` method is preferred, as it allows to catch errors in
361     * the STDERR buffer (and throw Exception) but it might be disabled in some
362     * systems for security reasons.
363     *
364     * @param string $cmd The command to execute.
365     * @throws Exception If the command fails.
366     * @return string
367     */
368    public function exec(string $cmd): string|false|null
369    {
370        if (function_exists('proc_open')) {
371            $proc = proc_open(
372                $cmd,
373                [
374                    1 => ['pipe','w'],
375                    2 => ['pipe','w'],
376                ],
377                $pipes
378            );
379            $out = stream_get_contents($pipes[1]);
380            fclose($pipes[1]);
381            $err = stream_get_contents($pipes[2]);
382            fclose($pipes[2]);
383            proc_close($proc);
384
385            if ($err) {
386                throw new Exception(
387                    sprintf('Error executing command "%s": %s', $cmd, $err)
388                );
389            }
390
391            return $out;
392        } else {
393            return shell_exec($cmd);
394        }
395    }
396
397    /**
398     * @param  string      $params The $cmd's arguments and options.
399     * @param  string|null $cmd    The command to run.
400     * @throws Exception If the tmp file was not properly set.
401     * @return ImagemagickImage Chainable
402     */
403    public function applyCmd(string $params, $cmd = null): static
404    {
405        $cmd = $cmd === null ? $this->mogrifyCmd() : $this->cmd($cmd);
406
407        if (!file_exists($this->tmp())) {
408            throw new Exception(
409                'No file currently set as tmp file, commands can not be executed.'
410            );
411        }
412
413        $this->exec($cmd . ' ' . $params . ' ' . $this->tmp());
414
415        return $this;
416    }
417
418    /**
419     * Convert a gravity name (string) to an `Imagick::GRAVITY_*` constant (integer)
420     *
421     * @param string $gravity The standard gravity name.
422     * @throws InvalidArgumentException If the gravity argument is not a valid gravity.
423     */
424    public function imagemagickGravity($gravity): string
425    {
426        $gravityMap = [
427            'center'    => 'center',
428            'n'         => 'north',
429            's'         => 'south',
430            'e'         => 'east',
431            'w'         => 'west',
432            'ne'        => 'northeast',
433            'nw'        => 'northwest',
434            'se'        => 'southeast',
435            'sw'        => 'southwest'
436        ];
437        if (!isset($gravityMap[$gravity])) {
438            throw new InvalidArgumentException(
439                'Invalid gravity. Possible values are: center, n, s, e, w, ne, nw, se, sw.'
440            );
441        }
442        return $gravityMap[$gravity];
443    }
444}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Imagemagick/dashboard.html b/packages/image/build/report/Imagemagick/dashboard.html deleted file mode 100644 index ede942db8..000000000 --- a/packages/image/build/report/Imagemagick/dashboard.html +++ /dev/null @@ -1,360 +0,0 @@ - - - - - Dashboard for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick - - - - - - - -
-
- -
-
-
-
-
-

Classes

-
-
-
-
-

Coverage Distribution

-
- -
-
-
-

Complexity

-
- -
-
-
- -
-
-

Methods

-
-
-
-
-

Coverage Distribution

-
- -
-
-
-

Complexity

-
- -
-
-
-
-
-

Insufficient Coverage

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MethodCoverage
process0%
processSoft0%
process0%
doCrop0%
process0%
process0%
process0%
processAdaptive0%
processUnsharp0%
process0%
cmd0%
compositeCmd0%
imagemagickGravity0%
findCmd33%
save45%
applyCmd57%
create66%
process75%
width75%
height75%
convertCmd75%
-
-
-
-

Project Risks

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MethodCoverageComplexityCRAP
cmd0.0%872
doCrop0.0%530
findCmd33.3%826
process0.0%420
process0.0%312
save45.5%59
process0.0%26
process0.0%26
process0.0%26
process0.0%26
compositeCmd0.0%26
imagemagickGravity0.0%26
create66.7%55
applyCmd57.1%33
process75.0%22
width75.0%22
height75.0%22
convertCmd75.0%22
-
-
-
- -
- - - - - - diff --git a/packages/image/build/report/Imagemagick/index.html b/packages/image/build/report/Imagemagick/index.html deleted file mode 100644 index 08c7411ba..000000000 --- a/packages/image/build/report/Imagemagick/index.html +++ /dev/null @@ -1,147 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagemagick - - - - - - - -
-
-
- -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 51.81% covered (warning) -
-
-
51.81%
172 / 332
-
- 34.78% covered (danger) -
-
-
34.78%
16 / 46
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 19
Effect
-
- 50.87% covered (warning) -
-
-
50.87%
88 / 173
-
- 24.00% covered (danger) -
-
-
24.00%
6 / 25
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 18
ImagemagickImage.php
-
- 52.83% covered (warning) -
-
-
52.83%
84 / 159
-
- 47.62% covered (warning) -
-
-
47.62%
10 / 21
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- -
- - diff --git a/packages/image/build/report/Imagick/Effect/ImagickAutoorientationEffect.php.html b/packages/image/build/report/Imagick/Effect/ImagickAutoorientationEffect.php.html deleted file mode 100644 index 88f4f376a..000000000 --- a/packages/image/build/report/Imagick/Effect/ImagickAutoorientationEffect.php.html +++ /dev/null @@ -1,212 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickAutoorientationEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 32
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagickAutoorientationEffect
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 32
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
132
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 process
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 32
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
132
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Imagick\Effect;
4
5use Imagick;
6use Charcoal\Image\Effect\AbstractAutoorientationEffect;
7
8/**
9 * Auto-orientation Effect for the Imagick driver.
10 */
11class ImagickAutoorientationEffect extends AbstractAutoorientationEffect
12{
13    /**
14     * @param array $data The effect data, if available.
15     * @return ImagickAutoorientationEffect Chainable
16     */
17    public function process(?array $data = null): static
18    {
19        if ($data !== null) {
20            $this->setData($data);
21        }
22
23        switch ($this->image()->imagick()->getImageOrientation()) {
24            case Imagick::ORIENTATION_TOPLEFT:
25                break;
26            case Imagick::ORIENTATION_TOPRIGHT:
27                $this->image()->imagick()->flopImage();
28                break;
29            case Imagick::ORIENTATION_BOTTOMRIGHT:
30                $this->image()->imagick()->rotateImage('#000', 180);
31                break;
32            case Imagick::ORIENTATION_BOTTOMLEFT:
33                $this->image()->imagick()->flopImage();
34                $this->image()->imagick()->rotateImage('#000', 180);
35                break;
36            case Imagick::ORIENTATION_LEFTTOP:
37                $this->image()->imagick()->flopImage();
38                $this->image()->imagick()->rotateImage('#000', -90);
39                break;
40            case Imagick::ORIENTATION_RIGHTTOP:
41                $this->image()->imagick()->rotateImage('#000', 90);
42                break;
43            case Imagick::ORIENTATION_RIGHTBOTTOM:
44                $this->image()->imagick()->flopImage();
45                $this->image()->imagick()->rotateImage('#000', 90);
46                break;
47            case Imagick::ORIENTATION_LEFTBOTTOM:
48                $this->image()->imagick()->rotateImage('#000', -90);
49                break;
50            default:
51                // Invalid orientation
52                break;
53        }
54
55        $this->image()->imagick()->setImageOrientation(Imagick::ORIENTATION_TOPLEFT);
56
57        return $this;
58    }
59}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Imagick/Effect/ImagickBlurEffect.php.html b/packages/image/build/report/Imagick/Effect/ImagickBlurEffect.php.html deleted file mode 100644 index 5779e5777..000000000 --- a/packages/image/build/report/Imagick/Effect/ImagickBlurEffect.php.html +++ /dev/null @@ -1,336 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickBlurEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 84.21% covered (success) -
-
-
84.21%
16 / 19
-
- 83.33% covered (success) -
-
-
83.33%
5 / 6
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagickBlurEffect
-
- 84.21% covered (success) -
-
-
84.21%
16 / 19
-
- 83.33% covered (success) -
-
-
83.33%
5 / 6
6.14
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 processAdaptive
-
- 100.00% covered (success) -
-
-
100.00%
3 / 3
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 processGaussian
-
- 100.00% covered (success) -
-
-
100.00%
3 / 3
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 processMotion
-
- 100.00% covered (success) -
-
-
100.00%
3 / 3
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 processRadial
-
- 100.00% covered (success) -
-
-
100.00%
4 / 4
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 processSoft
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 3
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2
 processStandard
-
- 100.00% covered (success) -
-
-
100.00%
3 / 3
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Imagick\Effect;
4
5use Exception;
6use Charcoal\Image\Effect\AbstractBlurEffect;
7
8/**
9 * Blur Effect for the Imagick driver.
10 */
11class ImagickBlurEffect extends AbstractBlurEffect
12{
13    /**
14     * @return ImagickBlurEffect Chainable
15     */
16    public function processAdaptive(): static
17    {
18        $channel = $this->image()->imagickChannel($this->channel());
19        $this->image()->imagick()->adaptiveBlurImage($this->radius(), $this->sigma(), $channel);
20        return $this;
21    }
22
23    /**
24     * @return ImagickBlurEffect Chainable
25     */
26    public function processGaussian(): static
27    {
28        $channel = $this->image()->imagickChannel($this->channel());
29        $this->image()->imagick()->gaussianBlurImage($this->radius(), $this->sigma(), $channel);
30        return $this;
31    }
32
33    /**
34     * @return ImagickBlurEffect Chainable
35     */
36    public function processMotion(): static
37    {
38        $channel = $this->image()->imagickChannel($this->channel());
39        $this->image()->imagick()->motionBlurImage($this->radius(), $this->sigma(), $this->angle(), $channel);
40        return $this;
41    }
42
43    /**
44     * @return ImagickBlurEffect Chainable
45     */
46    public function processRadial(): static
47    {
48        $angle = $this->angle();
49        $channel = $this->image()->imagickChannel($this->channel());
50        $this->image()->imagick()->rotationalBlurImage(($angle), $channel);
51        return $this;
52    }
53
54    /**
55     * @throws Exception This method is not yet supported on Imagick.
56     */
57    public function processSoft(): never
58    {
59        throw new Exception(
60            'Soft blur is not (yet) supported with imagick driver.'
61        );
62    }
63
64    /**
65     * @return ImagickBlurEffect Chainable
66     */
67    public function processStandard(): static
68    {
69        $channel = $this->image()->imagickChannel($this->channel());
70        $this->image()->imagick()->blurImage($this->radius(), $this->sigma(), $channel);
71        return $this;
72    }
73}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Imagick/Effect/ImagickCompressionEffect.php.html b/packages/image/build/report/Imagick/Effect/ImagickCompressionEffect.php.html deleted file mode 100644 index f9b4fe684..000000000 --- a/packages/image/build/report/Imagick/Effect/ImagickCompressionEffect.php.html +++ /dev/null @@ -1,209 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickCompressionEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 17
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagickCompressionEffect
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 17
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
56
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 process
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 17
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
56
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Imagick\Effect;
4
5use Charcoal\Image\Effect\AbstractCompressionEffect;
6
7/**
8 * Compression Effect for the Imagick driver.
9 */
10class ImagickCompressionEffect extends AbstractCompressionEffect
11{
12    /**
13     * @param array $data The effect data, if available.
14     */
15    public function process(?array $data = null): static
16    {
17        if ($data !== null) {
18            $this->setData($data);
19        }
20
21        $this->image()->target();
22        $extension = strtolower((string)$this->image()->imagick()->getImageFormat());
23
24        $invalidExtensions = [ 'gif', 'bmp' ];
25
26
27        // Abort when facing invalid extensions
28        if (in_array($extension, $invalidExtensions)) {
29            return $this;
30        }
31
32        // PNG compression is... different.
33        // 0 is for the best quality, 9 for the worst quality
34        // So we calculate the opposite quality (100 - quality) and extra that percent from 9
35        // If quality = 100, changes to 0
36        // Quality is rounded (ceil) to avoid any compression problems
37        if ($extension === 'png') {
38            $reverse = (100 - $this->quality());
39            if ($reverse > 0) {
40                $reverse = min(ceil(($reverse / 100) * 9), 9); // Make sure it doesn't get further than 9
41            }
42
43            if ($reverse <= 9) {
44                $this->image()->imagick()->setOption('png:compression-level', $reverse);
45                $this->image()->imagick()->setOption('png:compression-filter', '5');
46            }
47        }
48
49        // Default image compression applies to jpg, jpeg, webp and tiff
50        if (in_array($extension, ['jpg', 'jpeg', 'webp', 'tiff'])) {
51            $this->image()->imagick()->setImageCompressionQuality($this->quality());
52        }
53
54        return $this;
55    }
56}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Imagick/Effect/ImagickCropEffect.php.html b/packages/image/build/report/Imagick/Effect/ImagickCropEffect.php.html deleted file mode 100644 index 5ceefde4d..000000000 --- a/packages/image/build/report/Imagick/Effect/ImagickCropEffect.php.html +++ /dev/null @@ -1,220 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickCropEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 36
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagickCropEffect
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 36
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
110
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 doCrop
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 36
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
110
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Imagick\Effect;
4
5use Imagick;
6use Charcoal\Image\Effect\AbstractCropEffect;
7
8/**
9 * Resize Effect for the Imagick driver
10 */
11class ImagickCropEffect extends AbstractCropEffect
12{
13    /**
14     * @param integer $width  The crop width.
15     * @param integer $height The crop height.
16     * @param integer $x      The x-position (in pixel) of the crop.
17     * @param integer $y      The y-position (in pixel) of the crop.
18     * @return void
19     */
20    protected function doCrop($width, $height, $x, $y)
21    {
22        $gravity = $this->image()->imagickGravity($this->gravity());
23
24        // This sets the gravity for the rest of the chain
25        $this->image()->imagick()->setGravity($gravity);
26
27        // Apply gravity to crop coordinates
28
29        $imageWidth  = $this->image()->width();
30        $imageHeight = $this->image()->height();
31
32        switch ($this->image()->imagick()->getGravity()) {
33            case Imagick::GRAVITY_NORTHWEST:
34                break;
35            case Imagick::GRAVITY_NORTH:
36                $x = ($imageWidth / 2 - $width / 2);
37                break;
38            case Imagick::GRAVITY_NORTHEAST:
39                $x = ($imageWidth - $width);
40                break;
41            case Imagick::GRAVITY_WEST:
42                $y = ($imageHeight / 2 - $height / 2);
43                break;
44            case Imagick::GRAVITY_CENTER:
45                $x = ($imageWidth / 2 - $width / 2);
46                $y = ($imageHeight / 2 - $height / 2);
47                break;
48            case Imagick::GRAVITY_EAST:
49                $x = ($imageWidth - $width);
50                $y = ($imageHeight / 2 - $height / 2);
51                break;
52            case Imagick::GRAVITY_SOUTHWEST:
53                $y = ($imageHeight - $height);
54                break;
55            case Imagick::GRAVITY_SOUTH:
56                $x = ($imageWidth / 2 - $width / 2);
57                $y = ($imageHeight - $height);
58                break;
59            case Imagick::GRAVITY_SOUTHEAST:
60                $x = ($imageWidth - $width);
61                $y = ($imageHeight - $height);
62                break;
63        }
64
65        $this->image()->imagick()->cropImage($width, $height, $x, $y);
66    }
67}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Imagick/Effect/ImagickDitherEffect.php.html b/packages/image/build/report/Imagick/Effect/ImagickDitherEffect.php.html deleted file mode 100644 index 7da426f05..000000000 --- a/packages/image/build/report/Imagick/Effect/ImagickDitherEffect.php.html +++ /dev/null @@ -1,190 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickDitherEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 81.82% covered (success) -
-
-
81.82%
9 / 11
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagickDitherEffect
-
- 81.82% covered (success) -
-
-
81.82%
9 / 11
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
3.05
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 process
-
- 81.82% covered (success) -
-
-
81.82%
9 / 11
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
3.05
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Imagick\Effect;
4
5use Imagick;
6use Charcoal\Image\Effect\AbstractDitherEffect;
7
8/**
9 * Dither Effect for the Imagick driver.
10 */
11class ImagickDitherEffect extends AbstractDitherEffect
12{
13    /**
14     * @param array $data The effect data, if available.
15     * @return ImagickDitherEffect Chainable
16     */
17    public function process(?array $data = null): static
18    {
19        if ($data !== null) {
20            $this->setData($data);
21        }
22
23        $mode = $this->mode();
24        if ($mode === '') {
25            $colorspace = $this->image()->imagick()->getColorSpace();
26            $tree_depth = 0;
27            $dither = true;
28            $this->image()->imagick()->quantizeImage($this->colors(), $colorspace, $tree_depth, $dither, false);
29        } else {
30            $this->image()->imagick()->orderedPosterizeImage($mode);
31        }
32
33        $this->image()->imagick()->setImageColorSpace(Imagick::COLORSPACE_GRAY);
34
35        return $this;
36    }
37}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Imagick/Effect/ImagickFormatEffect.php.html b/packages/image/build/report/Imagick/Effect/ImagickFormatEffect.php.html deleted file mode 100644 index 0e4ecd3cc..000000000 --- a/packages/image/build/report/Imagick/Effect/ImagickFormatEffect.php.html +++ /dev/null @@ -1,178 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickFormatEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 3
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagickFormatEffect
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 3
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
6
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 process
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 3
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
6
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Imagick\Effect;
4
5use Exception;
6use Charcoal\Image\Effect\AbstractFormatEffect;
7
8/**
9 * Format Effect for the Imagick driver.
10 */
11class ImagickFormatEffect extends AbstractFormatEffect
12{
13    /**
14     * @param array $data The effect data, if available.
15     * @return ImageFormatEffect Chainable
16     */
17    public function process(?array $data = null): static
18    {
19        if ($this->format()) {
20            $this->image()->imagick()->setimageFormat($this->format());
21        }
22
23        return $this;
24    }
25}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Imagick/Effect/ImagickGrayscaleEffect.php.html b/packages/image/build/report/Imagick/Effect/ImagickGrayscaleEffect.php.html deleted file mode 100644 index 4a21cd4db..000000000 --- a/packages/image/build/report/Imagick/Effect/ImagickGrayscaleEffect.php.html +++ /dev/null @@ -1,180 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickGrayscaleEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 75.00% covered (warning) -
-
-
75.00%
3 / 4
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagickGrayscaleEffect
-
- 75.00% covered (warning) -
-
-
75.00%
3 / 4
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2.06
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 process
-
- 75.00% covered (warning) -
-
-
75.00%
3 / 4
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2.06
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Imagick\Effect;
4
5use Imagick as Imagick;
6use Charcoal\Image\Effect\AbstractGrayscaleEffect;
7
8/**
9 * Grayscale Effect for the Imagick driver.
10 */
11class ImagickGrayscaleEffect extends AbstractGrayscaleEffect
12{
13    /**
14     * @param array $data The effect data, if available.
15     * @return ImagickGrayscaleEffect Chainable
16     */
17    public function process(?array $data = null): static
18    {
19        if ($data !== null) {
20            $this->setData($data);
21        }
22
23        $this->image()->imagick()->transformImageColorSpace(Imagick::COLORSPACE_GRAY);
24
25        return $this;
26    }
27}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Imagick/Effect/ImagickMaskEffect.php.html b/packages/image/build/report/Imagick/Effect/ImagickMaskEffect.php.html deleted file mode 100644 index 53b4c8fa4..000000000 --- a/packages/image/build/report/Imagick/Effect/ImagickMaskEffect.php.html +++ /dev/null @@ -1,182 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickMaskEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 5
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagickMaskEffect
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 5
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
6
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 process
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 5
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
6
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3declare(strict_types=1);
4
5namespace Charcoal\Image\Imagick\Effect;
6
7use Exception;
8use Charcoal\Image\Effect\AbstractMaskEffect;
9
10/**
11 * Mask Effect for the Imagick driver.
12 */
13class ImagickMaskEffect extends AbstractMaskEffect
14{
15    /**
16     * @param array $data The effect data, if available.
17     * @throws Exception This effect is not yet supported for Imagick driver.
18     */
19    public function process(?array $data = null): void
20    {
21        if ($data !== null) {
22            $this->setData($data);
23        }
24
25        throw new Exception(
26            'Mask effect is not (yet) supported with imagick driver.'
27        );
28    }
29}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Imagick/Effect/ImagickMirrorEffect.php.html b/packages/image/build/report/Imagick/Effect/ImagickMirrorEffect.php.html deleted file mode 100644 index 70c1ead27..000000000 --- a/packages/image/build/report/Imagick/Effect/ImagickMirrorEffect.php.html +++ /dev/null @@ -1,183 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickMirrorEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 85.71% covered (success) -
-
-
85.71%
6 / 7
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagickMirrorEffect
-
- 85.71% covered (success) -
-
-
85.71%
6 / 7
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
3.03
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 process
-
- 85.71% covered (success) -
-
-
85.71%
6 / 7
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
3.03
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Imagick\Effect;
4
5use Charcoal\Image\Effect\AbstractMirrorEffect;
6
7/**
8 * Mirror Effect for the Imagick driver.
9 */
10class ImagickMirrorEffect extends AbstractMirrorEffect
11{
12    /**
13     * @param array $data The effect data, if available.
14     */
15    public function process(?array $data = null): static
16    {
17        if ($data !== null) {
18            $this->setData($data);
19        }
20
21        $axis = $this->axis();
22        if ($axis == 'x') {
23            // Vertical mirror
24            $this->image()->imagick()->flipImage();
25        } else {
26            $this->image()->imagick()->flopImage();
27        }
28        return $this;
29    }
30}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Imagick/Effect/ImagickModulateEffect.php.html b/packages/image/build/report/Imagick/Effect/ImagickModulateEffect.php.html deleted file mode 100644 index 03660a5e9..000000000 --- a/packages/image/build/report/Imagick/Effect/ImagickModulateEffect.php.html +++ /dev/null @@ -1,183 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickModulateEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 85.71% covered (success) -
-
-
85.71%
6 / 7
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagickModulateEffect
-
- 85.71% covered (success) -
-
-
85.71%
6 / 7
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2.01
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 process
-
- 85.71% covered (success) -
-
-
85.71%
6 / 7
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2.01
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Imagick\Effect;
4
5use Charcoal\Image\Effect\AbstractModulateEffect;
6
7/**
8 * Module Effect for the Imagick driver.
9 */
10class ImagickModulateEffect extends AbstractModulateEffect
11{
12    /**
13     * @param array $data The effect data, if available.
14     * @return AbstractModulateEffect Chainable
15     */
16    public function process(?array $data = null): static
17    {
18        if ($data !== null) {
19            $this->setData($data);
20        }
21
22        $h = ($this->hue() + 100);
23        $s = ($this->saturation() + 100);
24        $l = ($this->luminance() + 100);
25
26        $this->image()->imagick()->modulateImage($l, $s, $h);
27
28        return $this;
29    }
30}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Imagick/Effect/ImagickResizeEffect.php.html b/packages/image/build/report/Imagick/Effect/ImagickResizeEffect.php.html deleted file mode 100644 index 35dbcbbbb..000000000 --- a/packages/image/build/report/Imagick/Effect/ImagickResizeEffect.php.html +++ /dev/null @@ -1,180 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickResizeEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 66.67% covered (warning) -
-
-
66.67%
2 / 3
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagickResizeEffect
-
- 66.67% covered (warning) -
-
-
66.67%
2 / 3
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2.15
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 doResize
-
- 66.67% covered (warning) -
-
-
66.67%
2 / 3
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2.15
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Imagick\Effect;
4
5use Imagick;
6use Charcoal\Image\Effect\AbstractResizeEffect;
7
8/**
9 * Resize Effect for the Imagick driver
10 */
11class ImagickResizeEffect extends AbstractResizeEffect
12{
13    /**
14     * @param integer $width   The target width.
15     * @param integer $height  The target height.
16     * @param boolean $bestFit The "best_fit" flag.
17     * @return void
18     */
19    protected function doResize($width, $height, $bestFit = false)
20    {
21        if ($this->adaptive()) {
22            $this->image()->imagick()->adaptiveResizeImage($width, $height, $bestFit);
23        } else {
24            $this->image()->imagick()->resizeImage($width, $height, Imagick::FILTER_LANCZOS, 1, $bestFit);
25        }
26    }
27}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Imagick/Effect/ImagickRevertEffect.php.html b/packages/image/build/report/Imagick/Effect/ImagickRevertEffect.php.html deleted file mode 100644 index 50021d1e7..000000000 --- a/packages/image/build/report/Imagick/Effect/ImagickRevertEffect.php.html +++ /dev/null @@ -1,180 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickRevertEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 80.00% covered (success) -
-
-
80.00%
4 / 5
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagickRevertEffect
-
- 80.00% covered (success) -
-
-
80.00%
4 / 5
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2.03
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 process
-
- 80.00% covered (success) -
-
-
80.00%
4 / 5
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2.03
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Imagick\Effect;
4
5use Charcoal\Image\Effect\AbstractRevertEffect;
6
7/**
8 * Revert Effect for the Imagick driver.
9 */
10class ImagickRevertEffect extends AbstractRevertEffect
11{
12     /**
13      * @param array $data The effect data, if available.
14      * @return ImagickRevertEffect Chainable
15      */
16    public function process(?array $data = null): static
17    {
18        if ($data !== null) {
19            $this->setData($data);
20        }
21
22        $channel = $this->image()->imagickChannel($this->channel());
23        $this->image()->imagick()->negateImage(false, $channel);
24
25        return $this;
26    }
27}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Imagick/Effect/ImagickRotateEffect.php.html b/packages/image/build/report/Imagick/Effect/ImagickRotateEffect.php.html deleted file mode 100644 index cc81fc0a0..000000000 --- a/packages/image/build/report/Imagick/Effect/ImagickRotateEffect.php.html +++ /dev/null @@ -1,178 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickRotateEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 75.00% covered (warning) -
-
-
75.00%
3 / 4
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagickRotateEffect
-
- 75.00% covered (warning) -
-
-
75.00%
3 / 4
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2.06
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 process
-
- 75.00% covered (warning) -
-
-
75.00%
3 / 4
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2.06
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Imagick\Effect;
4
5use Charcoal\Image\Effect\AbstractRotateEffect;
6
7/**
8 * Rotate Effect for the imagick driver.
9 */
10class ImagickRotateEffect extends AbstractRotateEffect
11{
12    /**
13     * @param array $data The effect data, if available.
14     * @return ImagickRotateEffect Chainable
15     */
16    public function process(?array $data = null): static
17    {
18        if ($data !== null) {
19            $this->setData($data);
20        }
21
22        $this->image()->imagick()->rotateImage($this->backgroundColor(), $this->angle());
23        return $this;
24    }
25}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Imagick/Effect/ImagickSepiaEffect.php.html b/packages/image/build/report/Imagick/Effect/ImagickSepiaEffect.php.html deleted file mode 100644 index 024ee7d57..000000000 --- a/packages/image/build/report/Imagick/Effect/ImagickSepiaEffect.php.html +++ /dev/null @@ -1,179 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickSepiaEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 75.00% covered (warning) -
-
-
75.00%
3 / 4
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagickSepiaEffect
-
- 75.00% covered (warning) -
-
-
75.00%
3 / 4
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2.06
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 process
-
- 75.00% covered (warning) -
-
-
75.00%
3 / 4
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2.06
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Imagick\Effect;
4
5use Charcoal\Image\Effect\AbstractSepiaEffect;
6
7/**
8 * Sepia Effect for the Imagick driver.
9 */
10class ImagickSepiaEffect extends AbstractSepiaEffect
11{
12    /**
13     * @param array $data The effect data, if available.
14     * @return ImagickSepiaEffect Chainable
15     */
16    public function process(?array $data = null): static
17    {
18        if ($data !== null) {
19            $this->setData($data);
20        }
21
22        $this->image()->imagick()->sepiaToneImage($this->threshold());
23
24        return $this;
25    }
26}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Imagick/Effect/ImagickSharpenEffect.php.html b/packages/image/build/report/Imagick/Effect/ImagickSharpenEffect.php.html deleted file mode 100644 index 20900aae2..000000000 --- a/packages/image/build/report/Imagick/Effect/ImagickSharpenEffect.php.html +++ /dev/null @@ -1,244 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickSharpenEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 76.92% covered (warning) -
-
-
76.92%
10 / 13
-
- 66.67% covered (warning) -
-
-
66.67%
2 / 3
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagickSharpenEffect
-
- 76.92% covered (warning) -
-
-
76.92%
10 / 13
-
- 66.67% covered (warning) -
-
-
66.67%
2 / 3
3.11
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 processAdaptive
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 3
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2
 processUnsharp
-
- 100.00% covered (success) -
-
-
100.00%
7 / 7
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 processStandard
-
- 100.00% covered (success) -
-
-
100.00%
3 / 3
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Imagick\Effect;
4
5use Charcoal\Image\Effect\AbstractSharpenEffect;
6
7/**
8 * Sharpen Effect for Imagick driver.
9 */
10class ImagickSharpenEffect extends AbstractSharpenEffect
11{
12    /**
13     * @return ImagickSharpenEffect Chainable
14     */
15    public function processAdaptive(): static
16    {
17        $channel = $this->image()->imagickChannel($this->channel());
18        $this->image()->imagick()->adaptiveAbstractSharpenEffectImage($this->radius(), $this->sigma(), $channel);
19        return $this;
20    }
21
22    /**
23     * @return ImagickSharpenEffect Chainable
24     */
25    public function processUnsharp(): static
26    {
27        $radius = $this->radius();
28        $sigma = $this->sigma();
29        $amount = $this->amount();
30        $threshold = $this->threshold();
31        $channel = $this->image()->imagickChannel($this->channel());
32
33        $this->image()->imagick()->unsharpMaskImage($radius, $sigma, $amount, $threshold, $channel);
34
35        return $this;
36    }
37
38    /**
39     * @return ImagickSharpenEffect Chainable
40     */
41    public function processStandard(): static
42    {
43        $channel = $this->image()->imagickChannel($this->channel());
44        $this->image()->imagick()->sharpenImage($this->radius(), $this->sigma(), $channel);
45        return $this;
46    }
47}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Imagick/Effect/ImagickThresholdEffect.php.html b/packages/image/build/report/Imagick/Effect/ImagickThresholdEffect.php.html deleted file mode 100644 index c97a80dbb..000000000 --- a/packages/image/build/report/Imagick/Effect/ImagickThresholdEffect.php.html +++ /dev/null @@ -1,181 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickThresholdEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 85.71% covered (success) -
-
-
85.71%
6 / 7
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagickThresholdEffect
-
- 85.71% covered (success) -
-
-
85.71%
6 / 7
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2.01
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 process
-
- 85.71% covered (success) -
-
-
85.71%
6 / 7
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2.01
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Imagick\Effect;
4
5use Charcoal\Image\Effect\AbstractThresholdEffect;
6
7/**
8 * Threshold Effect for the Imagick driver.
9 */
10class ImagickThresholdEffect extends AbstractThresholdEffect
11{
12    /**
13     * @param array $data The effect data, if available.
14     */
15    public function process(?array $data = null): static
16    {
17        if ($data !== null) {
18            $this->setData($data);
19        }
20
21        $max = $this->image()->imagick()->getQuantumRange();
22        $max = $max['quantumRangeLong'];
23        $threshold = ($this->threshold() * $max);
24        $this->image()->imagick()->thresholdImage($threshold);
25
26        return $this;
27    }
28}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Imagick/Effect/ImagickTintEffect.php.html b/packages/image/build/report/Imagick/Effect/ImagickTintEffect.php.html deleted file mode 100644 index c5d609a8f..000000000 --- a/packages/image/build/report/Imagick/Effect/ImagickTintEffect.php.html +++ /dev/null @@ -1,185 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickTintEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 87.50% covered (success) -
-
-
87.50%
7 / 8
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagickTintEffect
-
- 87.50% covered (success) -
-
-
87.50%
7 / 8
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
3.02
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 process
-
- 87.50% covered (success) -
-
-
87.50%
7 / 8
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
3.02
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Imagick\Effect;
4
5use ImagickPixel;
6use Charcoal\Image\Effect\AbstractTintEffect;
7
8/**
9 * Tint Effect for the Imagick driver.
10 */
11class ImagickTintEffect extends AbstractTintEffect
12{
13    /**
14     * @param array $data The effect data, if available.
15     */
16    public function process(?array $data = null): static
17    {
18        if ($data !== null) {
19            $this->setData($data);
20        }
21        $color = new ImagickPixel($this->color());
22        $opacity = new ImagickPixel(sprintf('rgba(255,255,255,%f)', $this->opacity()));
23
24        if ($this->midtone() === true) {
25            $this->image()->imagick()->tintImage($color, $opacity);
26        } else {
27            $this->image()->imagick()->colorizeImage($color, $opacity);
28        }
29
30        return $this;
31    }
32}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Imagick/Effect/ImagickWatermarkEffect.php.html b/packages/image/build/report/Imagick/Effect/ImagickWatermarkEffect.php.html deleted file mode 100644 index 47f752486..000000000 --- a/packages/image/build/report/Imagick/Effect/ImagickWatermarkEffect.php.html +++ /dev/null @@ -1,246 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect/ImagickWatermarkEffect.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 82.14% covered (success) -
-
-
82.14%
46 / 56
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagickWatermarkEffect
-
- 82.14% covered (success) -
-
-
82.14%
46 / 56
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
15.12
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 process
-
- 82.14% covered (success) -
-
-
82.14%
46 / 56
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
15.12
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Imagick\Effect;
4
5use Exception;
6use Imagick;
7use Charcoal\Image\Effect\AbstractWatermarkEffect;
8use Charcoal\Image\ImageInterface;
9
10/**
11 * Watermark Effect for the Imagick driver.
12 */
13class ImagickWatermarkEffect extends AbstractWatermarkEffect
14{
15    /**
16     * @param array $data The effect data, if available.
17     * @throws Exception If the image data is invalid.
18     */
19    public function process(?array $data = null): static
20    {
21        if ($data !== null) {
22            $this->setData($data);
23        }
24
25        $img = $this->image();
26        $imageWidth = $img->width();
27        $imageHeight = $img->height();
28
29        if ($this->watermark() instanceof ImageInterface) {
30            $watermark = $this->watermark();
31        } else {
32            $imgClass = $img::class;
33            $watermark = new $imgClass();
34            $watermark->open($this->watermark());
35        }
36
37        if (($watermark->width() > $imageWidth) || ($watermark->height() > $imageHeight)) {
38            // Scale-down watermark image, if necessary
39            $watermark->resize([
40                'mode' => 'best_fit',
41                'width' => $imageWidth,
42                'height' => $imageHeight
43            ]);
44        }
45
46        $watermarkWidth = $watermark->width();
47        $watermarkHeight = $watermark->height();
48
49        $gravity = $this->gravity();
50        if ($gravity == 'nw') {
51            $x = $this->x();
52            $y = $this->y();
53        } elseif ($gravity == 'n') {
54            $x = ($imageWidth / 2 - ($watermarkWidth / 2) + $this->x());
55            $y = $this->y();
56        } elseif ($gravity == 'ne') {
57            $x = ($imageWidth - $watermarkHeight - $this->x());
58            $y = $this->y();
59        } elseif ($gravity == 'w') {
60            $x = $this->x();
61            $y = ($imageHeight / 2 - ($watermarkHeight / 2) + $this->y());
62        } elseif ($gravity == 'center') {
63            $x = ($imageWidth / 2 - ($watermarkWidth / 2) + $this->x());
64            $y = ($imageHeight / 2 - ($watermarkHeight / 2) + $this->y());
65        } elseif ($gravity == 'e') {
66            $x = ($imageWidth - $watermarkWidth - $this->x());
67            $y = ($imageHeight / 2 - ($watermarkHeight / 2) + $this->y());
68        } elseif ($gravity == 'sw') {
69            $x = $this->x();
70            $y = ($imageHeight - $watermarkHeight - $this->y());
71        } elseif ($gravity == 's') {
72            $x = ($imageWidth / 2 - ($watermarkWidth / 2) + $this->x());
73            $y = ($imageHeight - $watermarkHeight - $this->y());
74        } elseif ($gravity == 'se') {
75            $x = ($imageWidth - $watermarkWidth - $this->x());
76            $y = ($imageHeight - $watermarkHeight - $this->y());
77        } else {
78            throw new Exception(
79                'Invalid gravity'
80            );
81        }
82
83        $x = max(0, $x);
84        $x = min(($imageWidth - $watermarkWidth), $x);
85        $y = max(0, $y);
86        $y = min(($imageHeight - $watermarkHeight), $y);
87
88        $compositeMode = Imagick::COMPOSITE_MULTIPLY;
89        $this->image()->imagick()->compositeImage($watermark->imagick(), $compositeMode, $x, $y);
90
91        return $this;
92    }
93}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Imagick/Effect/dashboard.html b/packages/image/build/report/Imagick/Effect/dashboard.html deleted file mode 100644 index 35a9b8209..000000000 --- a/packages/image/build/report/Imagick/Effect/dashboard.html +++ /dev/null @@ -1,342 +0,0 @@ - - - - - Dashboard for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect - - - - - - - -
-
-
- -
-
-
-
-
-
-

Classes

-
-
-
-
-

Coverage Distribution

-
- -
-
-
-

Complexity

-
- -
-
-
- -
-
-

Methods

-
-
-
-
-

Coverage Distribution

-
- -
-
-
-

Complexity

-
- -
-
-
-
-
-

Insufficient Coverage

-
- - - - - - - - - - - - - - - - - - - - - -
MethodCoverage
process0%
processSoft0%
process0%
doCrop0%
process0%
process0%
processAdaptive0%
doResize66%
process75%
process75%
process75%
-
-
-
-

Project Risks

-
- - - - - - - - - - - - - - - - - - - - - -
MethodCoverageComplexityCRAP
process0.0%11132
doCrop0.0%10110
process0.0%756
process0.0%26
process0.0%26
process75.0%22
doResize66.7%22
process75.0%22
process75.0%22
-
-
-
- -
- - - - - - diff --git a/packages/image/build/report/Imagick/Effect/index.html b/packages/image/build/report/Imagick/Effect/index.html deleted file mode 100644 index c16022baa..000000000 --- a/packages/image/build/report/Imagick/Effect/index.html +++ /dev/null @@ -1,596 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/Effect - - - - - - - -
- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 50.21% covered (warning) -
-
-
50.21%
121 / 241
-
- 28.00% covered (danger) -
-
-
28.00%
7 / 25
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 18
ImagickAutoorientationEffect.php
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 32
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagickBlurEffect.php
-
- 84.21% covered (success) -
-
-
84.21%
16 / 19
-
- 83.33% covered (success) -
-
-
83.33%
5 / 6
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagickCompressionEffect.php
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 17
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagickCropEffect.php
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 36
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagickDitherEffect.php
-
- 81.82% covered (success) -
-
-
81.82%
9 / 11
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagickFormatEffect.php
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 3
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagickGrayscaleEffect.php
-
- 75.00% covered (warning) -
-
-
75.00%
3 / 4
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagickMaskEffect.php
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 5
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagickMirrorEffect.php
-
- 85.71% covered (success) -
-
-
85.71%
6 / 7
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagickModulateEffect.php
-
- 85.71% covered (success) -
-
-
85.71%
6 / 7
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagickResizeEffect.php
-
- 66.67% covered (warning) -
-
-
66.67%
2 / 3
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagickRevertEffect.php
-
- 80.00% covered (success) -
-
-
80.00%
4 / 5
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagickRotateEffect.php
-
- 75.00% covered (warning) -
-
-
75.00%
3 / 4
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagickSepiaEffect.php
-
- 75.00% covered (warning) -
-
-
75.00%
3 / 4
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagickSharpenEffect.php
-
- 76.92% covered (warning) -
-
-
76.92%
10 / 13
-
- 66.67% covered (warning) -
-
-
66.67%
2 / 3
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagickThresholdEffect.php
-
- 85.71% covered (success) -
-
-
85.71%
6 / 7
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagickTintEffect.php
-
- 87.50% covered (success) -
-
-
87.50%
7 / 8
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagickWatermarkEffect.php
-
- 82.14% covered (success) -
-
-
82.14%
46 / 56
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- -
- - diff --git a/packages/image/build/report/Imagick/ImagickImage.php.html b/packages/image/build/report/Imagick/ImagickImage.php.html deleted file mode 100644 index 49861b7f5..000000000 --- a/packages/image/build/report/Imagick/ImagickImage.php.html +++ /dev/null @@ -1,537 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick/ImagickImage.php - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 88.89% covered (success) -
-
-
88.89%
64 / 72
-
- 70.00% covered (warning) -
-
-
70.00%
7 / 10
CRAP
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
ImagickImage
-
- 88.89% covered (success) -
-
-
88.89%
64 / 72
-
- 70.00% covered (warning) -
-
-
70.00%
7 / 10
25.86
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 __construct
-
- 40.00% covered (danger) -
-
-
40.00%
2 / 5
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
4.94
 driverType
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 imagick
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 create
-
- 100.00% covered (success) -
-
-
100.00%
10 / 10
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
5
 open
-
- 80.00% covered (success) -
-
-
80.00%
8 / 10
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
5.20
 save
-
- 66.67% covered (warning) -
-
-
66.67%
6 / 9
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
4.59
 width
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 height
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
 imagickChannel
-
- 100.00% covered (success) -
-
-
100.00%
18 / 18
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
2
 imagickGravity
-
- 100.00% covered (success) -
-
-
100.00%
16 / 16
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
2
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1<?php
2
3namespace Charcoal\Image\Imagick;
4
5use Exception;
6use InvalidArgumentException;
7use Imagick;
8use Charcoal\Image\AbstractImage;
9
10/**
11 *
12 */
13class ImagickImage extends AbstractImage
14{
15    private readonly \Imagick $imagick;
16
17    /**
18     * @throws Exception If imagick driver can not be loaded.
19     */
20    public function __construct()
21    {
22        if (!extension_loaded('imagick') || !class_exists('Imagick')) {
23            throw new Exception(
24                'The Imagick PHP extension is required.'
25            );
26        }
27        $this->imagick = new Imagick();
28    }
29
30    public function driverType(): string
31    {
32        return 'imagick';
33    }
34
35    public function imagick(): \Imagick
36    {
37        return $this->imagick;
38    }
39
40    /**
41     * Create a blank canvas of a given size, with a given background color.
42     *
43     * @param integer $width  Image size, in pixels.
44     * @param integer $height Image height, in pixels.
45     * @param string  $color  Default to transparent.
46     * @throws InvalidArgumentException If the size arguments are not valid, positive integers.
47     */
48    public function create($width, $height, $color = 'rgb(100%, 100%, 100%, 0)'): static
49    {
50        if (!is_numeric($width) || $width < 1) {
51            throw new InvalidArgumentException(
52                'Width must be an integer of at least 1 pixel'
53            );
54        }
55        if (!is_numeric($height) || $height < 1) {
56            throw new InvalidArgumentException(
57                'Height must be an integer of at least 1 pixel'
58            );
59        }
60        $this->imagick()->newImage((int)$width, (int)$height, $color);
61        return $this;
62    }
63
64    /**
65     * Open an image file
66     *
67     * @param string $source The source path / filename.
68     * @throws InvalidArgumentException If the source argument is not a string.
69     */
70    public function open($source = null): static
71    {
72        if ($source !== null && !is_string($source)) {
73            throw new InvalidArgumentException(
74                'Source must be a string'
75            );
76        }
77
78        $source = $source ?: $this->source();
79        if (parse_url($source, PHP_URL_HOST)) {
80            $handle = fopen($source, 'rb');
81            $this->imagick()->readImageFile($handle);
82        } else {
83            $this->imagick()->readImage($source);
84        }
85
86        return $this;
87    }
88
89    /**
90     * Save an image to a target.
91     * If no target is set, the original source will be owerwritten
92     *
93     * @param string $target The target path / filename.
94     * @throws InvalidArgumentException If the target argument is not a string.
95     */
96    public function save($target = null): static
97    {
98        if ($target !== null && !is_string($target)) {
99            throw new InvalidArgumentException(
100                'Target must be a string (file path)'
101            );
102        }
103
104        $target  = $target ?: $this->target();
105        $fileExt = pathinfo($target, PATHINFO_EXTENSION);
106
107        $this->imagick()->setImageFormat($fileExt);
108        $this->imagick()->writeImage($target);
109
110        return $this;
111    }
112
113    /**
114     * Get the image's width, in pixels
115     */
116    public function width(): int
117    {
118        return $this->imagick()->getImageWidth();
119    }
120
121    /**
122     * Get the image's height, in pixels
123     */
124    public function height(): int
125    {
126        return $this->imagick()->getImageHeight();
127    }
128
129    /**
130     * Convert a channel name (string) to an `Imagick::CHANNEL_*` constant (integer)
131     *
132     * @param string $channel The standard "channel" string.
133     * @throws InvalidArgumentException If the channel argument is not a valid channel.
134     */
135    public function imagickChannel($channel): int
136    {
137        $channelMap = [
138            // RGB
139            'red'       => Imagick::CHANNEL_RED,
140            'green'     => Imagick::CHANNEL_GREEN,
141            'blue'      => Imagick::CHANNEL_BLUE,
142            // CMYK
143            'cyan'      => Imagick::CHANNEL_CYAN,
144            'magenta'   => Imagick::CHANNEL_MAGENTA,
145            'yellow'    => Imagick::CHANNEL_YELLOW,
146            'black'     => Imagick::CHANNEL_BLACK,
147            // Others
148            'all'       => Imagick::CHANNEL_ALL,
149            'alpha'     => Imagick::CHANNEL_ALPHA,
150            'opacity'   => Imagick::CHANNEL_RED,
151            'gray'      => Imagick::CHANNEL_GRAY
152        ];
153        if (!isset($channelMap[$channel])) {
154            throw new InvalidArgumentException(
155                'Invalid channel'
156            );
157        }
158        return $channelMap[$channel];
159    }
160
161    /**
162     * Convert a gravity name (string) to an `Imagick::GRAVITY_*` constant (integer)
163     *
164     * @param string $gravity The standard gravity name.
165     * @throws InvalidArgumentException If the gravity argument is not a valid gravity type.
166     */
167    public function imagickGravity($gravity): int
168    {
169        $gravityMap = [
170            'center'    => Imagick::GRAVITY_CENTER,
171            'n'         => Imagick::GRAVITY_NORTH,
172            's'         => Imagick::GRAVITY_SOUTH,
173            'e'         => Imagick::GRAVITY_EAST,
174            'w'         => Imagick::GRAVITY_WEST,
175            'ne'        => Imagick::GRAVITY_NORTHEAST,
176            'nw'        => Imagick::GRAVITY_NORTHWEST,
177            'se'        => Imagick::GRAVITY_SOUTHEAST,
178            'sw'        => Imagick::GRAVITY_SOUTHWEST
179        ];
180        if (!isset($gravityMap[$gravity])) {
181            throw new InvalidArgumentException(
182                'Invalid gravity'
183            );
184        }
185        return $gravityMap[$gravity];
186    }
187}
- - -
-
-

Legend

-

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

-

- Generated by php-code-coverage 12.5.6 using PHP 8.5.5 and PHPUnit 12.5.28 at Mon Jun 1 15:56:44 UTC 2026. -

- - - -
-
- - - - - diff --git a/packages/image/build/report/Imagick/dashboard.html b/packages/image/build/report/Imagick/dashboard.html deleted file mode 100644 index 22a263b9a..000000000 --- a/packages/image/build/report/Imagick/dashboard.html +++ /dev/null @@ -1,345 +0,0 @@ - - - - - Dashboard for /var/www/html/packages/image/src/Charcoal/Image/Imagick - - - - - - - -
-
-
- -
-
-
-
-
-
-

Classes

-
-
-
-
-

Coverage Distribution

-
- -
-
-
-

Complexity

-
- -
-
-
- -
-
-

Methods

-
-
-
-
-

Coverage Distribution

-
- -
-
-
-

Complexity

-
- -
-
-
-
-
-

Insufficient Coverage

-
- - - - - - - - - - - - - - - - - - - - - - - -
MethodCoverage
process0%
processSoft0%
process0%
doCrop0%
process0%
process0%
processAdaptive0%
__construct40%
doResize66%
save66%
process75%
process75%
process75%
-
-
-
-

Project Risks

-
- - - - - - - - - - - - - - - - - - - - - - - -
MethodCoverageComplexityCRAP
process0.0%11132
doCrop0.0%10110
process0.0%756
process0.0%26
process0.0%26
__construct40.0%34
save66.7%44
process75.0%22
doResize66.7%22
process75.0%22
process75.0%22
-
-
-
- -
- - - - - - diff --git a/packages/image/build/report/Imagick/index.html b/packages/image/build/report/Imagick/index.html deleted file mode 100644 index f2c4f8017..000000000 --- a/packages/image/build/report/Imagick/index.html +++ /dev/null @@ -1,147 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image/Imagick - - - - - - - -
-
- -
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 59.11% covered (warning) -
-
-
59.11%
185 / 313
-
- 40.00% covered (danger) -
-
-
40.00%
14 / 35
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 19
Effect
-
- 50.21% covered (warning) -
-
-
50.21%
121 / 241
-
- 28.00% covered (danger) -
-
-
28.00%
7 / 25
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 18
ImagickImage.php
-
- 88.89% covered (success) -
-
-
88.89%
64 / 72
-
- 70.00% covered (warning) -
-
-
70.00%
7 / 10
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- -
- - diff --git a/packages/image/build/report/dashboard.html b/packages/image/build/report/dashboard.html deleted file mode 100644 index 35be110b4..000000000 --- a/packages/image/build/report/dashboard.html +++ /dev/null @@ -1,469 +0,0 @@ - - - - - Dashboard for /var/www/html/packages/image/src/Charcoal/Image - - - - - - - -
-
-
- -
-
-
-
-
-
-

Classes

-
-
-
-
-

Coverage Distribution

-
- -
-
-
-

Complexity

-
- -
-
-
-
- - -
-
-
-

Methods

-
-
-
-
-

Coverage Distribution

-
- -
-
-
-

Complexity

-
- -
-
-
-
- -
-

Project Risks

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MethodCoverageComplexityCRAP
process40.5%38342
process0.0%11132
doCrop0.0%10110
cmd0.0%872
process0.0%756
doCrop0.0%530
findCmd33.3%826
setGeometry0.0%420
process0.0%420
process0.0%312
ratio0.0%312
orientation0.0%312
setWidth0.0%312
setHeight0.0%312
setX0.0%312
setY0.0%312
process0.0%312
setMinWidth0.0%312
setMinHeight0.0%312
setMaxWidth0.0%312
setMaxHeight0.0%312
process0.0%312
save45.5%59
createEffect57.9%56
setGravity0.0%26
setFormat0.0%26
process0.0%26
process0.0%26
process0.0%26
process0.0%26
compositeCmd0.0%26
imagemagickGravity0.0%26
process0.0%26
process0.0%26
process75.0%55
create66.7%55
__construct40.0%34
save66.7%44
applyCmd57.1%33
image40.0%22
setEffects75.0%22
process75.0%22
width75.0%22
height75.0%22
convertCmd75.0%22
process75.0%22
doResize66.7%22
process75.0%22
process75.0%22
-
-
-
- -
- - - - - - diff --git a/packages/image/build/report/index.html b/packages/image/build/report/index.html deleted file mode 100644 index ad9a0a45a..000000000 --- a/packages/image/build/report/index.html +++ /dev/null @@ -1,297 +0,0 @@ - - - - - Code Coverage for /var/www/html/packages/image/src/Charcoal/Image - - - - - - - -
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 62.21% covered (warning) -
-
-
62.21%
805 / 1294
-
- 57.87% covered (warning) -
-
-
57.87%
125 / 216
-
- 20.69% covered (danger) -
-
-
20.69%
12 / 58
Effect
-
- 71.32% covered (warning) -
-
-
71.32%
363 / 509
-
- 74.77% covered (warning) -
-
-
74.77%
80 / 107
-
- 64.71% covered (warning) -
-
-
64.71%
11 / 17
Imagemagick
-
- 51.81% covered (warning) -
-
-
51.81%
172 / 332
-
- 34.78% covered (danger) -
-
-
34.78%
16 / 46
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 19
Imagick
-
- 59.11% covered (warning) -
-
-
59.11%
185 / 313
-
- 40.00% covered (danger) -
-
-
40.00%
14 / 35
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 19
AbstractEffect.php
-
- 80.00% covered (success) -
-
-
80.00%
12 / 15
-
- 80.00% covered (success) -
-
-
80.00%
4 / 5
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
AbstractImage.php
-
- 56.30% covered (warning) -
-
-
56.30%
67 / 119
-
- 42.86% covered (warning) -
-
-
42.86%
9 / 21
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
EffectFactory.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
EffectInterface.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
ImageFactory.php
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
-
- 100.00% covered (success) -
-
-
100.00%
2 / 2
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
ImageInterface.php
n/a
0 / 0
n/a
0 / 0
n/a
0 / 0
-
- -
- - From 05797be1302a7b2aab755006462ce58412a4a445 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Mon, 1 Jun 2026 13:05:00 -0400 Subject: [PATCH 89/94] chore(gitignore): add build report directories to ignore list --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 30024c77a..e53552d75 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ phpunit.xml psalm.xml vendor/ tests/logs +build/report/ +packages/**/build/report From dfa506983a76bf6c26e91c3c7ffa11d5b4195329 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Mon, 1 Jun 2026 13:05:08 -0400 Subject: [PATCH 90/94] tests(config): add failOnDeprecation and failOnPhpunitWarning to PHPUnit configurations across all packages --- packages/admin/phpunit.xml.dist | 4 +++- packages/app/phpunit.xml.dist | 4 +++- packages/attachment/phpunit.xml.dist | 4 +++- packages/cache/phpunit.xml.dist | 4 +++- packages/cms/phpunit.xml.dist | 4 +++- packages/config/phpunit.xml.dist | 4 +++- packages/core/phpunit.xml.dist | 4 +++- packages/email/phpunit.xml.dist | 4 +++- packages/factory/phpunit.xml.dist | 4 +++- packages/image/phpunit.xml.dist | 4 +++- packages/object/phpunit.xml.dist | 4 +++- packages/property/phpunit.xml.dist | 4 +++- packages/queue/phpunit.xml.dist | 4 +++- packages/translator/phpunit.xml.dist | 4 +++- packages/ui/phpunit.xml.dist | 4 +++- packages/user/phpunit.xml.dist | 4 +++- packages/view/phpunit.xml.dist | 4 +++- phpunit.xml.dist | 4 +++- 18 files changed, 54 insertions(+), 18 deletions(-) diff --git a/packages/admin/phpunit.xml.dist b/packages/admin/phpunit.xml.dist index 9797e5ccf..93e59b0cb 100644 --- a/packages/admin/phpunit.xml.dist +++ b/packages/admin/phpunit.xml.dist @@ -9,7 +9,9 @@ cacheDirectory=".phpunit.cache" backupStaticProperties="false" failOnWarning="false" - failOnNotice="false"> + failOnPhpunitWarning="false" + failOnNotice="false" + failOnDeprecation="false"> ./tests/Charcoal diff --git a/packages/app/phpunit.xml.dist b/packages/app/phpunit.xml.dist index 5d32205ed..495930861 100644 --- a/packages/app/phpunit.xml.dist +++ b/packages/app/phpunit.xml.dist @@ -9,7 +9,9 @@ cacheDirectory=".phpunit.cache" backupStaticProperties="false" failOnWarning="false" - failOnNotice="false"> + failOnPhpunitWarning="false" + failOnNotice="false" + failOnDeprecation="false"> ./tests/Charcoal diff --git a/packages/attachment/phpunit.xml.dist b/packages/attachment/phpunit.xml.dist index 5d32205ed..495930861 100644 --- a/packages/attachment/phpunit.xml.dist +++ b/packages/attachment/phpunit.xml.dist @@ -9,7 +9,9 @@ cacheDirectory=".phpunit.cache" backupStaticProperties="false" failOnWarning="false" - failOnNotice="false"> + failOnPhpunitWarning="false" + failOnNotice="false" + failOnDeprecation="false"> ./tests/Charcoal diff --git a/packages/cache/phpunit.xml.dist b/packages/cache/phpunit.xml.dist index 5d32205ed..495930861 100644 --- a/packages/cache/phpunit.xml.dist +++ b/packages/cache/phpunit.xml.dist @@ -9,7 +9,9 @@ cacheDirectory=".phpunit.cache" backupStaticProperties="false" failOnWarning="false" - failOnNotice="false"> + failOnPhpunitWarning="false" + failOnNotice="false" + failOnDeprecation="false"> ./tests/Charcoal diff --git a/packages/cms/phpunit.xml.dist b/packages/cms/phpunit.xml.dist index 5d32205ed..495930861 100644 --- a/packages/cms/phpunit.xml.dist +++ b/packages/cms/phpunit.xml.dist @@ -9,7 +9,9 @@ cacheDirectory=".phpunit.cache" backupStaticProperties="false" failOnWarning="false" - failOnNotice="false"> + failOnPhpunitWarning="false" + failOnNotice="false" + failOnDeprecation="false"> ./tests/Charcoal diff --git a/packages/config/phpunit.xml.dist b/packages/config/phpunit.xml.dist index c91f4c66f..e84a44b3d 100644 --- a/packages/config/phpunit.xml.dist +++ b/packages/config/phpunit.xml.dist @@ -9,7 +9,9 @@ cacheDirectory=".phpunit.cache" backupStaticProperties="false" failOnWarning="false" - failOnNotice="false"> + failOnPhpunitWarning="false" + failOnNotice="false" + failOnDeprecation="false"> ./tests/Charcoal diff --git a/packages/core/phpunit.xml.dist b/packages/core/phpunit.xml.dist index 5d32205ed..495930861 100644 --- a/packages/core/phpunit.xml.dist +++ b/packages/core/phpunit.xml.dist @@ -9,7 +9,9 @@ cacheDirectory=".phpunit.cache" backupStaticProperties="false" failOnWarning="false" - failOnNotice="false"> + failOnPhpunitWarning="false" + failOnNotice="false" + failOnDeprecation="false"> ./tests/Charcoal diff --git a/packages/email/phpunit.xml.dist b/packages/email/phpunit.xml.dist index 5d32205ed..495930861 100644 --- a/packages/email/phpunit.xml.dist +++ b/packages/email/phpunit.xml.dist @@ -9,7 +9,9 @@ cacheDirectory=".phpunit.cache" backupStaticProperties="false" failOnWarning="false" - failOnNotice="false"> + failOnPhpunitWarning="false" + failOnNotice="false" + failOnDeprecation="false"> ./tests/Charcoal diff --git a/packages/factory/phpunit.xml.dist b/packages/factory/phpunit.xml.dist index 0c23d4743..7ac9cbf12 100644 --- a/packages/factory/phpunit.xml.dist +++ b/packages/factory/phpunit.xml.dist @@ -9,7 +9,9 @@ cacheDirectory=".phpunit.cache" backupStaticProperties="false" failOnWarning="false" - failOnNotice="false"> + failOnPhpunitWarning="false" + failOnNotice="false" + failOnDeprecation="false"> ./tests/Charcoal/Factory diff --git a/packages/image/phpunit.xml.dist b/packages/image/phpunit.xml.dist index 5d32205ed..495930861 100644 --- a/packages/image/phpunit.xml.dist +++ b/packages/image/phpunit.xml.dist @@ -9,7 +9,9 @@ cacheDirectory=".phpunit.cache" backupStaticProperties="false" failOnWarning="false" - failOnNotice="false"> + failOnPhpunitWarning="false" + failOnNotice="false" + failOnDeprecation="false"> ./tests/Charcoal diff --git a/packages/object/phpunit.xml.dist b/packages/object/phpunit.xml.dist index 37fe1b388..4803ef6df 100644 --- a/packages/object/phpunit.xml.dist +++ b/packages/object/phpunit.xml.dist @@ -9,7 +9,9 @@ cacheDirectory=".phpunit.cache" backupStaticProperties="false" failOnWarning="false" - failOnNotice="false"> + failOnPhpunitWarning="false" + failOnNotice="false" + failOnDeprecation="false"> ./tests/Charcoal diff --git a/packages/property/phpunit.xml.dist b/packages/property/phpunit.xml.dist index 5d32205ed..495930861 100644 --- a/packages/property/phpunit.xml.dist +++ b/packages/property/phpunit.xml.dist @@ -9,7 +9,9 @@ cacheDirectory=".phpunit.cache" backupStaticProperties="false" failOnWarning="false" - failOnNotice="false"> + failOnPhpunitWarning="false" + failOnNotice="false" + failOnDeprecation="false"> ./tests/Charcoal diff --git a/packages/queue/phpunit.xml.dist b/packages/queue/phpunit.xml.dist index 02c2d7bd7..7e4db788a 100644 --- a/packages/queue/phpunit.xml.dist +++ b/packages/queue/phpunit.xml.dist @@ -9,7 +9,9 @@ cacheDirectory=".phpunit.cache" backupStaticProperties="false" failOnWarning="false" - failOnNotice="false"> + failOnPhpunitWarning="false" + failOnNotice="false" + failOnDeprecation="false"> ./tests/Charcoal diff --git a/packages/translator/phpunit.xml.dist b/packages/translator/phpunit.xml.dist index 5d32205ed..495930861 100644 --- a/packages/translator/phpunit.xml.dist +++ b/packages/translator/phpunit.xml.dist @@ -9,7 +9,9 @@ cacheDirectory=".phpunit.cache" backupStaticProperties="false" failOnWarning="false" - failOnNotice="false"> + failOnPhpunitWarning="false" + failOnNotice="false" + failOnDeprecation="false"> ./tests/Charcoal diff --git a/packages/ui/phpunit.xml.dist b/packages/ui/phpunit.xml.dist index 5d32205ed..495930861 100644 --- a/packages/ui/phpunit.xml.dist +++ b/packages/ui/phpunit.xml.dist @@ -9,7 +9,9 @@ cacheDirectory=".phpunit.cache" backupStaticProperties="false" failOnWarning="false" - failOnNotice="false"> + failOnPhpunitWarning="false" + failOnNotice="false" + failOnDeprecation="false"> ./tests/Charcoal diff --git a/packages/user/phpunit.xml.dist b/packages/user/phpunit.xml.dist index 5d32205ed..495930861 100644 --- a/packages/user/phpunit.xml.dist +++ b/packages/user/phpunit.xml.dist @@ -9,7 +9,9 @@ cacheDirectory=".phpunit.cache" backupStaticProperties="false" failOnWarning="false" - failOnNotice="false"> + failOnPhpunitWarning="false" + failOnNotice="false" + failOnDeprecation="false"> ./tests/Charcoal diff --git a/packages/view/phpunit.xml.dist b/packages/view/phpunit.xml.dist index 5d32205ed..495930861 100644 --- a/packages/view/phpunit.xml.dist +++ b/packages/view/phpunit.xml.dist @@ -9,7 +9,9 @@ cacheDirectory=".phpunit.cache" backupStaticProperties="false" failOnWarning="false" - failOnNotice="false"> + failOnPhpunitWarning="false" + failOnNotice="false" + failOnDeprecation="false"> ./tests/Charcoal diff --git a/phpunit.xml.dist b/phpunit.xml.dist index af654b1e3..9739cf616 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -9,7 +9,9 @@ cacheDirectory=".phpunit.cache" backupStaticProperties="false" failOnWarning="false" - failOnNotice="false"> + failOnPhpunitWarning="false" + failOnNotice="false" + failOnDeprecation="false"> ./tests/Charcoal From c8c1a8bc2a39b9a525ba3f3dd33acbc8f65bf1e5 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Mon, 1 Jun 2026 15:44:31 -0400 Subject: [PATCH 91/94] refactor(core): remove #[Override] attributes and replace arrow functions with explicit loops for better compatibility --- .../Admin/Property/AbstractPropertyInput.php | 1 - .../Charcoal/Admin/Ui/ObjectContainerTrait.php | 2 +- .../src/Charcoal/Admin/Widget/DocWidget.php | 3 +-- .../Cache/Middleware/CacheMiddleware.php | 18 ++++++++++++++++-- .../Charcoal/Cms/Service/Loader/NewsLoader.php | 1 - .../Source/Database/DatabaseFilter.php | 1 - .../Charcoal/Source/Database/DatabaseOrder.php | 1 - .../src/Charcoal/Property/HtmlProperty.php | 1 - 8 files changed, 18 insertions(+), 10 deletions(-) diff --git a/packages/admin/src/Charcoal/Admin/Property/AbstractPropertyInput.php b/packages/admin/src/Charcoal/Admin/Property/AbstractPropertyInput.php index f046b256f..a44274f81 100644 --- a/packages/admin/src/Charcoal/Admin/Property/AbstractPropertyInput.php +++ b/packages/admin/src/Charcoal/Admin/Property/AbstractPropertyInput.php @@ -33,7 +33,6 @@ abstract class AbstractPropertyInput extends AbstractProperty implements /** * @var string $inputName */ - #[\Override] protected $inputName; /** diff --git a/packages/admin/src/Charcoal/Admin/Ui/ObjectContainerTrait.php b/packages/admin/src/Charcoal/Admin/Ui/ObjectContainerTrait.php index e0b7d28b5..6e1b2e375 100644 --- a/packages/admin/src/Charcoal/Admin/Ui/ObjectContainerTrait.php +++ b/packages/admin/src/Charcoal/Admin/Ui/ObjectContainerTrait.php @@ -415,7 +415,7 @@ protected function getSingularLabelFromObj(ModelInterface $obj) $label = null; } - $label = is_array($label) ? reset($label) : new ReflectionClass($obj)->getShortName(); + $label = is_array($label) ? reset($label) : (new ReflectionClass($obj))->getShortName(); static::$labelCache[$key] = $label; diff --git a/packages/admin/src/Charcoal/Admin/Widget/DocWidget.php b/packages/admin/src/Charcoal/Admin/Widget/DocWidget.php index 34a9f81e5..acb0cdc56 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/DocWidget.php +++ b/packages/admin/src/Charcoal/Admin/Widget/DocWidget.php @@ -54,7 +54,6 @@ class DocWidget extends FormWidget implements * * @var string */ - #[\Override] protected $formPropertyClass = DocFormPropertyWidget::class; /** @@ -136,7 +135,7 @@ public function sidebars() $metadata = $this->obj()->metadata(); $objType = (isset($metadata['labels']['singular_name']) ? $translator->translate($metadata['labels']['singular_name']) - : new ReflectionClass($obj)->getShortName()); + : (new ReflectionClass($this->obj()))->getShortName()); $label = $translator->translate('Back to {{name}} id: {{id}}'); $label = strtr($label, [ diff --git a/packages/cache/src/Charcoal/Cache/Middleware/CacheMiddleware.php b/packages/cache/src/Charcoal/Cache/Middleware/CacheMiddleware.php index 1a89bb03f..a85b6f8e6 100644 --- a/packages/cache/src/Charcoal/Cache/Middleware/CacheMiddleware.php +++ b/packages/cache/src/Charcoal/Cache/Middleware/CacheMiddleware.php @@ -336,7 +336,14 @@ private function isPathIncluded($path): bool if (empty($this->includedPath) && !is_numeric($this->includedPath)) { return false; } - return array_any((array)$this->includedPath, fn($included): int|false => preg_match('@' . $included . '@', $path)); + $found = false; + foreach ((array)$this->includedPath as $included) { + if (preg_match('@' . $included . '@', $path)) { + $found = true; + break; + } + } + return $found; } /** @@ -353,7 +360,14 @@ private function isPathExcluded($path): bool if (empty($this->excludedPath) && !is_numeric($this->excludedPath)) { return false; } - return array_any((array)$this->excludedPath, fn($excluded): int|false => preg_match('@' . $excluded . '@', $path)); + $found = false; + foreach ((array)$this->excludedPath as $excluded) { + if (preg_match('@' . $excluded . '@', $path)) { + $found = true; + break; + } + } + return $found; } /** diff --git a/packages/cms/src/Charcoal/Cms/Service/Loader/NewsLoader.php b/packages/cms/src/Charcoal/Cms/Service/Loader/NewsLoader.php index 57dfc0737..266876b06 100644 --- a/packages/cms/src/Charcoal/Cms/Service/Loader/NewsLoader.php +++ b/packages/cms/src/Charcoal/Cms/Service/Loader/NewsLoader.php @@ -21,7 +21,6 @@ class NewsLoader extends AbstractLoader /** * @var object $objType The object to load. */ - #[\Override] protected $objType; /** diff --git a/packages/core/src/Charcoal/Source/Database/DatabaseFilter.php b/packages/core/src/Charcoal/Source/Database/DatabaseFilter.php index 9e6ad04cf..2cb68216b 100644 --- a/packages/core/src/Charcoal/Source/Database/DatabaseFilter.php +++ b/packages/core/src/Charcoal/Source/Database/DatabaseFilter.php @@ -26,7 +26,6 @@ class DatabaseFilter extends Filter implements * * @var string */ - #[\Override] protected $table = DatabaseSource::DEFAULT_TABLE_ALIAS; /** diff --git a/packages/core/src/Charcoal/Source/Database/DatabaseOrder.php b/packages/core/src/Charcoal/Source/Database/DatabaseOrder.php index e8af9e88f..9b1ce2ee3 100644 --- a/packages/core/src/Charcoal/Source/Database/DatabaseOrder.php +++ b/packages/core/src/Charcoal/Source/Database/DatabaseOrder.php @@ -25,7 +25,6 @@ class DatabaseOrder extends Order implements * * @var string */ - #[\Override] protected $table = DatabaseSource::DEFAULT_TABLE_ALIAS; /** diff --git a/packages/property/src/Charcoal/Property/HtmlProperty.php b/packages/property/src/Charcoal/Property/HtmlProperty.php index ec888fb32..5aa3af3d5 100644 --- a/packages/property/src/Charcoal/Property/HtmlProperty.php +++ b/packages/property/src/Charcoal/Property/HtmlProperty.php @@ -19,7 +19,6 @@ class HtmlProperty extends TextProperty /** * @var boolean */ - #[\Override] protected $long = self::DEFAULT_LONG; /** From 09b8b98ea6bcbcfe8e6bd9d97a59889a362a7990 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Mon, 1 Jun 2026 15:44:37 -0400 Subject: [PATCH 92/94] refactor(rector): add RemoveOverrideAttributeFromPropertiesRector to clean up #[Override] attributes from class properties --- ...eOverrideAttributeFromPropertiesRector.php | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 src/Rector/Property/RemoveOverrideAttributeFromPropertiesRector.php diff --git a/src/Rector/Property/RemoveOverrideAttributeFromPropertiesRector.php b/src/Rector/Property/RemoveOverrideAttributeFromPropertiesRector.php new file mode 100644 index 000000000..675182654 --- /dev/null +++ b/src/Rector/Property/RemoveOverrideAttributeFromPropertiesRector.php @@ -0,0 +1,79 @@ +> + */ + public function getNodeTypes(): array + { + return [Property::class]; + } + + /** + * @param Property $node + */ + public function refactor(Node $node): ?Node + { + if ($node->attrGroups === []) { + return null; + } + + $hasChanged = false; + + foreach ($node->attrGroups as $attrGroupKey => $attrGroup) { + foreach ($attrGroup->attrs as $attrKey => $attribute) { + // Check if the attribute name matches "Override" + if ($this->isName($attribute->name, 'Override')) { + unset($attrGroup->attrs[$attrKey]); + $hasChanged = true; + } + } + + // Clean up the parent group if it contains no more attributes + if ($attrGroup->attrs === []) { + unset($node->attrGroups[$attrGroupKey]); + } else { + // Reindex array keys + $attrGroup->attrs = array_values($attrGroup->attrs); + } + } + + if ($hasChanged) { + $node->attrGroups = array_values($node->attrGroups); + return $node; + } + + return null; + } +} From c3e0d39e5228f3e1dff2b40ec211163886520ab8 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Mon, 1 Jun 2026 15:45:36 -0400 Subject: [PATCH 93/94] refactor(rector): downgrade PHP version compatibility from 8.5 to 8.3 and update associated configuration --- rector.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/rector.php b/rector.php index d6dd8fa27..e4d3417e8 100644 --- a/rector.php +++ b/rector.php @@ -3,6 +3,7 @@ use Rector\Config\RectorConfig; use Rector\Php84\Rector\Class_\DeprecatedAnnotationToDeprecatedAttributeRector; use Rector\PHPUnit\Set\PHPUnitSetList; +use Rector\Set\ValueObject\DowngradeLevelSetList; use Rector\ValueObject\PhpVersion; use Rector\Symfony\Set\SymfonySetList; use Rector\Doctrine\Set\DoctrineSetList; @@ -27,9 +28,9 @@ symfony: true, doctrine: true ) - ->withPhpVersion(PhpVersion::PHP_85) + ->withPhpVersion(PhpVersion::PHP_83) ->withSets([ - \Rector\Set\ValueObject\LevelSetList::UP_TO_PHP_85, + \Rector\Set\ValueObject\LevelSetList::UP_TO_PHP_83, PHPUnitSetList::PHPUNIT_100, PHPUnitSetList::PHPUNIT_110, PHPUnitSetList::PHPUNIT_120 @@ -43,4 +44,4 @@ 'Serializable', ]) ->withPreparedSets(typeDeclarations: true, deadCode: true, codeQuality: true) - ->withPhpSets(php85: true); + ->withPhpSets(php83: true); From 9fef6495d7a7a8970460939c2236f654a15e84cf Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Mon, 1 Jun 2026 15:45:44 -0400 Subject: [PATCH 94/94] refactor(composer): update PSR-4 autoload mappings to include Rector property namespace --- composer.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/composer.json b/composer.json index d01b050d1..3f49bedc3 100644 --- a/composer.json +++ b/composer.json @@ -127,6 +127,9 @@ "packages/ui/tests/Charcoal", "packages/user/tests/Charcoal/", "packages/view/tests/Charcoal" + ], + "Charcoal\\Rector\\Property\\": [ + "src/Rector/Property/" ] } },