diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 4a579c2..8a43bd3 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -21,6 +21,6 @@ body: id: versions attributes: label: Versions - placeholder: "MODX 3.2.4, PHP 8.3, pdoTools 3.1.0" + placeholder: "MODX 3.2.4, PHP 8.3, pdoTools3 1.0.0" validations: required: true diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml index a395959..f13225b 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/phpunit.yml @@ -15,7 +15,7 @@ jobs: strategy: fail-fast: false matrix: - php: ["8.1", "8.2", "8.3", "8.4"] + php: ["8.2", "8.3", "8.4", "8.5"] steps: - uses: actions/checkout@v5 - uses: shivammathur/setup-php@v2 @@ -31,7 +31,7 @@ jobs: strategy: fail-fast: false matrix: - php: ["8.1", "8.2", "8.3", "8.4"] + php: ["8.2", "8.3", "8.4", "8.5"] steps: - uses: actions/checkout@v5 - uses: shivammathur/setup-php@v2 @@ -52,7 +52,7 @@ jobs: strategy: fail-fast: false matrix: - php: ["8.1", "8.2", "8.3", "8.4"] + php: ["8.2", "8.3", "8.4", "8.5"] services: mysql: image: mysql:8.0 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e235572..231c605 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -32,7 +32,7 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: "8.1" + php-version: "8.2" extensions: pdo_mysql, mysqli, gd, zip, json, xml, mbstring, curl, tokenizer coverage: none tools: composer @@ -123,12 +123,12 @@ jobs: - name: Collect transport package run: | mkdir -p _packages - find modx/core/packages -name 'pdotools-*.transport.zip' -exec cp -t _packages {} + + find modx/core/packages -name 'pdotools3-*.transport.zip' -exec cp -t _packages {} + ls -la _packages/ - test -n "$(ls _packages/pdotools-*.transport.zip 2>/dev/null)" + test -n "$(ls _packages/pdotools3-*.transport.zip 2>/dev/null)" python3 - <<'PY' import glob, sys, zipfile - zpath = glob.glob('_packages/pdotools-*.transport.zip')[0] + zpath = glob.glob('_packages/pdotools3-*.transport.zip')[0] # MODX 3 may serialize the FQN with single or double backslashes; # older builds used underscores. Any of these means vehicles are present. needles = ( diff --git a/README.md b/README.md index 3f7464b..0f4210a 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,14 @@ -## pdoTools +## pdoTools3 [![Tests](https://github.com/modx-pro/pdoTools3/actions/workflows/phpunit.yml/badge.svg)](https://github.com/modx-pro/pdoTools3/actions/workflows/phpunit.yml) -[![PHP](https://img.shields.io/badge/php-8.1%20%7C%208.2%20%7C%208.3%20%7C%208.4-777BB4)](https://github.com/modx-pro/pdoTools3/actions/workflows/phpunit.yml) +[![PHP](https://img.shields.io/badge/php-8.2%20%7C%208.3%20%7C%208.4%20%7C%208.5-777BB4)](https://github.com/modx-pro/pdoTools3/actions/workflows/phpunit.yml) [![Coverage](https://img.shields.io/codecov/c/github/modx-pro/pdoTools3)](https://codecov.io/gh/modx-pro/pdoTools3) -pdoTools 3 is a set of everyday [MODX Revolution](https://modx.com/) 3 snippets plus a small library that keeps them fast. Queries are built with xPDO and run through PDO. Elements can live in the database or in files. [MiniShop3](https://github.com/modx-pro/MiniShop3) use it. +pdoTools3 is a set of everyday [MODX Revolution](https://modx.com/) 3 snippets plus a small library that keeps them fast. Queries are built with xPDO and run through PDO. Elements can live in the database or in files. [MiniShop3](https://github.com/modx-pro/MiniShop3) use it. -This branch requires **MODX 3** and **PHP 8.1+**. For MODX 2 use [pdoTools 2.x](https://github.com/modx-pro/pdoTools). +Requires **MODX 3** and **PHP 8.2+**. For MODX 2 use [pdoTools](https://github.com/modx-pro/pdoTools). + +Package signature is `pdotools3` (transport `pdotools3-1.0.0-pl.transport.zip`). Component paths, snippets (`pdoResources`, …), and settings (`pdotools_*`) stay under `pdotools`. Sites on older `pdoTools` 3.x install this package over the same files, then remove the old package record in Package Manager. **Documentation:** [docs.modx.pro/components/pdotools](https://docs.modx.pro/components/pdotools/) diff --git a/_build/build.config.php b/_build/build.config.php index 9037f0b..c6757ee 100644 --- a/_build/build.config.php +++ b/_build/build.config.php @@ -1,9 +1,11 @@ error = $modx->services->get('error'); $builder = new modPackageBuilder($modx); -$builder->createPackage(PKG_NAME_LOWER, PKG_VERSION, PKG_RELEASE); +$builder->createPackage(PKG_SIGNATURE, PKG_VERSION, PKG_RELEASE); $builder->registerNamespace(PKG_NAME_LOWER, false, true, '{core_path}components/' . PKG_NAME_LOWER . '/'); $modx->log(modX::LOG_LEVEL_INFO, 'Created Transport Package and Namespace.'); diff --git a/assets/components/pdotools/connector.php b/assets/components/pdotools/connector.php index 3bc1651..242a254 100644 --- a/assets/components/pdotools/connector.php +++ b/assets/components/pdotools/connector.php @@ -1,8 +1,8 @@ initialize('web'); $modx->services->add('error', new MODX\Revolution\Error\modError($modx)); $modx->error = $modx->services->get('error'); diff --git a/core/components/pdotools/composer.json b/core/components/pdotools/composer.json index 82c3fa4..28dccd4 100644 --- a/core/components/pdotools/composer.json +++ b/core/components/pdotools/composer.json @@ -29,16 +29,16 @@ } }, "require": { - "php": ">=8.1", - "fenom/fenom": "^2.11" + "php": ">=8.2", + "fenom/fenom": "^3.1" }, "require-dev": { - "phpunit/phpunit": "^9.6", + "phpunit/phpunit": "^10.0", "modx/revolution": "3.2.4-pl" }, "config": { "platform": { - "php": "8.1.31" + "php": "8.2.0" }, "audit": { "block-insecure": false, diff --git a/core/components/pdotools/composer.lock b/core/components/pdotools/composer.lock index 96e658f..a02e7b3 100644 --- a/core/components/pdotools/composer.lock +++ b/core/components/pdotools/composer.lock @@ -4,34 +4,33 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "97ed7a43e10bfcd3bf273d1ce017b774", + "content-hash": "6fbcd6896b803d078643101aad5d83ad", "packages": [ { "name": "fenom/fenom", - "version": "2.12.1", + "version": "v3.1.0", "source": { "type": "git", "url": "https://github.com/fenom-template/fenom.git", - "reference": "8fb0a703111189901291bea93b5558e2bcd7df21" + "reference": "30961f4bf0d0374cc4c2d17c0bd2905f9d8b60f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fenom-template/fenom/zipball/8fb0a703111189901291bea93b5558e2bcd7df21", - "reference": "8fb0a703111189901291bea93b5558e2bcd7df21", + "url": "https://api.github.com/repos/fenom-template/fenom/zipball/30961f4bf0d0374cc4c2d17c0bd2905f9d8b60f4", + "reference": "30961f4bf0d0374cc4c2d17c0bd2905f9d8b60f4", "shasum": "" }, "require": { "ext-tokenizer": "*", - "php": ">=5.3.0" + "php": ">=8.2.0" }, "require-dev": { - "phpunit/phpunit": "<6.0", - "satooshi/php-coveralls": "*" + "phpunit/phpunit": "^10.0" }, "type": "library", "autoload": { - "psr-0": { - "Fenom\\": "src/" + "psr-4": { + "Fenom\\": "src/Fenom" }, "classmap": [ "src/Fenom.php" @@ -56,9 +55,9 @@ ], "support": { "issues": "https://github.com/fenom-template/fenom/issues", - "source": "https://github.com/fenom-template/fenom/tree/2.12.1" + "source": "https://github.com/fenom-template/fenom/tree/v3.1.0" }, - "time": "2021-11-11T21:08:13+00:00" + "time": "2026-04-23T09:50:20+00:00" } ], "packages-dev": [ @@ -213,76 +212,6 @@ }, "time": "2026-09-01T18:09:58+00:00" }, - { - "name": "doctrine/instantiator", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", - "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", - "shasum": "" - }, - "require": { - "php": "^8.1" - }, - "require-dev": { - "doctrine/coding-standard": "^11", - "ext-pdo": "*", - "ext-phar": "*", - "phpbench/phpbench": "^1.2", - "phpstan/phpstan": "^1.9.4", - "phpstan/phpstan-phpunit": "^1.3", - "phpunit/phpunit": "^9.5.27", - "vimeo/psalm": "^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/2.0.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:23:10+00:00" - }, { "name": "erusev/parsedown", "version": "1.8.0", @@ -1554,16 +1483,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "9.2.32", + "version": "10.1.16", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5" + "reference": "7e308268858ed6baedc8704a304727d20bc07c77" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/85402a822d1ecf1db1096959413d35e1c37cf1a5", - "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7e308268858ed6baedc8704a304727d20bc07c77", + "reference": "7e308268858ed6baedc8704a304727d20bc07c77", "shasum": "" }, "require": { @@ -1571,18 +1500,18 @@ "ext-libxml": "*", "ext-xmlwriter": "*", "nikic/php-parser": "^4.19.1 || ^5.1.0", - "php": ">=7.3", - "phpunit/php-file-iterator": "^3.0.6", - "phpunit/php-text-template": "^2.0.4", - "sebastian/code-unit-reverse-lookup": "^2.0.3", - "sebastian/complexity": "^2.0.3", - "sebastian/environment": "^5.1.5", - "sebastian/lines-of-code": "^1.0.4", - "sebastian/version": "^3.0.2", + "php": ">=8.1", + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-text-template": "^3.0.1", + "sebastian/code-unit-reverse-lookup": "^3.0.0", + "sebastian/complexity": "^3.2.0", + "sebastian/environment": "^6.1.0", + "sebastian/lines-of-code": "^2.0.2", + "sebastian/version": "^4.0.1", "theseer/tokenizer": "^1.2.3" }, "require-dev": { - "phpunit/phpunit": "^9.6" + "phpunit/phpunit": "^10.1" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -1591,7 +1520,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "9.2.x-dev" + "dev-main": "10.1.x-dev" } }, "autoload": { @@ -1620,7 +1549,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/9.2.32" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.16" }, "funding": [ { @@ -1628,32 +1557,32 @@ "type": "github" } ], - "time": "2024-08-22T04:23:01+00:00" + "time": "2024-08-22T04:31:57+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "3.0.6", + "version": "4.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c" }, "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/a95037b6d9e608ba092da1b23931e537cadc3c3c", + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -1680,7 +1609,8 @@ ], "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/4.1.0" }, "funding": [ { @@ -1688,28 +1618,28 @@ "type": "github" } ], - "time": "2021-12-02T12:48:52+00:00" + "time": "2023-08-31T06:24:48+00:00" }, { "name": "phpunit/php-invoker", - "version": "3.1.1", + "version": "4.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7" }, "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/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { "ext-pcntl": "*", - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "suggest": { "ext-pcntl": "*" @@ -1717,7 +1647,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -1743,7 +1673,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0" }, "funding": [ { @@ -1751,32 +1681,32 @@ "type": "github" } ], - "time": "2020-09-28T05:58:55+00:00" + "time": "2023-02-03T06:56:09+00:00" }, { "name": "phpunit/php-text-template", - "version": "2.0.4", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748" }, "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/0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -1802,7 +1732,8 @@ ], "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/3.0.1" }, "funding": [ { @@ -1810,32 +1741,32 @@ "type": "github" } ], - "time": "2020-10-26T05:33:50+00:00" + "time": "2023-08-31T14:07:24+00:00" }, { "name": "phpunit/php-timer", - "version": "5.0.3", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d" }, "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/e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -1861,7 +1792,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0" }, "funding": [ { @@ -1869,24 +1800,23 @@ "type": "github" } ], - "time": "2020-10-26T13:16:10+00:00" + "time": "2023-02-03T06:57:52+00:00" }, { "name": "phpunit/phpunit", - "version": "9.6.36", + "version": "10.5.64", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "abab27ed286d3e1246fbbfe6b56bfd732d945ec9" + "reference": "0e8c1d19cea35ad97d4887f363d07c78e30fbf06" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/abab27ed286d3e1246fbbfe6b56bfd732d945ec9", - "reference": "abab27ed286d3e1246fbbfe6b56bfd732d945ec9", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/0e8c1d19cea35ad97d4887f363d07c78e30fbf06", + "reference": "0e8c1d19cea35ad97d4887f363d07c78e30fbf06", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.5.0 || ^2", "ext-dom": "*", "ext-filter": "*", "ext-json": "*", @@ -1896,27 +1826,26 @@ "myclabs/deep-copy": "^1.13.4", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", - "php": ">=7.3", - "phpunit/php-code-coverage": "^9.2.32", - "phpunit/php-file-iterator": "^3.0.6", - "phpunit/php-invoker": "^3.1.1", - "phpunit/php-text-template": "^2.0.4", - "phpunit/php-timer": "^5.0.3", - "sebastian/cli-parser": "^1.0.2", - "sebastian/code-unit": "^1.0.8", - "sebastian/comparator": "^4.0.10", - "sebastian/diff": "^4.0.6", - "sebastian/environment": "^5.1.5", - "sebastian/exporter": "^4.0.9", - "sebastian/global-state": "^5.0.8", - "sebastian/object-enumerator": "^4.0.4", - "sebastian/resource-operations": "^3.0.4", - "sebastian/type": "^3.2.1", - "sebastian/version": "^3.0.2" + "php": ">=8.1", + "phpunit/php-code-coverage": "^10.1.16", + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-invoker": "^4.0.0", + "phpunit/php-text-template": "^3.0.1", + "phpunit/php-timer": "^6.0.0", + "sebastian/cli-parser": "^2.0.1", + "sebastian/code-unit": "^2.0.0", + "sebastian/comparator": "^5.0.5", + "sebastian/diff": "^5.1.1", + "sebastian/environment": "^6.1.0", + "sebastian/exporter": "^5.1.4", + "sebastian/global-state": "^6.0.2", + "sebastian/object-enumerator": "^5.0.0", + "sebastian/recursion-context": "^5.0.1", + "sebastian/type": "^4.0.0", + "sebastian/version": "^4.0.1" }, "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" + "ext-soap": "To be able to generate mocks based on WSDL files" }, "bin": [ "phpunit" @@ -1924,7 +1853,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.6-dev" + "dev-main": "10.5-dev" } }, "autoload": { @@ -1956,7 +1885,7 @@ "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.36" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.64" }, "funding": [ { @@ -1964,7 +1893,7 @@ "type": "other" } ], - "time": "2026-08-11T06:25:15+00:00" + "time": "2026-07-06T14:50:35+00:00" }, { "name": "pimple/pimple", @@ -2278,28 +2207,28 @@ }, { "name": "sebastian/cli-parser", - "version": "1.0.2", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b" + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/2b56bea83a09de3ac06bb18b92f068e60cc6f50b", - "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "2.0-dev" } }, "autoload": { @@ -2322,7 +2251,8 @@ "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.2" + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.1" }, "funding": [ { @@ -2330,32 +2260,32 @@ "type": "github" } ], - "time": "2024-03-02T06:27:43+00:00" + "time": "2024-03-02T07:12:49+00:00" }, { "name": "sebastian/code-unit", - "version": "1.0.8", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "2.0-dev" } }, "autoload": { @@ -2378,7 +2308,7 @@ "homepage": "https://github.com/sebastianbergmann/code-unit", "support": { "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" }, "funding": [ { @@ -2386,32 +2316,32 @@ "type": "github" } ], - "time": "2020-10-26T13:08:54+00:00" + "time": "2023-02-03T06:58:43+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", - "version": "2.0.3", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" }, "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/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -2433,7 +2363,7 @@ "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", "support": { "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0" }, "funding": [ { @@ -2441,34 +2371,36 @@ "type": "github" } ], - "time": "2020-09-28T05:30:19+00:00" + "time": "2023-02-03T06:59:15+00:00" }, { "name": "sebastian/comparator", - "version": "4.0.10", + "version": "5.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "e4df00b9b3571187db2831ae9aada2c6efbd715d" + "reference": "55dfef806eb7dfeb6e7a6935601fef866f8ca48d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/e4df00b9b3571187db2831ae9aada2c6efbd715d", - "reference": "e4df00b9b3571187db2831ae9aada2c6efbd715d", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55dfef806eb7dfeb6e7a6935601fef866f8ca48d", + "reference": "55dfef806eb7dfeb6e7a6935601fef866f8ca48d", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/diff": "^4.0", - "sebastian/exporter": "^4.0" + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/diff": "^5.0", + "sebastian/exporter": "^5.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -2507,7 +2439,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.10" + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.5" }, "funding": [ { @@ -2527,33 +2460,33 @@ "type": "tidelift" } ], - "time": "2026-01-24T09:22:56+00:00" + "time": "2026-01-24T09:25:16+00:00" }, { "name": "sebastian/complexity", - "version": "2.0.3", + "version": "3.2.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a" + "reference": "68ff824baeae169ec9f2137158ee529584553799" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a", - "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799", + "reference": "68ff824baeae169ec9f2137158ee529584553799", "shasum": "" }, "require": { "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "3.2-dev" } }, "autoload": { @@ -2576,7 +2509,8 @@ "homepage": "https://github.com/sebastianbergmann/complexity", "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", - "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3" + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0" }, "funding": [ { @@ -2584,33 +2518,33 @@ "type": "github" } ], - "time": "2023-12-22T06:19:30+00:00" + "time": "2023-12-21T08:37:17+00:00" }, { "name": "sebastian/diff", - "version": "4.0.6", + "version": "5.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc" + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc", - "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e", + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3", - "symfony/process": "^4.2 || ^5" + "phpunit/phpunit": "^10.0", + "symfony/process": "^6.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "5.1-dev" } }, "autoload": { @@ -2642,7 +2576,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.6" + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/5.1.1" }, "funding": [ { @@ -2650,27 +2585,27 @@ "type": "github" } ], - "time": "2024-03-02T06:30:58+00:00" + "time": "2024-03-02T07:15:17+00:00" }, { "name": "sebastian/environment", - "version": "5.1.5", + "version": "6.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984", + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "suggest": { "ext-posix": "*" @@ -2678,7 +2613,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.1-dev" + "dev-main": "6.1-dev" } }, "autoload": { @@ -2697,7 +2632,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", @@ -2705,7 +2640,8 @@ ], "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/6.1.0" }, "funding": [ { @@ -2713,34 +2649,34 @@ "type": "github" } ], - "time": "2023-02-03T06:03:51+00:00" + "time": "2024-03-23T08:47:14+00:00" }, { "name": "sebastian/exporter", - "version": "4.0.9", + "version": "5.1.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "4352c1a3df741a7ba9e61af6fed51d1fee41cbf7" + "reference": "0735b90f4da94969541dac1da743446e276defa6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/4352c1a3df741a7ba9e61af6fed51d1fee41cbf7", - "reference": "4352c1a3df741a7ba9e61af6fed51d1fee41cbf7", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/0735b90f4da94969541dac1da743446e276defa6", + "reference": "0735b90f4da94969541dac1da743446e276defa6", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/recursion-context": "^4.0" + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/recursion-context": "^5.0" }, "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "5.1-dev" } }, "autoload": { @@ -2782,7 +2718,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.9" + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.4" }, "funding": [ { @@ -2802,38 +2739,35 @@ "type": "tidelift" } ], - "time": "2026-08-11T04:55:59+00:00" + "time": "2025-09-24T06:09:11+00:00" }, { "name": "sebastian/global-state", - "version": "5.0.8", + "version": "6.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6" + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/b6781316bdcd28260904e7cc18ec983d0d2ef4f6", - "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" }, "require-dev": { "ext-dom": "*", - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-uopz": "*" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -2852,59 +2786,48 @@ } ], "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.8" + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/6.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/global-state", - "type": "tidelift" } ], - "time": "2025-08-10T07:10:35+00:00" + "time": "2024-03-02T07:19:19+00:00" }, { "name": "sebastian/lines-of-code", - "version": "1.0.4", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5" + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5", - "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0", "shasum": "" }, "require": { "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "2.0-dev" } }, "autoload": { @@ -2927,7 +2850,8 @@ "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.4" + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2" }, "funding": [ { @@ -2935,34 +2859,34 @@ "type": "github" } ], - "time": "2023-12-22T06:20:34+00:00" + "time": "2023-12-21T08:38:20+00:00" }, { "name": "sebastian/object-enumerator", - "version": "4.0.4", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" }, "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/202d0e344a580d7f7d04b3fafce6933e59dae906", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -2984,7 +2908,7 @@ "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" + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" }, "funding": [ { @@ -2992,32 +2916,32 @@ "type": "github" } ], - "time": "2020-10-26T13:12:34+00:00" + "time": "2023-02-03T07:08:32+00:00" }, { "name": "sebastian/object-reflector", - "version": "2.0.4", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" }, "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/24ed13d98130f0e7122df55d06c5c4942a577957", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -3039,7 +2963,7 @@ "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" + "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" }, "funding": [ { @@ -3047,32 +2971,32 @@ "type": "github" } ], - "time": "2020-10-26T13:14:26+00:00" + "time": "2023-02-03T07:06:18+00:00" }, { "name": "sebastian/recursion-context", - "version": "4.0.7", + "version": "5.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "c85be6922b7fd365942b986b9a50397d65407611" + "reference": "5d32fe257a9b39cb63146924d6b4e32a22d4502a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/c85be6922b7fd365942b986b9a50397d65407611", - "reference": "c85be6922b7fd365942b986b9a50397d65407611", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5d32fe257a9b39cb63146924d6b4e32a22d4502a", + "reference": "5d32fe257a9b39cb63146924d6b4e32a22d4502a", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^10.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -3102,7 +3026,8 @@ "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.7" + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.2" }, "funding": [ { @@ -3122,86 +3047,32 @@ "type": "tidelift" } ], - "time": "2026-08-11T05:25:24+00:00" - }, - { - "name": "sebastian/resource-operations", - "version": "3.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e", - "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "support": { - "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-03-14T16:00:52+00:00" + "time": "2026-08-11T05:27:39+00:00" }, { "name": "sebastian/type", - "version": "3.2.1", + "version": "4.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.5" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -3224,7 +3095,7 @@ "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" }, "funding": [ { @@ -3232,29 +3103,29 @@ "type": "github" } ], - "time": "2023-02-03T06:13:03+00:00" + "time": "2023-02-03T07:10:45+00:00" }, { "name": "sebastian/version", - "version": "3.0.2", + "version": "4.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c6c1022351a901512170118436c764e473f6de8c" + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", - "reference": "c6c1022351a901512170118436c764e473f6de8c", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -3277,7 +3148,7 @@ "homepage": "https://github.com/sebastianbergmann/version", "support": { "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" }, "funding": [ { @@ -3285,7 +3156,7 @@ "type": "github" } ], - "time": "2020-09-28T06:39:44+00:00" + "time": "2023-02-07T11:34:05+00:00" }, { "name": "simplepie/simplepie", @@ -4621,11 +4492,11 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": ">=8.1" + "php": ">=8.2" }, "platform-dev": {}, "platform-overrides": { - "php": "8.1.31" + "php": "8.2.0" }, "plugin-api-version": "2.9.0" } diff --git a/core/components/pdotools/docs/changelog.txt b/core/components/pdotools/docs/changelog.txt index a81a0fc..6dbf3a0 100644 --- a/core/components/pdotools/docs/changelog.txt +++ b/core/components/pdotools/docs/changelog.txt @@ -3,6 +3,41 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) Versioning: [Semantic Versioning](https://semver.org/spec/v2.0.0.html) +## [1.0.0-pl] - 2026-09-03 + +First release of the **pdoTools3** extra for MODX 3 (separate package signature `pdotools3`). +Sites already on `pdoTools` 3.0–3.1.0 are not upgraded automatically: install pdoTools3 over the same +`core/components/pdotools` paths, then remove the old package record. Snippets, settings (`pdotools_*`), +and PHP namespace stay the same. + +### Added + +- [#397] Fenom 3.1.0 (PHP 8.2+). Snippet-as-modifier fallback via `getModifier()`. +- PHPUnit 10 unit suite; CI on PHP 8.2, 8.3, 8.4, 8.5. + +### Changed + +- Extra signature is `pdotools3`; display name `pdoTools3`. Component paths remain `pdotools`. +- PHP requirement is 8.2+. +- `useFenomCache` drives Fenom native file-cache options only. The old MODX compile-cache path for Fenom templates is removed. +- `strftime()` replaced with `date()` in Fenom `fuzzydate` and pdoArchive (PHP 8.4+). +- String class names (`modResource`, `modTemplateVar`) replaced with FQN `::class`. +- pdoPage connector and transport build bootstrap via `vendor/autoload.php`. +- Chunk/Template Fenom providers implement typed Fenom 3 `ProviderInterface`. + +### Fixed + +- Fenom `getModifier()` no longer calls parent `_loadModifier(null)` (TypeError under Fenom 3 when a compiled template reloads). +- `useFenomCache` writes/loads compile files under `cachePath/file` again (native Fenom include, not MODX cache). +- Chunk/Template providers set Fenom `$time` from a stable `editedon`/`createdon` (or static filemtime) so AUTO_RELOAD does not thrash `{include}`. +- Legacy `&dateFormat` / fuzzydate tokens with `%` are mapped to `date()` via `DateFormat::toDate()`. +- Template provider called nonexistent `getConfig()`; now uses `config()`. + +### Notes + +- `&loadModels` still uses xPDO `addPackage()` under `components//model/`. PSR-4 extras (e.g. MiniShop3) do not need it. +- Chunk and Template Fenom providers share `ElementProvider`. + ## [3.1.0-pl] - 2026-09-02 ### Added diff --git a/core/components/pdotools/elements/snippets/snippet.pdoarchive.php b/core/components/pdotools/elements/snippets/snippet.pdoarchive.php index 6d2e5da..2b9de97 100644 --- a/core/components/pdotools/elements/snippets/snippet.pdoarchive.php +++ b/core/components/pdotools/elements/snippets/snippet.pdoarchive.php @@ -1,6 +1,7 @@ resource->id) ? $modx->resource - : $modx->getObject('modResource', $id); + : $modx->getObject(modResource::class, $id); if (!$resource) { return ''; } diff --git a/core/components/pdotools/lexicon/de/properties.inc.php b/core/components/pdotools/lexicon/de/properties.inc.php index b0efc09..7ce2e56 100644 --- a/core/components/pdotools/lexicon/de/properties.inc.php +++ b/core/components/pdotools/lexicon/de/properties.inc.php @@ -187,4 +187,4 @@ $_lang['pdotools_prop_tplMonth'] = 'Template für den Monat'; $_lang['pdotools_prop_tplDay'] = 'Template für den Tag'; $_lang['pdotools_prop_dateField'] = 'Das Feld der Ressource, aus dem das Dokument-Datum ausgelesen werden soll: createdon, publishedon oder editedon.'; -$_lang['pdotools_prop_dateFormat'] = 'Datumsformat für die Funktion strftime()'; \ No newline at end of file +$_lang['pdotools_prop_dateFormat'] = 'Datumsformat für die Funktion date()'; \ No newline at end of file diff --git a/core/components/pdotools/lexicon/en/properties.inc.php b/core/components/pdotools/lexicon/en/properties.inc.php index bfc66f9..c87c387 100644 --- a/core/components/pdotools/lexicon/en/properties.inc.php +++ b/core/components/pdotools/lexicon/en/properties.inc.php @@ -187,4 +187,4 @@ $_lang['pdotools_prop_tplMonth'] = 'Template for the month'; $_lang['pdotools_prop_tplDay'] = 'Template for the day'; $_lang['pdotools_prop_dateField'] = 'The field of resource for obtaining document date: createdon, publishedon, or editedon.'; -$_lang['pdotools_prop_dateFormat'] = 'The date format for the function strftime()'; +$_lang['pdotools_prop_dateFormat'] = 'The date format for the function date()'; diff --git a/core/components/pdotools/lexicon/ru/properties.inc.php b/core/components/pdotools/lexicon/ru/properties.inc.php index 02ba8e3..4551f08 100644 --- a/core/components/pdotools/lexicon/ru/properties.inc.php +++ b/core/components/pdotools/lexicon/ru/properties.inc.php @@ -185,4 +185,4 @@ $_lang['pdotools_prop_tplMonth'] = 'Шаблон для оформления месяца'; $_lang['pdotools_prop_tplDay'] = 'Шаблон для оформления дня'; $_lang['pdotools_prop_dateField'] = 'Поле ресурса для получения даты документа: createdon, publishedon или editedon.'; -$_lang['pdotools_prop_dateFormat'] = 'Формат даты для функции strftime()'; +$_lang['pdotools_prop_dateFormat'] = 'Формат даты для функции date()'; diff --git a/core/components/pdotools/phpunit.xml.dist b/core/components/pdotools/phpunit.xml.dist index 01c7674..53bdca8 100644 --- a/core/components/pdotools/phpunit.xml.dist +++ b/core/components/pdotools/phpunit.xml.dist @@ -1,11 +1,9 @@ @@ -17,11 +15,11 @@ tests/Integration - - + + src - - + + diff --git a/core/components/pdotools/src/CoreTools.php b/core/components/pdotools/src/CoreTools.php index 52a7b74..9fa4210 100644 --- a/core/components/pdotools/src/CoreTools.php +++ b/core/components/pdotools/src/CoreTools.php @@ -949,7 +949,7 @@ public function prepareRows(array $rows = []) /** @var modTemplateVar $templateVar */ if (!$templateVar = $this->getStore($tv, 'tv')) { - if ($templateVar = $this->modx->getObject('modTemplateVar', ['name' => $tv])) { + if ($templateVar = $this->modx->getObject(modTemplateVar::class, ['name' => $tv])) { $sourceCache = isset($prepareTypes[$templateVar->type]) ? $templateVar->getSourceCache($this->modx->context->get('key')) : null; diff --git a/core/components/pdotools/src/Fetch.php b/core/components/pdotools/src/Fetch.php index e7b6d6a..6cf5964 100644 --- a/core/components/pdotools/src/Fetch.php +++ b/core/components/pdotools/src/Fetch.php @@ -768,9 +768,9 @@ public function additionalConditions($where = []) } // Support of MiniShop3 extra categories $members = []; - $resourceMap = $this->modx->classMap['MODX\\Revolution\\' . 'modResource'] ?? []; - $ms3Category = 'MiniShop3\\Model\\' . 'msCategory'; - $ms3CategoryMember = 'MiniShop3\\Model\\' . 'msCategoryMember'; + $resourceMap = $this->modx->classMap[modResource::class] ?? []; + $ms3Category = 'MiniShop3\\Model\\msCategory'; + $ms3CategoryMember = 'MiniShop3\\Model\\msCategoryMember'; if (empty($this->config['disableMS3']) && in_array($ms3Category, $resourceMap, true)) { if (!empty($parents_in) || !empty($parents_out)) { $q = $this->modx->newQuery($ms3CategoryMember); diff --git a/core/components/pdotools/src/Parsing/Fenom/Fenom.php b/core/components/pdotools/src/Parsing/Fenom/Fenom.php index b9bc895..6260ee7 100644 --- a/core/components/pdotools/src/Parsing/Fenom/Fenom.php +++ b/core/components/pdotools/src/Parsing/Fenom/Fenom.php @@ -10,6 +10,7 @@ use ModxPro\PdoTools\Parsing\Fenom\Providers\Chunk; use ModxPro\PdoTools\Parsing\Fenom\Providers\File; use ModxPro\PdoTools\Parsing\Fenom\Providers\Template; +use ModxPro\PdoTools\Support\DateFormat; use MODX\Revolution\modX; use MODX\Revolution\modUser; use MODX\Revolution\modResource; @@ -105,18 +106,20 @@ public function process($chunk, array $properties = []) /** @var \Fenom\Template $tpl */ $source = is_array($chunk) ? $chunk : []; if (!$tpl = $this->pdoTools->getStore($name, 'fenom')) { - if (!empty($this->pdoTools->config('useFenomCache'))) { - $compileKey = 'pdotools/' . $name; - if (!$cache = $this->pdoTools->getExactCache($compileKey)) { - if ($tpl = $this->_compileChunk($content, $name, $source)) { - $this->pdoTools->setExactCache($compileKey, $tpl->getTemplateCode()); - } - } else { - $cache = preg_replace('#^<\?php#', '', $cache); - $tpl = eval($cache); + $useCache = !empty($this->pdoTools->config('useFenomCache')); + $file = rtrim((string)$this->_compile_dir, '/') . '/' . $this->getCompileName($name); + if ($useCache && is_file($file)) { + $fenom = $this; + $loaded = include $file; + if ($loaded instanceof Render) { + $tpl = $loaded; } - } else { + } + if (!$tpl) { $tpl = $this->_compileChunk($content, $name, $source); + if ($tpl && $useCache) { + @file_put_contents($file, $tpl->getTemplateCode()); + } } if ($tpl) { $this->pdoTools->setStore($name, $tpl, 'fenom'); @@ -147,15 +150,13 @@ public function process($chunk, array $properties = []) * * @param string $dir directory to store compiled templates in * - * @return \Fenom * @throws LogicException */ - public function setCompileDir($dir) + public function setCompileDir(string $dir): static { $dir = str_replace(MODX_CORE_PATH, '', $dir); $path = MODX_CORE_PATH; $tmp = explode('/', trim($dir, '/')); - // FIX: use Flysystem foreach ($tmp as $v) { if (!empty($v)) { $path .= $v . '/'; @@ -447,7 +448,7 @@ protected function _addDefaultModifiers() return wordwrap($string, $width, $break, true); }; - $this->_modifiers['fuzzydate'] = function ($date, $format = '%b %e') use ($modx) { + $this->_modifiers['fuzzydate'] = function ($date, $format = 'M j') use ($modx) { $output = '—'; if (!empty($date)) { @@ -458,12 +459,13 @@ protected function _addDefaultModifiers() $time = !is_numeric($date) ? strtotime($date) : $date; + $format = DateFormat::toDate((string)$format); if ($time >= strtotime('today')) { - $output = $modx->lexicon('today_at', ['time' => strftime('%I:%M %p', $time)]); + $output = $modx->lexicon('today_at', ['time' => date('h:i A', $time)]); } elseif ($time >= strtotime('yesterday')) { - $output = $modx->lexicon('yesterday_at', ['time' => strftime('%I:%M %p', $time)]); + $output = $modx->lexicon('yesterday_at', ['time' => date('h:i A', $time)]); } else { - $output = strftime($format, $time); + $output = date($format, $time); } } @@ -747,25 +749,30 @@ protected function _addDefaultModifiers() /** - * Modifier autoloader + * Resolve a modifier: registered / allowed first, then a MODX snippet of the same name. * - * @param string $name - * @param \Fenom\Template $template - * - * @return callable + * Do not call parent::getModifier(): Fenom::_loadModifier() requires a non-null Template, + * while compiled templates invoke getModifier($name) with null at runtime. */ - protected function _loadModifier($name, $template) + public function getModifier(string $modifier, ?\Fenom\Template $template = null): ?callable { + if (isset($this->_modifiers[$modifier])) { + return $this->_modifiers[$modifier]; + } + if ($this->isAllowedFunction($modifier)) { + return $modifier; + } + $pdo = $this->pdoTools; - return function ($input, $options = null) use ($name, $pdo) { - $pdo->debugParserModifier($input, $name, $options); - $result = $pdo->runSnippet($name, [ + return $this->_modifiers[$modifier] = function ($input, $options = null) use ($modifier, $pdo) { + $pdo->debugParserModifier($input, $modifier, $options); + $result = $pdo->runSnippet($modifier, [ 'input' => $input, 'options' => $options, 'pdoTools' => $pdo, ]); - $pdo->debugParserModifier($input, $name, $options); + $pdo->debugParserModifier($input, $modifier, $options); return $result === false ? $input diff --git a/core/components/pdotools/src/Parsing/Fenom/Providers/Chunk.php b/core/components/pdotools/src/Parsing/Fenom/Providers/Chunk.php index 264e6e0..7c391c9 100644 --- a/core/components/pdotools/src/Parsing/Fenom/Providers/Chunk.php +++ b/core/components/pdotools/src/Parsing/Fenom/Providers/Chunk.php @@ -2,130 +2,22 @@ namespace ModxPro\PdoTools\Parsing\Fenom\Providers; -use PDO; -use Iterator; -use Fenom\ProviderInterface; use MODX\Revolution\modChunk; -use MODX\Revolution\modX; -use ModxPro\PdoTools\CoreTools; -class Chunk implements ProviderInterface +class Chunk extends ElementProvider { - /** @var modX $modx */ - public $modx; - /** @var CoreTools $pdoTools */ - public $pdoTools; - - - public function __construct(modX $modx, CoreTools $pdoTools) - { - $this->modx = $modx; - $this->pdoTools = $pdoTools; - } - - - /** - * @param string $tpl - * - * @return bool - */ - public function templateExists($tpl) - { - $c = is_numeric($tpl) && $tpl > 0 - ? $tpl - : ['name' => $tpl]; - - return (bool)$this->modx->getCount(modChunk::class, $c); - } - - - /** - * @param string $tpl - * @param int $time - * - * @return string - */ - public function getSource($tpl, &$time) - { - $content = ''; - if ($pos = strpos($tpl, '@')) { - $propertySet = substr($tpl, $pos + 1); - $tpl = substr($tpl, 0, $pos); - } - $c = is_numeric($tpl) && $tpl > 0 - ? ['id' => $tpl] - : ['name' => $tpl]; - /** @var modChunk $element */ - if ($element = $this->modx->getObject(modChunk::class, $c)) { - $content = $element->getContent(); - - $properties = []; - if (!empty($propertySet)) { - if ($tmp = $element->getPropertySet($propertySet)) { - $properties = $tmp; - } - } else { - $properties = $element->getProperties(); - } - if (!empty($content) && !empty($properties)) { - $useFenom = $this->pdoTools->config('useFenom'); - $this->pdoTools->config(['useFenom' => false]); - - $content = $this->pdoTools->parseChunk('@INLINE ' . $content, $properties); - $this->pdoTools->config(['useFenom' => $useFenom]); - } - } - - return $content; - } - - - /** - * @param string $tpl - * - * @return int - */ - public function getLastModified($tpl) + protected function elementClass(): string { - $c = is_numeric($tpl) && $tpl > 0 - ? $tpl - : ['name' => $tpl]; - - /** @var modChunk $chunk */ - if (($chunk = $this->modx->getObject(modChunk::class, $c)) && $chunk->isStatic() && $file = $chunk->getSourceFile()) { - return filemtime($file); - } - - return time(); + return modChunk::class; } - - /** - * Verify templates (check mtime) - * - * @param array $templates [template_name => modified, ...] By conversation, you may trust the template's name - * - * @return bool if true - all templates are valid else some templates are invalid - */ - public function verify(array $templates) + protected function nameField(): string { - return true; + return 'name'; } - - /** - * Get all names of template from provider - * @return array - */ - public function getList() + protected function listColumn(): string { - $c = $this->modx->newQuery(modChunk::class); - $c->select('name'); - if ($c->prepare() && $c->stmt->execute()) { - return $c->stmt->fetchAll(PDO::FETCH_COLUMN); - } - - return []; + return 'name'; } - } diff --git a/core/components/pdotools/src/Parsing/Fenom/Providers/ElementProvider.php b/core/components/pdotools/src/Parsing/Fenom/Providers/ElementProvider.php new file mode 100644 index 0000000..3e2d8d5 --- /dev/null +++ b/core/components/pdotools/src/Parsing/Fenom/Providers/ElementProvider.php @@ -0,0 +1,138 @@ +modx = $modx; + $this->pdoTools = $pdoTools; + } + + abstract protected function elementClass(): string; + + abstract protected function nameField(): string; + + abstract protected function listColumn(): string; + + public function templateExists(string $tpl): bool + { + return (bool)$this->modx->getCount($this->elementClass(), $this->lookupCriteria($tpl)); + } + + public function getSource(string $tpl, float &$time): string + { + $content = ''; + $name = $tpl; + $propertySet = ''; + if ($pos = strpos($tpl, '@')) { + $propertySet = substr($tpl, $pos + 1); + $name = substr($tpl, 0, $pos); + } + + $element = $this->modx->getObject($this->elementClass(), $this->objectCriteria($name)); + if ($element) { + $content = (string)$element->getContent(); + $properties = []; + if ($propertySet !== '') { + if ($tmp = $element->getPropertySet($propertySet)) { + $properties = $tmp; + } + } else { + $properties = $element->getProperties(); + } + if ($content !== '' && !empty($properties)) { + $useFenom = $this->pdoTools->config('useFenom'); + $this->pdoTools->config(['useFenom' => false]); + $content = $this->pdoTools->parseChunk('@INLINE ' . $content, $properties); + $this->pdoTools->config(['useFenom' => $useFenom]); + } + } + + // Fenom AUTO_RELOAD compares this to getLastModified(); must be stable. + $time = $this->getLastModified($name); + + return $content; + } + + public function getLastModified(string $tpl): float + { + if ($pos = strpos($tpl, '@')) { + $tpl = substr($tpl, 0, $pos); + } + + $element = $this->modx->getObject($this->elementClass(), $this->objectCriteria($tpl)); + if (!$element) { + return 0.0; + } + + if ($element->isStatic() && ($file = $element->getSourceFile())) { + $mtime = @filemtime($file); + + return $mtime !== false ? (float)$mtime : 0.0; + } + + if (method_exists($element, 'get')) { + $stamp = $element->get('editedon') ?: $element->get('createdon'); + if ($stamp !== null && $stamp !== '' && $stamp !== '0' && $stamp !== 0) { + return is_numeric($stamp) ? (float)$stamp : (float)strtotime((string)$stamp); + } + } + + return 0.0; + } + + public function verify(array $templates): bool + { + return true; + } + + public function getList(): iterable + { + $c = $this->modx->newQuery($this->elementClass()); + $c->select($this->listColumn()); + if ($c->prepare() && $c->stmt->execute()) { + return $c->stmt->fetchAll(PDO::FETCH_COLUMN); + } + + return []; + } + + /** + * @return array|int + */ + protected function lookupCriteria(string $tpl) + { + if (is_numeric($tpl) && (float)$tpl > 0) { + return (int)$tpl; + } + + return [$this->nameField() => $tpl]; + } + + /** + * @return array + */ + protected function objectCriteria(string $tpl): array + { + if (is_numeric($tpl) && (float)$tpl > 0) { + return ['id' => (int)$tpl]; + } + + return [$this->nameField() => $tpl]; + } +} diff --git a/core/components/pdotools/src/Parsing/Fenom/Providers/Template.php b/core/components/pdotools/src/Parsing/Fenom/Providers/Template.php index 38e818c..3c7054b 100644 --- a/core/components/pdotools/src/Parsing/Fenom/Providers/Template.php +++ b/core/components/pdotools/src/Parsing/Fenom/Providers/Template.php @@ -2,132 +2,22 @@ namespace ModxPro\PdoTools\Parsing\Fenom\Providers; -use PDO; -use Iterator; -use Fenom\ProviderInterface; -use MODX\Revolution\modX; -use MODX\Revolution\modChunk; use MODX\Revolution\modTemplate; -use ModxPro\PdoTools\CoreTools; -class Template implements ProviderInterface +class Template extends ElementProvider { - /** @var modX $modx */ - public $modx; - /** @var CoreTools $pdoTools */ - public $pdoTools; - - - public function __construct(modX $modx, CoreTools $pdoTools) + protected function elementClass(): string { - $this->modx = $modx; - $this->pdoTools = $pdoTools; + return modTemplate::class; } - - /** - * @param string $tpl - * - * @return bool - */ - public function templateExists($tpl) + protected function nameField(): string { - $c = is_numeric($tpl) && $tpl > 0 - ? $tpl - : ['templatename' => $tpl]; - - return (bool)$this->modx->getCount(modTemplate::class, $c); + return 'templatename'; } - - /** - * @param string $tpl - * @param int $time - * - * @return string - */ - public function getSource($tpl, &$time) + protected function listColumn(): string { - $content = ''; - if ($pos = strpos($tpl, '@')) { - $propertySet = substr($tpl, $pos + 1); - $tpl = substr($tpl, 0, $pos); - } - $c = is_numeric($tpl) && $tpl > 0 - ? $tpl - : ['templatename' => $tpl]; - /** @var modChunk $chunk */ - if ($element = $this->modx->getObject(modTemplate::class, $c)) { - $content = $element->getContent(); - - $properties = []; - if (!empty($propertySet)) { - if ($tmp = $element->getPropertySet($propertySet)) { - $properties = $tmp; - } - } else { - $properties = $element->getProperties(); - } - if (!empty($content) && !empty($properties)) { - $useFenom = $this->pdoTools->getConfig('useFenom'); - $this->pdoTools->config(['useFenom' => false]); - - $content = $this->pdoTools->parseChunk('@INLINE ' . $content, $properties); - $this->pdoTools->config(['useFenom' => $useFenom]); - } - } - - return $content; + return 'templatename'; } - - - /** - * @param string $tpl - * - * @return int - */ - public function getLastModified($tpl) - { - $c = is_numeric($tpl) && $tpl > 0 - ? $tpl - : ['templatename' => $tpl]; - /** @var modChunk $chunk */ - if ($chunk = $this->modx->getObject(modTemplate::class, $c)) { - if ($chunk->isStatic() && $file = $chunk->getSourceFile()) { - return filemtime($file); - } - } - - return time(); - } - - - /** - * Verify templates (check mtime) - * - * @param array $templates [template_name => modified, ...] By conversation, you may trust the template's name - * - * @return bool if true - all templates are valid else some templates are invalid - */ - public function verify(array $templates) - { - return true; - } - - - /** - * Get all names of template from provider - * @return array|Iterator - */ - public function getList() - { - $c = $this->modx->newQuery(modTemplate::class); - $c->select('templatename'); - if ($c->prepare() && $c->stmt->execute()) { - return $c->stmt->fetchAll(PDO::FETCH_COLUMN); - } - - return []; - } - -} \ No newline at end of file +} diff --git a/core/components/pdotools/src/Parsing/Parser.php b/core/components/pdotools/src/Parsing/Parser.php index d2346a3..3c3abf3 100644 --- a/core/components/pdotools/src/Parsing/Parser.php +++ b/core/components/pdotools/src/Parsing/Parser.php @@ -164,7 +164,7 @@ public function processTag($tag, $processUncacheable = true) if (is_numeric($tmp[0])) { /** @var modResource $resource */ if (!$resource = $this->pdoTools->getStore($tmp[0], 'resource')) { - $resource = $this->modx->getObject('modResource', ['id' => $tmp[0]]); + $resource = $this->modx->getObject(modResource::class, ['id' => $tmp[0]]); $this->pdoTools->setStore($tmp[0], $resource, 'resource'); } $output = ''; diff --git a/core/components/pdotools/src/Support/DateFormat.php b/core/components/pdotools/src/Support/DateFormat.php new file mode 100644 index 0000000..1b2f572 --- /dev/null +++ b/core/components/pdotools/src/Support/DateFormat.php @@ -0,0 +1,35 @@ + 'Y', + '%y' => 'y', + '%m' => 'm', + '%d' => 'd', + '%e' => 'j', + '%B' => 'F', + '%b' => 'M', + '%H' => 'H', + '%I' => 'h', + '%M' => 'i', + '%S' => 's', + '%p' => 'A', + '%P' => 'a', + '%%' => '%', + ]; + + public static function toDate(string $format): string + { + if ($format === '' || !str_contains($format, '%')) { + return $format; + } + + return strtr($format, self::MAP); + } +} diff --git a/core/components/pdotools/tests/Stubs/ModxStub.php b/core/components/pdotools/tests/Stubs/ModxStub.php index 1f59a81..d34937d 100644 --- a/core/components/pdotools/tests/Stubs/ModxStub.php +++ b/core/components/pdotools/tests/Stubs/ModxStub.php @@ -72,6 +72,8 @@ class modX public $elementCache = []; public $queryTime = 0; public $executedQueries = 0; + /** @var object|null */ + public $parser = null; /** @var string[] */ public $logs = []; @@ -151,6 +153,22 @@ public function getObject($class, $criteria = null) return null; } + public function newQuery($class, $criteria = null) + { + return new class { + public $stmt = null; + + public function select($columns): void + { + } + + public function prepare(): bool + { + return false; + } + }; + } + public function getAncestry($class) { return [$class]; diff --git a/core/components/pdotools/tests/Unit/Parsing/FenomCacheTest.php b/core/components/pdotools/tests/Unit/Parsing/FenomCacheTest.php new file mode 100644 index 0000000..e921ee0 --- /dev/null +++ b/core/components/pdotools/tests/Unit/Parsing/FenomCacheTest.php @@ -0,0 +1,64 @@ +fail('Cannot create Fenom compile dir'); + } + + $tools = new CoreTools($this->modx, ['useFenom' => true]); + $tools->config([ + 'useFenomCache' => true, + 'cachePath' => $cachePath, + ]); + + $fenom1 = new Fenom($this->modx, $tools); + $this->assertSame(0, $fenom1->getOptions() & \Fenom::FORCE_COMPILE); + $this->assertSame(0, $fenom1->getOptions() & \Fenom::DISABLE_CACHE); + $this->assertNotSame(0, $fenom1->getOptions() & \Fenom::AUTO_RELOAD); + + $this->assertSame('42', $fenom1->process('{var $x = 42}{$x}')); + + $compileDir = $this->compileDir($fenom1); + $files = glob(rtrim($compileDir, '/') . '/*.php') ?: []; + $this->assertNotEmpty($files, 'useFenomCache must write a compile file'); + + $fenom2 = new Fenom($this->modx, $tools); + $name = md5('{var $x = 42}{$x}'); + $file = rtrim($this->compileDir($fenom2), '/') . '/' . $fenom2->getCompileName($name); + $this->assertFileExists($file); + + $fenom = $fenom2; + $loaded = include $file; + $this->assertInstanceOf(Render::class, $loaded); + $this->assertSame('42', $loaded->fetch([])); + } + + private function compileDir(Fenom $fenom): string + { + $ref = new ReflectionClass(\Fenom::class); + $prop = $ref->getProperty('_compile_dir'); + $prop->setAccessible(true); + + return (string)$prop->getValue($fenom); + } +} diff --git a/core/components/pdotools/tests/Unit/Parsing/FenomModifiersTest.php b/core/components/pdotools/tests/Unit/Parsing/FenomModifiersTest.php index 1f59e3a..0b3218d 100644 --- a/core/components/pdotools/tests/Unit/Parsing/FenomModifiersTest.php +++ b/core/components/pdotools/tests/Unit/Parsing/FenomModifiersTest.php @@ -13,7 +13,7 @@ class FenomModifiersTest extends TestCase * * @return array}> */ - public function modifierProvider(): array + public static function modifierProvider(): array { return [ 'intval' => ['42', '{$v|intval}', ['v' => '42.8']], @@ -39,4 +39,48 @@ public function testDefaultModifier(string $expected, string $code, array $vars) { $this->assertRender($expected, $code, $vars); } + + public function testGetModifierWithNullTemplateResolvesSnippet(): void + { + $tools = new class($this->modx, [ + 'useFenom' => true, + 'useFenomCache' => false, + 'cachePath' => dirname(__DIR__, 3) . '/tmp/cache/pdotools', + ]) extends \ModxPro\PdoTools\CoreTools { + public function runSnippet($name, array $properties = []) + { + return 'OK:' . $properties['input']; + } + }; + + $compile = dirname(__DIR__, 3) . '/tmp/cache/pdotools/file'; + if (!is_dir($compile) && !mkdir($compile, 0777, true) && !is_dir($compile)) { + $this->fail('Cannot create Fenom compile dir'); + } + + $fenom = new \ModxPro\PdoTools\Parsing\Fenom\Fenom($this->modx, $tools); + $cb = $fenom->getModifier('ghostSnippet', null); + $this->assertIsCallable($cb); + $this->assertSame('OK:x', $cb('x')); + } + + public function testFuzzydateUsesDateFormat(): void + { + $old = strtotime('2020-01-15 12:00:00'); + $this->assertRender( + date('M j', $old), + '{$v|fuzzydate}', + ['v' => $old] + ); + } + + public function testFuzzydateAcceptsLegacyStrftimeFormat(): void + { + $old = strtotime('2020-01-15 12:00:00'); + $this->assertRender( + date('Y-m-d', $old), + '{$v|fuzzydate:"%Y-%m-%d"}', + ['v' => $old] + ); + } } diff --git a/core/components/pdotools/tests/Unit/Parsing/ProvidersTest.php b/core/components/pdotools/tests/Unit/Parsing/ProvidersTest.php index ffd7c8a..13ec566 100644 --- a/core/components/pdotools/tests/Unit/Parsing/ProvidersTest.php +++ b/core/components/pdotools/tests/Unit/Parsing/ProvidersTest.php @@ -8,6 +8,8 @@ use ModxPro\PdoTools\Parsing\Fenom\Providers\File; use ModxPro\PdoTools\Parsing\Fenom\Providers\Template; use ModxPro\PdoTools\Tests\TestCase; +use MODX\Revolution\modChunk; +use MODX\Revolution\modTemplate; class ProvidersTest extends TestCase { @@ -34,4 +36,152 @@ public function testTemplateProviderReportsMissingTemplate(): void $provider = new Template($this->modx, $this->pdoTools); $this->assertFalse($provider->templateExists('NoSuchTemplate')); } + + public function testChunkProviderEmptyPathsWhenMissing(): void + { + $provider = new Chunk($this->modx, $this->pdoTools); + $time = -1.0; + + $this->assertSame('', $provider->getSource('Missing', $time)); + $this->assertSame(0.0, $time); + $this->assertSame(0.0, $provider->getLastModified('Missing')); + $this->assertSame(0.0, $provider->getLastModified('42')); + $this->assertTrue($provider->verify(['Missing' => 1.0])); + $this->assertSame([], $provider->getList()); + } + + public function testTemplateProviderEmptyPathsWhenMissing(): void + { + $provider = new Template($this->modx, $this->pdoTools); + $time = -1.0; + + $this->assertSame('', $provider->getSource('MissingTpl', $time)); + $this->assertSame(0.0, $time); + $this->assertSame(0.0, $provider->getLastModified('MissingTpl')); + $this->assertTrue($provider->verify(['MissingTpl' => 1.0])); + $this->assertSame([], $provider->getList()); + } + + public function testChunkProviderStableMtimeFromEditedon(): void + { + $element = $this->elementStub('chunk-body', 1_700_000_000); + $this->modx = $this->modxWithObjects([modChunk::class => $element]); + $this->pdoTools = new \ModxPro\PdoTools\CoreTools($this->modx, [ + 'useFenom' => true, + 'useFenomCache' => false, + 'cachePath' => dirname(__DIR__, 3) . '/tmp/cache/pdotools', + ]); + + $provider = new Chunk($this->modx, $this->pdoTools); + $time = 0.0; + $this->assertSame('chunk-body', $provider->getSource('Home', $time)); + $this->assertSame(1_700_000_000.0, $time); + $this->assertSame(1_700_000_000.0, $provider->getLastModified('Home')); + $this->assertSame('chunk-body', $provider->getSource('Home@set', $time)); + $this->assertSame(1_700_000_000.0, $time); + } + + public function testTemplateProviderStableMtimeFromEditedon(): void + { + $element = $this->elementStub('tpl-body', 1_700_000_111); + $this->modx = $this->modxWithObjects([modTemplate::class => $element]); + $this->pdoTools = new \ModxPro\PdoTools\CoreTools($this->modx, [ + 'useFenom' => true, + 'useFenomCache' => false, + 'cachePath' => dirname(__DIR__, 3) . '/tmp/cache/pdotools', + ]); + + $provider = new Template($this->modx, $this->pdoTools); + $time = 0.0; + $this->assertSame('tpl-body', $provider->getSource('Base', $time)); + $this->assertSame(1_700_000_111.0, $time); + $this->assertSame(1_700_000_111.0, $provider->getLastModified('Base')); + } + + private function elementStub(string $content, int $editedon): object + { + return new class($content, $editedon) { + private $content; + private $editedon; + + public function __construct(string $content, int $editedon) + { + $this->content = $content; + $this->editedon = $editedon; + } + + public function getContent(): string + { + return $this->content; + } + + public function getProperties(): array + { + return []; + } + + public function getPropertySet($name) + { + return null; + } + + public function isStatic(): bool + { + return false; + } + + public function getSourceFile() + { + return false; + } + + public function get($key) + { + return $key === 'editedon' ? $this->editedon : null; + } + }; + } + + /** + * @param array $objects + */ + private function modxWithObjects(array $objects): \MODX\Revolution\modX + { + return new class($objects) extends \MODX\Revolution\modX { + /** @var array */ + private $map; + + public function __construct(array $map) + { + parent::__construct(); + $this->map = $map; + } + + public function getCount($class, $criteria = null) + { + return isset($this->map[$class]) ? 1 : 0; + } + + public function getObject($class, $criteria = null) + { + return $this->map[$class] ?? null; + } + + public function newQuery($class, $criteria = null) + { + return new class { + public $stmt; + + public function select($columns): void + { + } + + public function prepare(): bool + { + return false; + } + }; + } + }; + } } diff --git a/core/components/pdotools/tests/Unit/Parsing/SnippetModifierTest.php b/core/components/pdotools/tests/Unit/Parsing/SnippetModifierTest.php new file mode 100644 index 0000000..c5c346c --- /dev/null +++ b/core/components/pdotools/tests/Unit/Parsing/SnippetModifierTest.php @@ -0,0 +1,47 @@ +modx, [ + 'useFenom' => true, + 'useFenomCache' => false, + 'cachePath' => dirname(__DIR__, 3) . '/tmp/cache/pdotools', + ]) extends CoreTools { + /** @var array|null */ + public $lastSnippetProps = null; + + public $lastSnippetName = ''; + + public function runSnippet($name, array $properties = []) + { + $this->lastSnippetName = (string)$name; + $this->lastSnippetProps = $properties; + + return 'SNIP:' . $properties['input']; + } + }; + + $compile = dirname(__DIR__, 3) . '/tmp/cache/pdotools/file'; + if (!is_dir($compile) && !mkdir($compile, 0777, true) && !is_dir($compile)) { + $this->fail('Cannot create Fenom compile dir'); + } + + $fenom = new Fenom($this->modx, $tools); + $tpl = $fenom->compileCode('{$v|mySnippet}', 'snippet-mod.tpl'); + $out = $tpl->fetch(['v' => 'hello']); + + $this->assertSame('SNIP:hello', $out); + $this->assertSame('mySnippet', $tools->lastSnippetName); + $this->assertSame('hello', $tools->lastSnippetProps['input'] ?? null); + } +} diff --git a/core/components/pdotools/tests/Unit/SourceMapTest.php b/core/components/pdotools/tests/Unit/SourceMapTest.php index 42ddc9d..443ec52 100644 --- a/core/components/pdotools/tests/Unit/SourceMapTest.php +++ b/core/components/pdotools/tests/Unit/SourceMapTest.php @@ -11,7 +11,7 @@ class SourceMapTest extends TestCase /** * @return list */ - public function srcClassProvider(): array + public static function srcClassProvider(): array { return [ ['ModxPro\\PdoTools\\CoreTools', 'src/CoreTools.php'], @@ -22,11 +22,13 @@ public function srcClassProvider(): array ['ModxPro\\PdoTools\\Parsing\\Tag', 'src/Parsing/Tag.php'], ['ModxPro\\PdoTools\\Parsing\\Fenom\\Fenom', 'src/Parsing/Fenom/Fenom.php'], ['ModxPro\\PdoTools\\Parsing\\Fenom\\Providers\\Chunk', 'src/Parsing/Fenom/Providers/Chunk.php'], + ['ModxPro\\PdoTools\\Parsing\\Fenom\\Providers\\ElementProvider', 'src/Parsing/Fenom/Providers/ElementProvider.php'], ['ModxPro\\PdoTools\\Parsing\\Fenom\\Providers\\File', 'src/Parsing/Fenom/Providers/File.php'], ['ModxPro\\PdoTools\\Parsing\\Fenom\\Providers\\Template', 'src/Parsing/Fenom/Providers/Template.php'], ['ModxPro\\PdoTools\\Parsing\\Fenom\\Support\\App', 'src/Parsing/Fenom/Support/App.php'], ['ModxPro\\PdoTools\\Parsing\\Fenom\\Support\\Lexicon', 'src/Parsing/Fenom/Support/Lexicon.php'], ['ModxPro\\PdoTools\\Parsing\\Fenom\\Support\\CacheManager', 'src/Parsing/Fenom/Support/CacheManager.php'], + ['ModxPro\\PdoTools\\Support\\DateFormat', 'src/Support/DateFormat.php'], ]; } @@ -43,7 +45,7 @@ public function testSrcClassIsAutoloadable(string $class, string $relative): voi /** * @return list */ - public function snippetFileProvider(): array + public static function snippetFileProvider(): array { $dir = dirname(__DIR__, 2) . '/elements/snippets'; $out = []; diff --git a/core/components/pdotools/tests/Unit/Support/DateFormatTest.php b/core/components/pdotools/tests/Unit/Support/DateFormatTest.php new file mode 100644 index 0000000..dcbec2c --- /dev/null +++ b/core/components/pdotools/tests/Unit/Support/DateFormatTest.php @@ -0,0 +1,22 @@ +assertSame('Y-m-d', DateFormat::toDate('Y-m-d')); + } + + public function testConvertsCommonStrftimeTokens(): void + { + $this->assertSame('Y-m-d H:i', DateFormat::toDate('%Y-%m-%d %H:%M')); + $this->assertSame('F j', DateFormat::toDate('%B %e')); + } +} diff --git a/core/components/pdotools/tests/Unit/Support/PaginatorPageCompareTest.php b/core/components/pdotools/tests/Unit/Support/PaginatorPageCompareTest.php index 61bb461..d68c03a 100644 --- a/core/components/pdotools/tests/Unit/Support/PaginatorPageCompareTest.php +++ b/core/components/pdotools/tests/Unit/Support/PaginatorPageCompareTest.php @@ -14,7 +14,7 @@ class PaginatorPageCompareTest extends TestCase /** * @return list */ - public function lastPageProvider(): array + public static function lastPageProvider(): array { return [ [2, 2, true], diff --git a/core/components/pdotools/tests/bootstrap.php b/core/components/pdotools/tests/bootstrap.php index b1197ad..5cf881d 100644 --- a/core/components/pdotools/tests/bootstrap.php +++ b/core/components/pdotools/tests/bootstrap.php @@ -10,7 +10,6 @@ require $vendor; -// Vendor Fenom 2.12 still uses implicit nullable parameters. error_reporting(E_ALL & ~E_DEPRECATED); $tmp = __DIR__ . '/tmp';