N°5809 Update Symfony artifacts from 6.4.0 to 6.4.2

symfony/console
symfony/dotenv
symfony/framework-bundle
symfony/http-foundation
symfony/http-kernel
symfony/var-dumper
symfony/web-profiler-bundle
This commit is contained in:
Pierre Goiffon
2024-01-26 09:55:51 +01:00
parent ddce3058be
commit dfb5a4875a
110 changed files with 1115 additions and 929 deletions

View File

@@ -2950,6 +2950,8 @@ return array(
'Twig\\Node\\Expression\\Binary\\FloorDivBinary' => $vendorDir . '/twig/twig/src/Node/Expression/Binary/FloorDivBinary.php',
'Twig\\Node\\Expression\\Binary\\GreaterBinary' => $vendorDir . '/twig/twig/src/Node/Expression/Binary/GreaterBinary.php',
'Twig\\Node\\Expression\\Binary\\GreaterEqualBinary' => $vendorDir . '/twig/twig/src/Node/Expression/Binary/GreaterEqualBinary.php',
'Twig\\Node\\Expression\\Binary\\HasEveryBinary' => $vendorDir . '/twig/twig/src/Node/Expression/Binary/HasEveryBinary.php',
'Twig\\Node\\Expression\\Binary\\HasSomeBinary' => $vendorDir . '/twig/twig/src/Node/Expression/Binary/HasSomeBinary.php',
'Twig\\Node\\Expression\\Binary\\InBinary' => $vendorDir . '/twig/twig/src/Node/Expression/Binary/InBinary.php',
'Twig\\Node\\Expression\\Binary\\LessBinary' => $vendorDir . '/twig/twig/src/Node/Expression/Binary/LessBinary.php',
'Twig\\Node\\Expression\\Binary\\LessEqualBinary' => $vendorDir . '/twig/twig/src/Node/Expression/Binary/LessEqualBinary.php',
@@ -3030,6 +3032,8 @@ return array(
'Twig\\Source' => $vendorDir . '/twig/twig/src/Source.php',
'Twig\\Template' => $vendorDir . '/twig/twig/src/Template.php',
'Twig\\TemplateWrapper' => $vendorDir . '/twig/twig/src/TemplateWrapper.php',
'Twig\\Test\\IntegrationTestCase' => $vendorDir . '/twig/twig/src/Test/IntegrationTestCase.php',
'Twig\\Test\\NodeTestCase' => $vendorDir . '/twig/twig/src/Test/NodeTestCase.php',
'Twig\\Token' => $vendorDir . '/twig/twig/src/Token.php',
'Twig\\TokenParser\\AbstractTokenParser' => $vendorDir . '/twig/twig/src/TokenParser/AbstractTokenParser.php',
'Twig\\TokenParser\\ApplyTokenParser' => $vendorDir . '/twig/twig/src/TokenParser/ApplyTokenParser.php',

View File

@@ -3325,6 +3325,8 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'Twig\\Node\\Expression\\Binary\\FloorDivBinary' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Binary/FloorDivBinary.php',
'Twig\\Node\\Expression\\Binary\\GreaterBinary' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Binary/GreaterBinary.php',
'Twig\\Node\\Expression\\Binary\\GreaterEqualBinary' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Binary/GreaterEqualBinary.php',
'Twig\\Node\\Expression\\Binary\\HasEveryBinary' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Binary/HasEveryBinary.php',
'Twig\\Node\\Expression\\Binary\\HasSomeBinary' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Binary/HasSomeBinary.php',
'Twig\\Node\\Expression\\Binary\\InBinary' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Binary/InBinary.php',
'Twig\\Node\\Expression\\Binary\\LessBinary' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Binary/LessBinary.php',
'Twig\\Node\\Expression\\Binary\\LessEqualBinary' => __DIR__ . '/..' . '/twig/twig/src/Node/Expression/Binary/LessEqualBinary.php',
@@ -3405,6 +3407,8 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'Twig\\Source' => __DIR__ . '/..' . '/twig/twig/src/Source.php',
'Twig\\Template' => __DIR__ . '/..' . '/twig/twig/src/Template.php',
'Twig\\TemplateWrapper' => __DIR__ . '/..' . '/twig/twig/src/TemplateWrapper.php',
'Twig\\Test\\IntegrationTestCase' => __DIR__ . '/..' . '/twig/twig/src/Test/IntegrationTestCase.php',
'Twig\\Test\\NodeTestCase' => __DIR__ . '/..' . '/twig/twig/src/Test/NodeTestCase.php',
'Twig\\Token' => __DIR__ . '/..' . '/twig/twig/src/Token.php',
'Twig\\TokenParser\\AbstractTokenParser' => __DIR__ . '/..' . '/twig/twig/src/TokenParser/AbstractTokenParser.php',
'Twig\\TokenParser\\ApplyTokenParser' => __DIR__ . '/..' . '/twig/twig/src/TokenParser/ApplyTokenParser.php',

View File

@@ -2141,17 +2141,17 @@
},
{
"name": "symfony/cache",
"version": "v6.4.0",
"version_normalized": "6.4.0.0",
"version": "v6.4.2",
"version_normalized": "6.4.2.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/cache.git",
"reference": "ac2d25f97b17eec6e19760b6b9962a4f7c44356a"
"reference": "14a75869bbb41cb35bc5d9d322473928c6f3f978"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/cache/zipball/ac2d25f97b17eec6e19760b6b9962a4f7c44356a",
"reference": "ac2d25f97b17eec6e19760b6b9962a4f7c44356a",
"url": "https://api.github.com/repos/symfony/cache/zipball/14a75869bbb41cb35bc5d9d322473928c6f3f978",
"reference": "14a75869bbb41cb35bc5d9d322473928c6f3f978",
"shasum": ""
},
"require": {
@@ -2185,7 +2185,7 @@
"symfony/messenger": "^5.4|^6.0|^7.0",
"symfony/var-dumper": "^5.4|^6.0|^7.0"
},
"time": "2023-11-24T19:28:07+00:00",
"time": "2023-12-29T15:34:34+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -2220,7 +2220,7 @@
"psr6"
],
"support": {
"source": "https://github.com/symfony/cache/tree/v6.4.0"
"source": "https://github.com/symfony/cache/tree/v6.4.2"
},
"funding": [
{
@@ -2397,17 +2397,17 @@
},
{
"name": "symfony/console",
"version": "v6.4.0",
"version_normalized": "6.4.0.0",
"version": "v6.4.2",
"version_normalized": "6.4.2.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "cd9864b47c367450e14ab32f78fdbf98c44c26b6"
"reference": "0254811a143e6bc6c8deea08b589a7e68a37f625"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/cd9864b47c367450e14ab32f78fdbf98c44c26b6",
"reference": "cd9864b47c367450e14ab32f78fdbf98c44c26b6",
"url": "https://api.github.com/repos/symfony/console/zipball/0254811a143e6bc6c8deea08b589a7e68a37f625",
"reference": "0254811a143e6bc6c8deea08b589a7e68a37f625",
"shasum": ""
},
"require": {
@@ -2440,7 +2440,7 @@
"symfony/stopwatch": "^5.4|^6.0|^7.0",
"symfony/var-dumper": "^5.4|^6.0|^7.0"
},
"time": "2023-11-20T16:41:16+00:00",
"time": "2023-12-10T16:15:48+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -2474,7 +2474,7 @@
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v6.4.0"
"source": "https://github.com/symfony/console/tree/v6.4.2"
},
"funding": [
{
@@ -2639,17 +2639,17 @@
},
{
"name": "symfony/dependency-injection",
"version": "v6.4.0",
"version_normalized": "6.4.0.0",
"version": "v6.4.2",
"version_normalized": "6.4.2.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/dependency-injection.git",
"reference": "5dc8ad5f2bbba7046f5947682bf7d868ce80d4e8"
"reference": "226ea431b1eda6f0d9f5a4b278757171960bb195"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/dependency-injection/zipball/5dc8ad5f2bbba7046f5947682bf7d868ce80d4e8",
"reference": "5dc8ad5f2bbba7046f5947682bf7d868ce80d4e8",
"url": "https://api.github.com/repos/symfony/dependency-injection/zipball/226ea431b1eda6f0d9f5a4b278757171960bb195",
"reference": "226ea431b1eda6f0d9f5a4b278757171960bb195",
"shasum": ""
},
"require": {
@@ -2675,7 +2675,7 @@
"symfony/expression-language": "^5.4|^6.0|^7.0",
"symfony/yaml": "^5.4|^6.0|^7.0"
},
"time": "2023-10-31T08:40:20+00:00",
"time": "2023-12-28T19:16:56+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -2703,7 +2703,7 @@
"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/v6.4.0"
"source": "https://github.com/symfony/dependency-injection/tree/v6.4.2"
},
"funding": [
{
@@ -2793,17 +2793,17 @@
},
{
"name": "symfony/dotenv",
"version": "v6.4.0",
"version_normalized": "6.4.0.0",
"version": "v6.4.2",
"version_normalized": "6.4.2.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/dotenv.git",
"reference": "d0d584a91422ddaa2c94317200d4c4e5b935555f"
"reference": "835f8d2d1022934ac038519de40b88158798c96f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/dotenv/zipball/d0d584a91422ddaa2c94317200d4c4e5b935555f",
"reference": "d0d584a91422ddaa2c94317200d4c4e5b935555f",
"url": "https://api.github.com/repos/symfony/dotenv/zipball/835f8d2d1022934ac038519de40b88158798c96f",
"reference": "835f8d2d1022934ac038519de40b88158798c96f",
"shasum": ""
},
"require": {
@@ -2817,7 +2817,7 @@
"symfony/console": "^5.4|^6.0|^7.0",
"symfony/process": "^5.4|^6.0|^7.0"
},
"time": "2023-10-26T18:19:48+00:00",
"time": "2023-12-28T19:16:56+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -2850,7 +2850,7 @@
"environment"
],
"support": {
"source": "https://github.com/symfony/dotenv/tree/v6.4.0"
"source": "https://github.com/symfony/dotenv/tree/v6.4.2"
},
"funding": [
{
@@ -2948,17 +2948,17 @@
},
{
"name": "symfony/event-dispatcher",
"version": "v6.4.0",
"version_normalized": "6.4.0.0",
"version": "v6.4.2",
"version_normalized": "6.4.2.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
"reference": "d76d2632cfc2206eecb5ad2b26cd5934082941b6"
"reference": "e95216850555cd55e71b857eb9d6c2674124603a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/d76d2632cfc2206eecb5ad2b26cd5934082941b6",
"reference": "d76d2632cfc2206eecb5ad2b26cd5934082941b6",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/e95216850555cd55e71b857eb9d6c2674124603a",
"reference": "e95216850555cd55e71b857eb9d6c2674124603a",
"shasum": ""
},
"require": {
@@ -2983,7 +2983,7 @@
"symfony/service-contracts": "^2.5|^3",
"symfony/stopwatch": "^5.4|^6.0|^7.0"
},
"time": "2023-07-27T06:52:43+00:00",
"time": "2023-12-27T22:16:42+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -3011,7 +3011,7 @@
"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/v6.4.0"
"source": "https://github.com/symfony/event-dispatcher/tree/v6.4.2"
},
"funding": [
{
@@ -3243,17 +3243,17 @@
},
{
"name": "symfony/framework-bundle",
"version": "v6.4.0",
"version_normalized": "6.4.0.0",
"version": "v6.4.2",
"version_normalized": "6.4.2.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/framework-bundle.git",
"reference": "981e016715b4a7f22f58c1d9fdf444311965d25e"
"reference": "c26a221e0462027d1f9d4a802ed63f8ab07a43d0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/framework-bundle/zipball/981e016715b4a7f22f58c1d9fdf444311965d25e",
"reference": "981e016715b4a7f22f58c1d9fdf444311965d25e",
"url": "https://api.github.com/repos/symfony/framework-bundle/zipball/c26a221e0462027d1f9d4a802ed63f8ab07a43d0",
"reference": "c26a221e0462027d1f9d4a802ed63f8ab07a43d0",
"shasum": ""
},
"require": {
@@ -3281,7 +3281,7 @@
"symfony/asset": "<5.4",
"symfony/asset-mapper": "<6.4",
"symfony/clock": "<6.3",
"symfony/console": "<5.4",
"symfony/console": "<5.4|>=7.0",
"symfony/dom-crawler": "<6.4",
"symfony/dotenv": "<5.4",
"symfony/form": "<5.4",
@@ -3346,7 +3346,7 @@
"symfony/yaml": "^5.4|^6.0|^7.0",
"twig/twig": "^2.10|^3.0"
},
"time": "2023-11-25T19:10:27+00:00",
"time": "2023-12-29T15:34:34+00:00",
"type": "symfony-bundle",
"installation-source": "dist",
"autoload": {
@@ -3374,7 +3374,7 @@
"description": "Provides a tight integration between Symfony components and the Symfony full-stack framework",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/framework-bundle/tree/v6.4.0"
"source": "https://github.com/symfony/framework-bundle/tree/v6.4.2"
},
"funding": [
{
@@ -3394,17 +3394,17 @@
},
{
"name": "symfony/http-foundation",
"version": "v6.4.0",
"version_normalized": "6.4.0.0",
"version": "v6.4.2",
"version_normalized": "6.4.2.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
"reference": "44a6d39a9cc11e154547d882d5aac1e014440771"
"reference": "172d807f9ef3fc3fbed8377cc57c20d389269271"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/44a6d39a9cc11e154547d882d5aac1e014440771",
"reference": "44a6d39a9cc11e154547d882d5aac1e014440771",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/172d807f9ef3fc3fbed8377cc57c20d389269271",
"reference": "172d807f9ef3fc3fbed8377cc57c20d389269271",
"shasum": ""
},
"require": {
@@ -3426,7 +3426,7 @@
"symfony/mime": "^5.4|^6.0|^7.0",
"symfony/rate-limiter": "^5.4|^6.0|^7.0"
},
"time": "2023-11-20T16:41:16+00:00",
"time": "2023-12-27T22:16:42+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -3454,7 +3454,7 @@
"description": "Defines an object-oriented layer for the HTTP specification",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/http-foundation/tree/v6.4.0"
"source": "https://github.com/symfony/http-foundation/tree/v6.4.2"
},
"funding": [
{
@@ -3474,17 +3474,17 @@
},
{
"name": "symfony/http-kernel",
"version": "v6.4.0",
"version_normalized": "6.4.0.0",
"version": "v6.4.2",
"version_normalized": "6.4.2.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-kernel.git",
"reference": "16a29c453966f29466ad34444ce97970a336f3c8"
"reference": "13e8387320b5942d0dc408440c888e2d526efef4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/16a29c453966f29466ad34444ce97970a336f3c8",
"reference": "16a29c453966f29466ad34444ce97970a336f3c8",
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/13e8387320b5942d0dc408440c888e2d526efef4",
"reference": "13e8387320b5942d0dc408440c888e2d526efef4",
"shasum": ""
},
"require": {
@@ -3542,7 +3542,7 @@
"symfony/var-exporter": "^6.2|^7.0",
"twig/twig": "^2.13|^3.0.4"
},
"time": "2023-11-29T10:40:15+00:00",
"time": "2023-12-30T15:31:44+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -3570,7 +3570,7 @@
"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/v6.4.0"
"source": "https://github.com/symfony/http-kernel/tree/v6.4.2"
},
"funding": [
{
@@ -4270,17 +4270,17 @@
},
{
"name": "symfony/routing",
"version": "v6.4.0",
"version_normalized": "6.4.0.0",
"version": "v6.4.2",
"version_normalized": "6.4.2.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/routing.git",
"reference": "ae014d60d7c8e80be5c3b644a286e91249a3e8f4"
"reference": "98eab13a07fddc85766f1756129c69f207ffbc21"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/routing/zipball/ae014d60d7c8e80be5c3b644a286e91249a3e8f4",
"reference": "ae014d60d7c8e80be5c3b644a286e91249a3e8f4",
"url": "https://api.github.com/repos/symfony/routing/zipball/98eab13a07fddc85766f1756129c69f207ffbc21",
"reference": "98eab13a07fddc85766f1756129c69f207ffbc21",
"shasum": ""
},
"require": {
@@ -4302,7 +4302,7 @@
"symfony/http-foundation": "^5.4|^6.0|^7.0",
"symfony/yaml": "^5.4|^6.0|^7.0"
},
"time": "2023-11-29T08:04:54+00:00",
"time": "2023-12-29T15:34:34+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -4336,7 +4336,7 @@
"url"
],
"support": {
"source": "https://github.com/symfony/routing/tree/v6.4.0"
"source": "https://github.com/symfony/routing/tree/v6.4.2"
},
"funding": [
{
@@ -4438,35 +4438,31 @@
},
{
"name": "symfony/service-contracts",
"version": "v2.5.2",
"version_normalized": "2.5.2.0",
"version": "v3.4.1",
"version_normalized": "3.4.1.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/service-contracts.git",
"reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c"
"reference": "fe07cbc8d837f60caf7018068e350cc5163681a0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/4b426aac47d6427cc1a1d0f7e2ac724627f5966c",
"reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c",
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/fe07cbc8d837f60caf7018068e350cc5163681a0",
"reference": "fe07cbc8d837f60caf7018068e350cc5163681a0",
"shasum": ""
},
"require": {
"php": ">=7.2.5",
"psr/container": "^1.1",
"symfony/deprecation-contracts": "^2.1|^3"
"php": ">=8.1",
"psr/container": "^1.1|^2.0"
},
"conflict": {
"ext-psr": "<1.1|>=2"
},
"suggest": {
"symfony/service-implementation": ""
},
"time": "2022-05-30T19:17:29+00:00",
"time": "2023-12-26T14:02:43+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "2.5-dev"
"dev-main": "3.4-dev"
},
"thanks": {
"name": "symfony/contracts",
@@ -4477,7 +4473,10 @@
"autoload": {
"psr-4": {
"Symfony\\Contracts\\Service\\": ""
}
},
"exclude-from-classmap": [
"/Test/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -4504,7 +4503,7 @@
"standards"
],
"support": {
"source": "https://github.com/symfony/service-contracts/tree/v2.5.2"
"source": "https://github.com/symfony/service-contracts/tree/v3.4.1"
},
"funding": [
{
@@ -4589,17 +4588,17 @@
},
{
"name": "symfony/string",
"version": "v6.4.0",
"version_normalized": "6.4.0.0",
"version": "v6.4.2",
"version_normalized": "6.4.2.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
"reference": "b45fcf399ea9c3af543a92edf7172ba21174d809"
"reference": "7cb80bc10bfcdf6b5492741c0b9357dac66940bc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/string/zipball/b45fcf399ea9c3af543a92edf7172ba21174d809",
"reference": "b45fcf399ea9c3af543a92edf7172ba21174d809",
"url": "https://api.github.com/repos/symfony/string/zipball/7cb80bc10bfcdf6b5492741c0b9357dac66940bc",
"reference": "7cb80bc10bfcdf6b5492741c0b9357dac66940bc",
"shasum": ""
},
"require": {
@@ -4619,7 +4618,7 @@
"symfony/translation-contracts": "^2.5|^3.0",
"symfony/var-exporter": "^5.4|^6.0|^7.0"
},
"time": "2023-11-28T20:41:49+00:00",
"time": "2023-12-10T16:15:48+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -4658,7 +4657,7 @@
"utf8"
],
"support": {
"source": "https://github.com/symfony/string/tree/v6.4.0"
"source": "https://github.com/symfony/string/tree/v6.4.2"
},
"funding": [
{
@@ -4958,17 +4957,17 @@
},
{
"name": "symfony/var-dumper",
"version": "v6.4.0",
"version_normalized": "6.4.0.0",
"version": "v6.4.2",
"version_normalized": "6.4.2.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-dumper.git",
"reference": "c40f7d17e91d8b407582ed51a2bbf83c52c367f6"
"reference": "68d6573ec98715ddcae5a0a85bee3c1c27a4c33f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/c40f7d17e91d8b407582ed51a2bbf83c52c367f6",
"reference": "c40f7d17e91d8b407582ed51a2bbf83c52c367f6",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/68d6573ec98715ddcae5a0a85bee3c1c27a4c33f",
"reference": "68d6573ec98715ddcae5a0a85bee3c1c27a4c33f",
"shasum": ""
},
"require": {
@@ -4988,7 +4987,7 @@
"symfony/uid": "^5.4|^6.0|^7.0",
"twig/twig": "^2.13|^3.0.4"
},
"time": "2023-11-09T08:28:32+00:00",
"time": "2023-12-28T19:16:56+00:00",
"bin": [
"Resources/bin/var-dump-server"
],
@@ -5026,7 +5025,7 @@
"dump"
],
"support": {
"source": "https://github.com/symfony/var-dumper/tree/v6.4.0"
"source": "https://github.com/symfony/var-dumper/tree/v6.4.2"
},
"funding": [
{
@@ -5046,17 +5045,17 @@
},
{
"name": "symfony/var-exporter",
"version": "v6.4.0",
"version_normalized": "6.4.0.0",
"version": "v6.4.2",
"version_normalized": "6.4.2.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-exporter.git",
"reference": "d6081c0316f0f5921f2010d1766925005a82ea3b"
"reference": "5fe9a0021b8d35e67d914716ec8de50716a68e7e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/var-exporter/zipball/d6081c0316f0f5921f2010d1766925005a82ea3b",
"reference": "d6081c0316f0f5921f2010d1766925005a82ea3b",
"url": "https://api.github.com/repos/symfony/var-exporter/zipball/5fe9a0021b8d35e67d914716ec8de50716a68e7e",
"reference": "5fe9a0021b8d35e67d914716ec8de50716a68e7e",
"shasum": ""
},
"require": {
@@ -5066,7 +5065,7 @@
"require-dev": {
"symfony/var-dumper": "^5.4|^6.0|^7.0"
},
"time": "2023-11-28T20:41:49+00:00",
"time": "2023-12-27T08:18:35+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -5104,7 +5103,7 @@
"serialize"
],
"support": {
"source": "https://github.com/symfony/var-exporter/tree/v6.4.0"
"source": "https://github.com/symfony/var-exporter/tree/v6.4.2"
},
"funding": [
{
@@ -5124,17 +5123,17 @@
},
{
"name": "symfony/web-profiler-bundle",
"version": "v6.4.0",
"version_normalized": "6.4.0.0",
"version": "v6.4.2",
"version_normalized": "6.4.2.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/web-profiler-bundle.git",
"reference": "14752d3fb77c3c69b6cee7c03c06e2d6494a196b"
"reference": "38462d16856740ec0d1ba2cb902eebf09100dde2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/14752d3fb77c3c69b6cee7c03c06e2d6494a196b",
"reference": "14752d3fb77c3c69b6cee7c03c06e2d6494a196b",
"url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/38462d16856740ec0d1ba2cb902eebf09100dde2",
"reference": "38462d16856740ec0d1ba2cb902eebf09100dde2",
"shasum": ""
},
"require": {
@@ -5158,7 +5157,7 @@
"symfony/css-selector": "^5.4|^6.0|^7.0",
"symfony/stopwatch": "^5.4|^6.0|^7.0"
},
"time": "2023-11-07T14:57:07+00:00",
"time": "2023-12-27T08:18:35+00:00",
"type": "symfony-bundle",
"installation-source": "dist",
"autoload": {
@@ -5189,7 +5188,7 @@
"dev"
],
"support": {
"source": "https://github.com/symfony/web-profiler-bundle/tree/v6.4.0"
"source": "https://github.com/symfony/web-profiler-bundle/tree/v6.4.2"
},
"funding": [
{
@@ -5347,35 +5346,31 @@
},
{
"name": "twig/twig",
"version": "v3.4.3",
"version_normalized": "3.4.3.0",
"version": "v3.8.0",
"version_normalized": "3.8.0.0",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "c38fd6b0b7f370c198db91ffd02e23b517426b58"
"reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/c38fd6b0b7f370c198db91ffd02e23b517426b58",
"reference": "c38fd6b0b7f370c198db91ffd02e23b517426b58",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/9d15f0ac07f44dc4217883ec6ae02fd555c6f71d",
"reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d",
"shasum": ""
},
"require": {
"php": ">=7.2.5",
"symfony/polyfill-ctype": "^1.8",
"symfony/polyfill-mbstring": "^1.3"
"symfony/polyfill-mbstring": "^1.3",
"symfony/polyfill-php80": "^1.22"
},
"require-dev": {
"psr/container": "^1.0",
"symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0"
"psr/container": "^1.0|^2.0",
"symfony/phpunit-bridge": "^5.4.9|^6.3|^7.0"
},
"time": "2022-09-28T08:42:51+00:00",
"time": "2023-11-21T18:54:41+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.4-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
@@ -5410,7 +5405,7 @@
],
"support": {
"issues": "https://github.com/twigphp/Twig/issues",
"source": "https://github.com/twigphp/Twig/tree/v3.4.3"
"source": "https://github.com/twigphp/Twig/tree/v3.8.0"
},
"funding": [
{

View File

@@ -3,7 +3,7 @@
'name' => 'combodo/itop',
'pretty_version' => 'dev-develop',
'version' => 'dev-develop',
'reference' => '195d4137172d9169005b6b7f0569866480e6260f',
'reference' => '30fabba7375459537e74897dc7dcdad61f73d10e',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@@ -22,7 +22,7 @@
'combodo/itop' => array(
'pretty_version' => 'dev-develop',
'version' => 'dev-develop',
'reference' => '195d4137172d9169005b6b7f0569866480e6260f',
'reference' => '30fabba7375459537e74897dc7dcdad61f73d10e',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@@ -360,9 +360,9 @@
'dev_requirement' => false,
),
'symfony/cache' => array(
'pretty_version' => 'v6.4.0',
'version' => '6.4.0.0',
'reference' => 'ac2d25f97b17eec6e19760b6b9962a4f7c44356a',
'pretty_version' => 'v6.4.2',
'version' => '6.4.2.0',
'reference' => '14a75869bbb41cb35bc5d9d322473928c6f3f978',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/cache',
'aliases' => array(),
@@ -393,9 +393,9 @@
'dev_requirement' => false,
),
'symfony/console' => array(
'pretty_version' => 'v6.4.0',
'version' => '6.4.0.0',
'reference' => 'cd9864b47c367450e14ab32f78fdbf98c44c26b6',
'pretty_version' => 'v6.4.2',
'version' => '6.4.2.0',
'reference' => '0254811a143e6bc6c8deea08b589a7e68a37f625',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/console',
'aliases' => array(),
@@ -420,9 +420,9 @@
'dev_requirement' => true,
),
'symfony/dependency-injection' => array(
'pretty_version' => 'v6.4.0',
'version' => '6.4.0.0',
'reference' => '5dc8ad5f2bbba7046f5947682bf7d868ce80d4e8',
'pretty_version' => 'v6.4.2',
'version' => '6.4.2.0',
'reference' => '226ea431b1eda6f0d9f5a4b278757171960bb195',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/dependency-injection',
'aliases' => array(),
@@ -438,9 +438,9 @@
'dev_requirement' => false,
),
'symfony/dotenv' => array(
'pretty_version' => 'v6.4.0',
'version' => '6.4.0.0',
'reference' => 'd0d584a91422ddaa2c94317200d4c4e5b935555f',
'pretty_version' => 'v6.4.2',
'version' => '6.4.2.0',
'reference' => '835f8d2d1022934ac038519de40b88158798c96f',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/dotenv',
'aliases' => array(),
@@ -456,9 +456,9 @@
'dev_requirement' => false,
),
'symfony/event-dispatcher' => array(
'pretty_version' => 'v6.4.0',
'version' => '6.4.0.0',
'reference' => 'd76d2632cfc2206eecb5ad2b26cd5934082941b6',
'pretty_version' => 'v6.4.2',
'version' => '6.4.2.0',
'reference' => 'e95216850555cd55e71b857eb9d6c2674124603a',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/event-dispatcher',
'aliases' => array(),
@@ -498,27 +498,27 @@
'dev_requirement' => false,
),
'symfony/framework-bundle' => array(
'pretty_version' => 'v6.4.0',
'version' => '6.4.0.0',
'reference' => '981e016715b4a7f22f58c1d9fdf444311965d25e',
'pretty_version' => 'v6.4.2',
'version' => '6.4.2.0',
'reference' => 'c26a221e0462027d1f9d4a802ed63f8ab07a43d0',
'type' => 'symfony-bundle',
'install_path' => __DIR__ . '/../symfony/framework-bundle',
'aliases' => array(),
'dev_requirement' => false,
),
'symfony/http-foundation' => array(
'pretty_version' => 'v6.4.0',
'version' => '6.4.0.0',
'reference' => '44a6d39a9cc11e154547d882d5aac1e014440771',
'pretty_version' => 'v6.4.2',
'version' => '6.4.2.0',
'reference' => '172d807f9ef3fc3fbed8377cc57c20d389269271',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/http-foundation',
'aliases' => array(),
'dev_requirement' => false,
),
'symfony/http-kernel' => array(
'pretty_version' => 'v6.4.0',
'version' => '6.4.0.0',
'reference' => '16a29c453966f29466ad34444ce97970a336f3c8',
'pretty_version' => 'v6.4.2',
'version' => '6.4.2.0',
'reference' => '13e8387320b5942d0dc408440c888e2d526efef4',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/http-kernel',
'aliases' => array(),
@@ -597,9 +597,9 @@
'dev_requirement' => false,
),
'symfony/routing' => array(
'pretty_version' => 'v6.4.0',
'version' => '6.4.0.0',
'reference' => 'ae014d60d7c8e80be5c3b644a286e91249a3e8f4',
'pretty_version' => 'v6.4.2',
'version' => '6.4.2.0',
'reference' => '98eab13a07fddc85766f1756129c69f207ffbc21',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/routing',
'aliases' => array(),
@@ -615,9 +615,9 @@
'dev_requirement' => false,
),
'symfony/service-contracts' => array(
'pretty_version' => 'v2.5.2',
'version' => '2.5.2.0',
'reference' => '4b426aac47d6427cc1a1d0f7e2ac724627f5966c',
'pretty_version' => 'v3.4.1',
'version' => '3.4.1.0',
'reference' => 'fe07cbc8d837f60caf7018068e350cc5163681a0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/service-contracts',
'aliases' => array(),
@@ -639,9 +639,9 @@
'dev_requirement' => true,
),
'symfony/string' => array(
'pretty_version' => 'v6.4.0',
'version' => '6.4.0.0',
'reference' => 'b45fcf399ea9c3af543a92edf7172ba21174d809',
'pretty_version' => 'v6.4.2',
'version' => '6.4.2.0',
'reference' => '7cb80bc10bfcdf6b5492741c0b9357dac66940bc',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/string',
'aliases' => array(),
@@ -675,27 +675,27 @@
'dev_requirement' => false,
),
'symfony/var-dumper' => array(
'pretty_version' => 'v6.4.0',
'version' => '6.4.0.0',
'reference' => 'c40f7d17e91d8b407582ed51a2bbf83c52c367f6',
'pretty_version' => 'v6.4.2',
'version' => '6.4.2.0',
'reference' => '68d6573ec98715ddcae5a0a85bee3c1c27a4c33f',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/var-dumper',
'aliases' => array(),
'dev_requirement' => false,
),
'symfony/var-exporter' => array(
'pretty_version' => 'v6.4.0',
'version' => '6.4.0.0',
'reference' => 'd6081c0316f0f5921f2010d1766925005a82ea3b',
'pretty_version' => 'v6.4.2',
'version' => '6.4.2.0',
'reference' => '5fe9a0021b8d35e67d914716ec8de50716a68e7e',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/var-exporter',
'aliases' => array(),
'dev_requirement' => false,
),
'symfony/web-profiler-bundle' => array(
'pretty_version' => 'v6.4.0',
'version' => '6.4.0.0',
'reference' => '14752d3fb77c3c69b6cee7c03c06e2d6494a196b',
'pretty_version' => 'v6.4.2',
'version' => '6.4.2.0',
'reference' => '38462d16856740ec0d1ba2cb902eebf09100dde2',
'type' => 'symfony-bundle',
'install_path' => __DIR__ . '/../symfony/web-profiler-bundle',
'aliases' => array(),
@@ -726,9 +726,9 @@
'dev_requirement' => false,
),
'twig/twig' => array(
'pretty_version' => 'v3.4.3',
'version' => '3.4.3.0',
'reference' => 'c38fd6b0b7f370c198db91ffd02e23b517426b58',
'pretty_version' => 'v3.8.0',
'version' => '3.8.0.0',
'reference' => '9d15f0ac07f44dc4217883ec6ae02fd555c6f71d',
'type' => 'library',
'install_path' => __DIR__ . '/../twig/twig',
'aliases' => array(),

View File

@@ -35,7 +35,7 @@ class CouchbaseCollectionAdapter extends AbstractAdapter
public function __construct(Collection $connection, string $namespace = '', int $defaultLifetime = 0, MarshallerInterface $marshaller = null)
{
if (!static::isSupported()) {
throw new CacheException('Couchbase >= 3.0.0 < 4.0.0 is required.');
throw new CacheException('Couchbase >= 3.0.5 < 4.0.0 is required.');
}
$this->maxIdLength = static::MAX_KEY_LENGTH;
@@ -54,7 +54,7 @@ class CouchbaseCollectionAdapter extends AbstractAdapter
}
if (!static::isSupported()) {
throw new CacheException('Couchbase >= 3.0.0 < 4.0.0 is required.');
throw new CacheException('Couchbase >= 3.0.5 < 4.0.0 is required.');
}
set_error_handler(static fn ($type, $msg, $file, $line) => throw new \ErrorException($msg, 0, $type, $file, $line));
@@ -183,7 +183,7 @@ class CouchbaseCollectionAdapter extends AbstractAdapter
}
$upsertOptions = new UpsertOptions();
$upsertOptions->expiry($lifetime);
$upsertOptions->expiry(\DateTimeImmutable::createFromFormat('U', time() + $lifetime));
$ko = [];
foreach ($values as $key => $value) {

View File

@@ -97,18 +97,17 @@ class StreamOutput extends Output
return false;
}
if ('Hyper' === getenv('TERM_PROGRAM')) {
if (\DIRECTORY_SEPARATOR === '\\'
&& \function_exists('sapi_windows_vt100_support')
&& @sapi_windows_vt100_support($this->stream)
) {
return true;
}
if (\DIRECTORY_SEPARATOR === '\\') {
return (\function_exists('sapi_windows_vt100_support')
&& @sapi_windows_vt100_support($this->stream))
|| false !== getenv('ANSICON')
|| 'ON' === getenv('ConEmuANSI')
|| 'xterm' === getenv('TERM');
}
return stream_isatty($this->stream);
return 'Hyper' === getenv('TERM_PROGRAM')
|| false !== getenv('ANSICON')
|| 'ON' === getenv('ConEmuANSI')
|| str_starts_with((string) getenv('TERM'), 'xterm')
|| stream_isatty($this->stream);
}
}

View File

@@ -7,7 +7,14 @@ interfaces.
Sponsor
-------
Help Symfony by [sponsoring][1] its development!
The Console component for Symfony 6.4 is [backed][1] by [Les-Tilleuls.coop][2].
Les-Tilleuls.coop is a team of 70+ Symfony experts who can help you design, develop and
fix your projects. They provide a wide range of professional services including development,
consulting, coaching, training and audits. They also are highly skilled in JS, Go and DevOps.
They are a worker cooperative!
Help Symfony by [sponsoring][3] its development!
Resources
---------
@@ -24,4 +31,6 @@ Credits
`Resources/bin/hiddeninput.exe` is a third party binary provided within this
component. Find sources and license at https://github.com/Seldaek/hidden-input.
[1]: https://symfony.com/sponsor
[1]: https://symfony.com/backers
[2]: https://les-tilleuls.coop
[3]: https://symfony.com/sponsor

View File

@@ -36,10 +36,12 @@ final class Target
return lcfirst(str_replace(' ', '', ucwords(preg_replace('/[^a-zA-Z0-9\x7f-\xff]++/', ' ', $this->name))));
}
public static function parseName(\ReflectionParameter $parameter, self &$attribute = null): string
public static function parseName(\ReflectionParameter $parameter, self &$attribute = null, string &$parsedName = null): string
{
$attribute = null;
if (!$target = $parameter->getAttributes(self::class)[0] ?? null) {
$parsedName = (new self($parameter->name))->getParsedName();
return $parameter->name;
}
@@ -57,6 +59,6 @@ final class Target
throw new InvalidArgumentException(sprintf('Invalid #[Target] name "%s" on parameter "$%s" of "%s()": the first character must be a letter.', $name, $parameter->name, $function));
}
return $parsedName;
return preg_match('/^[a-zA-Z0-9_\x7f-\xff]++$/', $name) ? $name : $parsedName;
}
}

View File

@@ -454,20 +454,30 @@ class AutowirePass extends AbstractRecursivePass
$name = $target = (array_filter($reference->getAttributes(), static fn ($a) => $a instanceof Target)[0] ?? null)?->name;
if (null !== $name ??= $reference->getName()) {
if ($this->container->has($alias = $type.' $'.$name) && !$this->container->findDefinition($alias)->isAbstract()) {
return new TypedReference($alias, $type, $reference->getInvalidBehavior());
}
if (null !== ($alias = $this->getCombinedAlias($type, $name)) && !$this->container->findDefinition($alias)->isAbstract()) {
return new TypedReference($alias, $type, $reference->getInvalidBehavior());
}
$parsedName = (new Target($name))->getParsedName();
if ($this->container->has($alias = $type.' $'.$parsedName) && !$this->container->findDefinition($alias)->isAbstract()) {
return new TypedReference($alias, $type, $reference->getInvalidBehavior());
}
if (null !== ($alias = $this->getCombinedAlias($type, $parsedName) ?? null) && !$this->container->findDefinition($alias)->isAbstract()) {
if (null !== ($alias = $this->getCombinedAlias($type, $parsedName)) && !$this->container->findDefinition($alias)->isAbstract()) {
return new TypedReference($alias, $type, $reference->getInvalidBehavior());
}
if ($this->container->has($name) && !$this->container->findDefinition($name)->isAbstract()) {
if (($this->container->has($n = $name) && !$this->container->findDefinition($n)->isAbstract())
|| ($this->container->has($n = $parsedName) && !$this->container->findDefinition($n)->isAbstract())
) {
foreach ($this->container->getAliases() as $id => $alias) {
if ($name === (string) $alias && str_starts_with($id, $type.' $')) {
return new TypedReference($name, $type, $reference->getInvalidBehavior());
if ($n === (string) $alias && str_starts_with($id, $type.' $')) {
return new TypedReference($n, $type, $reference->getInvalidBehavior());
}
}
}
@@ -481,7 +491,7 @@ class AutowirePass extends AbstractRecursivePass
return new TypedReference($type, $type, $reference->getInvalidBehavior());
}
if (null !== ($alias = $this->getCombinedAlias($type) ?? null) && !$this->container->findDefinition($alias)->isAbstract()) {
if (null !== ($alias = $this->getCombinedAlias($type)) && !$this->container->findDefinition($alias)->isAbstract()) {
return new TypedReference($alias, $type, $reference->getInvalidBehavior());
}

View File

@@ -190,16 +190,19 @@ class ResolveBindingsPass extends AbstractRecursivePass
$typeHint = ltrim(ProxyHelper::exportType($parameter) ?? '', '?');
$name = Target::parseName($parameter);
$name = Target::parseName($parameter, parsedName: $parsedName);
if ($typeHint && \array_key_exists($k = preg_replace('/(^|[(|&])\\\\/', '\1', $typeHint).' $'.$name, $bindings)) {
if ($typeHint && (
\array_key_exists($k = preg_replace('/(^|[(|&])\\\\/', '\1', $typeHint).' $'.$name, $bindings)
|| \array_key_exists($k = preg_replace('/(^|[(|&])\\\\/', '\1', $typeHint).' $'.$parsedName, $bindings)
)) {
$arguments[$key] = $this->getBindingValue($bindings[$k]);
continue;
}
if (\array_key_exists('$'.$name, $bindings)) {
$arguments[$key] = $this->getBindingValue($bindings['$'.$name]);
if (\array_key_exists($k = '$'.$name, $bindings) || \array_key_exists($k = '$'.$parsedName, $bindings)) {
$arguments[$key] = $this->getBindingValue($bindings[$k]);
continue;
}
@@ -210,7 +213,7 @@ class ResolveBindingsPass extends AbstractRecursivePass
continue;
}
if (isset($bindingNames[$name]) || isset($bindingNames[$parameter->name])) {
if (isset($bindingNames[$name]) || isset($bindingNames[$parsedName]) || isset($bindingNames[$parameter->name])) {
$bindingKey = array_search($binding, $bindings, true);
$argumentType = substr($bindingKey, 0, strpos($bindingKey, ' '));
$this->errorMessages[] = sprintf('Did you forget to add the type "%s" to argument "$%s" of method "%s::%s()"?', $argumentType, $parameter->name, $reflectionMethod->class, $reflectionMethod->name);

View File

@@ -258,6 +258,7 @@ class PhpDumper extends Dumper
<?php
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
/*{$this->docStar}

View File

@@ -23,7 +23,6 @@ interface EnvVarProcessorInterface
/**
* Returns the value of the given variable as managed by the current instance.
*
* @param string $prefix The namespace of the variable
* @param string $prefix The namespace of the variable; when the empty string is passed, null values should be kept as is
* @param string $name The name of the variable within the namespace
* @param \Closure(string): mixed $getEnv A closure that allows fetching more env vars

View File

@@ -151,7 +151,13 @@ EOT
private function getAvailableVars(): array
{
$vars = explode(',', $_SERVER['SYMFONY_DOTENV_VARS'] ?? '');
$dotenvVars = $_SERVER['SYMFONY_DOTENV_VARS'] ?? '';
if ('' === $dotenvVars) {
return [];
}
$vars = explode(',', $dotenvVars);
sort($vars);
return $vars;

View File

@@ -25,7 +25,7 @@ use Symfony\Component\Process\Process;
*/
final class Dotenv
{
public const VARNAME_REGEX = '(?i:[A-Z][A-Z0-9_]*+)';
public const VARNAME_REGEX = '(?i:_?[A-Z][A-Z0-9_]*+)';
public const STATE_VARNAME = 0;
public const STATE_VALUE = 1;
@@ -341,8 +341,8 @@ final class Dotenv
++$this->cursor;
$value = str_replace(['\\"', '\r', '\n'], ['"', "\r", "\n"], $value);
$resolvedValue = $value;
$resolvedValue = $this->resolveVariables($resolvedValue, $loadedVars);
$resolvedValue = $this->resolveCommands($resolvedValue, $loadedVars);
$resolvedValue = $this->resolveVariables($resolvedValue, $loadedVars);
$resolvedValue = str_replace('\\\\', '\\', $resolvedValue);
$v .= $resolvedValue;
} else {
@@ -364,8 +364,8 @@ final class Dotenv
}
$value = rtrim($value);
$resolvedValue = $value;
$resolvedValue = $this->resolveVariables($resolvedValue, $loadedVars);
$resolvedValue = $this->resolveCommands($resolvedValue, $loadedVars);
$resolvedValue = $this->resolveVariables($resolvedValue, $loadedVars);
$resolvedValue = str_replace('\\\\', '\\', $resolvedValue);
if ($resolvedValue === $value && preg_match('/\s+/', $value)) {

View File

@@ -29,7 +29,7 @@ class GenericEvent extends Event implements \ArrayAccess, \IteratorAggregate
protected $arguments;
/**
* Encapsulate an event with $subject and $args.
* Encapsulate an event with $subject and $arguments.
*
* @param mixed $subject The subject of the event, usually an object or a callable
* @param array $arguments Arguments to store in the event

View File

@@ -166,7 +166,7 @@ class Application extends BaseApplication
public function getLongVersion(): string
{
return parent::getLongVersion().sprintf(' (env: <comment>%s</>, debug: <comment>%s</>) <bg=#0057B7;fg=#FFDD00>#StandWith</><bg=#FFDD00;fg=#0057B7>Ukraine</> <href=https://sf.to/ukraine>https://sf.to/ukraine</>', $this->kernel->getEnvironment(), $this->kernel->isDebug() ? 'true' : 'false');
return parent::getLongVersion().sprintf(' (env: <comment>%s</>, debug: <comment>%s</>)', $this->kernel->getEnvironment(), $this->kernel->isDebug() ? 'true' : 'false');
}
public function add(Command $command): ?Command

View File

@@ -111,7 +111,7 @@ abstract class AbstractController implements ServiceSubscriberInterface
/**
* Forwards the request to another controller.
*
* @param string $controller The controller name (a string like Bundle\BlogBundle\Controller\PostController::indexAction)
* @param string $controller The controller name (a string like "App\Controller\PostController::index" or "App\Controller\PostController" if it is invokable)
*/
protected function forward(string $controller, array $path = [], array $query = []): Response
{

View File

@@ -1938,7 +1938,7 @@ class FrameworkExtension extends Extension
$container->removeDefinition('serializer.mapping.cache_class_metadata_factory');
}
if (!class_exists(Translator::class)) {
if (!$this->readConfigEnabled('translator', $container, $config)) {
$container->removeDefinition('serializer.normalizer.translatable');
}
@@ -1990,21 +1990,23 @@ class FrameworkExtension extends Extension
$container->getDefinition('serializer.name_converter.metadata_aware')->setArgument(1, new Reference($config['name_converter']));
}
$defaultContext = $config['default_context'] ?? [];
if ($defaultContext) {
$container->setParameter('serializer.default_context', $defaultContext);
}
if (isset($config['circular_reference_handler']) && $config['circular_reference_handler']) {
$arguments = $container->getDefinition('serializer.normalizer.object')->getArguments();
$context = ($arguments[6] ?? []) + ['circular_reference_handler' => new Reference($config['circular_reference_handler'])];
$context = ($arguments[6] ?? $defaultContext) + ['circular_reference_handler' => new Reference($config['circular_reference_handler'])];
$container->getDefinition('serializer.normalizer.object')->setArgument(5, null);
$container->getDefinition('serializer.normalizer.object')->setArgument(6, $context);
}
if ($config['max_depth_handler'] ?? false) {
$defaultContext = $container->getDefinition('serializer.normalizer.object')->getArgument(6);
$defaultContext += ['max_depth_handler' => new Reference($config['max_depth_handler'])];
$container->getDefinition('serializer.normalizer.object')->replaceArgument(6, $defaultContext);
}
if (isset($config['default_context']) && $config['default_context']) {
$container->setParameter('serializer.default_context', $config['default_context']);
$arguments = $container->getDefinition('serializer.normalizer.object')->getArguments();
$context = ($arguments[6] ?? $defaultContext) + ['max_depth_handler' => new Reference($config['max_depth_handler'])];
$container->getDefinition('serializer.normalizer.object')->setArgument(6, $context);
}
}
@@ -2678,13 +2680,15 @@ class FrameworkExtension extends Extension
if ($webhookEnabled) {
$webhookRequestParsers = [
MailerBridge\Brevo\Webhook\BrevoRequestParser::class => 'mailer.webhook.request_parser.brevo',
MailerBridge\Mailgun\Webhook\MailgunRequestParser::class => 'mailer.webhook.request_parser.mailgun',
MailerBridge\Mailjet\Webhook\MailjetRequestParser::class => 'mailer.webhook.request_parser.mailjet',
MailerBridge\Postmark\Webhook\PostmarkRequestParser::class => 'mailer.webhook.request_parser.postmark',
MailerBridge\Sendgrid\Webhook\SendgridRequestParser::class => 'mailer.webhook.request_parser.sendgrid',
];
foreach ($webhookRequestParsers as $class => $service) {
$package = substr($service, \strlen('mailer.transport_factory.'));
$package = substr($service, \strlen('mailer.webhook.request_parser.'));
if (!ContainerBuilder::willBeAvailable(sprintf('symfony/%s-mailer', 'gmail' === $package ? 'google' : $package), $class, ['symfony/framework-bundle', 'symfony/mailer'])) {
$container->removeDefinition($service);
@@ -2868,7 +2872,9 @@ class FrameworkExtension extends Extension
if (ContainerBuilder::willBeAvailable('symfony/mercure-notifier', NotifierBridge\Mercure\MercureTransportFactory::class, $parentPackages) && ContainerBuilder::willBeAvailable('symfony/mercure-bundle', MercureBundle::class, $parentPackages) && \in_array(MercureBundle::class, $container->getParameter('kernel.bundles'), true)) {
$container->getDefinition($classToServices[NotifierBridge\Mercure\MercureTransportFactory::class])
->replaceArgument('$registry', new Reference(HubRegistry::class));
->replaceArgument('$registry', new Reference(HubRegistry::class))
->replaceArgument('$client', new Reference('http_client', ContainerBuilder::NULL_ON_INVALID_REFERENCE))
->replaceArgument('$dispatcher', new Reference('event_dispatcher', ContainerBuilder::NULL_ON_INVALID_REFERENCE));
} elseif (ContainerBuilder::willBeAvailable('symfony/mercure-notifier', NotifierBridge\Mercure\MercureTransportFactory::class, $parentPackages)) {
$container->removeDefinition($classToServices[NotifierBridge\Mercure\MercureTransportFactory::class]);
}
@@ -2876,13 +2882,17 @@ class FrameworkExtension extends Extension
if (ContainerBuilder::willBeAvailable('symfony/fake-chat-notifier', NotifierBridge\FakeChat\FakeChatTransportFactory::class, ['symfony/framework-bundle', 'symfony/notifier', 'symfony/mailer'])) {
$container->getDefinition($classToServices[NotifierBridge\FakeChat\FakeChatTransportFactory::class])
->replaceArgument('$mailer', new Reference('mailer'))
->replaceArgument('$logger', new Reference('logger'));
->replaceArgument('$logger', new Reference('logger'))
->replaceArgument('$client', new Reference('http_client', ContainerBuilder::NULL_ON_INVALID_REFERENCE))
->replaceArgument('$dispatcher', new Reference('event_dispatcher', ContainerBuilder::NULL_ON_INVALID_REFERENCE));
}
if (ContainerBuilder::willBeAvailable('symfony/fake-sms-notifier', NotifierBridge\FakeSms\FakeSmsTransportFactory::class, ['symfony/framework-bundle', 'symfony/notifier', 'symfony/mailer'])) {
$container->getDefinition($classToServices[NotifierBridge\FakeSms\FakeSmsTransportFactory::class])
->replaceArgument('$mailer', new Reference('mailer'))
->replaceArgument('$logger', new Reference('logger'));
->replaceArgument('$logger', new Reference('logger'))
->replaceArgument('$client', new Reference('http_client', ContainerBuilder::NULL_ON_INVALID_REFERENCE))
->replaceArgument('$dispatcher', new Reference('event_dispatcher', ContainerBuilder::NULL_ON_INVALID_REFERENCE));
}
if (isset($config['admin_recipients'])) {
@@ -2896,6 +2906,19 @@ class FrameworkExtension extends Extension
if ($webhookEnabled) {
$loader->load('notifier_webhook.php');
$webhookRequestParsers = [
NotifierBridge\Twilio\Webhook\TwilioRequestParser::class => 'notifier.webhook.request_parser.twilio',
NotifierBridge\Vonage\Webhook\VonageRequestParser::class => 'notifier.webhook.request_parser.vonage',
];
foreach ($webhookRequestParsers as $class => $service) {
$package = substr($service, \strlen('notifier.webhook.request_parser.'));
if (!ContainerBuilder::willBeAvailable(sprintf('symfony/%s-notifier', $package), $class, ['symfony/framework-bundle', 'symfony/notifier'])) {
$container->removeDefinition($service);
}
}
}
}

View File

@@ -42,7 +42,8 @@ final class ConsoleProfilerListener implements EventSubscriberInterface
private readonly Profiler $profiler,
private readonly RequestStack $requestStack,
private readonly Stopwatch $stopwatch,
private readonly UrlGeneratorInterface $urlGenerator,
private readonly bool $cliMode,
private readonly ?UrlGeneratorInterface $urlGenerator = null,
) {
$this->profiles = new \SplObjectStorage();
$this->parents = new \SplObjectStorage();
@@ -59,6 +60,10 @@ final class ConsoleProfilerListener implements EventSubscriberInterface
public function initialize(ConsoleCommandEvent $event): void
{
if (!$this->cliMode) {
return;
}
$input = $event->getInput();
if (!$input->hasOption('profile') || !$input->getOption('profile')) {
$this->profiler->disable();
@@ -78,12 +83,16 @@ final class ConsoleProfilerListener implements EventSubscriberInterface
public function catch(ConsoleErrorEvent $event): void
{
if (!$this->cliMode) {
return;
}
$this->error = $event->getError();
}
public function profile(ConsoleTerminateEvent $event): void
{
if (!$this->profiler->isEnabled()) {
if (!$this->cliMode || !$this->profiler->isEnabled()) {
return;
}
@@ -131,12 +140,14 @@ final class ConsoleProfilerListener implements EventSubscriberInterface
$p = $this->profiles[$r];
$this->profiler->saveProfile($p);
$token = $p->getToken();
$output?->writeln(sprintf(
'See profile <href=%s>%s</>',
$this->urlGenerator->generate('_profiler', ['token' => $token], UrlGeneratorInterface::ABSOLUTE_URL),
$token
));
if ($this->urlGenerator && $output) {
$token = $p->getToken();
$output->writeln(sprintf(
'See profile <href=%s>%s</>',
$this->urlGenerator->generate('_profiler', ['token' => $token], UrlGeneratorInterface::ABSOLUTE_URL),
$token
));
}
}
$this->profiles = new \SplObjectStorage();

View File

@@ -11,8 +11,12 @@
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
use Symfony\Component\Mailer\Bridge\Brevo\RemoteEvent\BrevoPayloadConverter;
use Symfony\Component\Mailer\Bridge\Brevo\Webhook\BrevoRequestParser;
use Symfony\Component\Mailer\Bridge\Mailgun\RemoteEvent\MailgunPayloadConverter;
use Symfony\Component\Mailer\Bridge\Mailgun\Webhook\MailgunRequestParser;
use Symfony\Component\Mailer\Bridge\Mailjet\RemoteEvent\MailjetPayloadConverter;
use Symfony\Component\Mailer\Bridge\Mailjet\Webhook\MailjetRequestParser;
use Symfony\Component\Mailer\Bridge\Postmark\RemoteEvent\PostmarkPayloadConverter;
use Symfony\Component\Mailer\Bridge\Postmark\Webhook\PostmarkRequestParser;
use Symfony\Component\Mailer\Bridge\Sendgrid\RemoteEvent\SendgridPayloadConverter;
@@ -20,11 +24,21 @@ use Symfony\Component\Mailer\Bridge\Sendgrid\Webhook\SendgridRequestParser;
return static function (ContainerConfigurator $container) {
$container->services()
->set('mailer.payload_converter.brevo', BrevoPayloadConverter::class)
->set('mailer.webhook.request_parser.brevo', BrevoRequestParser::class)
->args([service('mailer.payload_converter.brevo')])
->alias(BrevoRequestParser::class, 'mailer.webhook.request_parser.brevo')
->set('mailer.payload_converter.mailgun', MailgunPayloadConverter::class)
->set('mailer.webhook.request_parser.mailgun', MailgunRequestParser::class)
->args([service('mailer.payload_converter.mailgun')])
->alias(MailgunRequestParser::class, 'mailer.webhook.request_parser.mailgun')
->set('mailer.payload_converter.mailjet', MailjetPayloadConverter::class)
->set('mailer.webhook.request_parser.mailjet', MailjetRequestParser::class)
->args([service('mailer.payload_converter.mailjet')])
->alias(MailjetRequestParser::class, 'mailer.webhook.request_parser.mailjet')
->set('mailer.payload_converter.postmark', PostmarkPayloadConverter::class)
->set('mailer.webhook.request_parser.postmark', PostmarkRequestParser::class)
->args([service('mailer.payload_converter.postmark')])

View File

@@ -12,10 +12,14 @@
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
use Symfony\Component\Notifier\Bridge\Twilio\Webhook\TwilioRequestParser;
use Symfony\Component\Notifier\Bridge\Vonage\Webhook\VonageRequestParser;
return static function (ContainerConfigurator $container) {
$container->services()
->set('notifier.webhook.request_parser.twilio', TwilioRequestParser::class)
->alias(TwilioRequestParser::class, 'notifier.webhook.request_parser.twilio')
->set('notifier.webhook.request_parser.vonage', VonageRequestParser::class)
->alias(VonageRequestParser::class, 'notifier.webhook.request_parser.vonage')
;
};

View File

@@ -43,7 +43,8 @@ return static function (ContainerConfigurator $container) {
service('profiler'),
service('.virtual_request_stack'),
service('debug.stopwatch'),
service('router'),
param('kernel.runtime_mode.cli'),
service('router')->nullOnInvalid(),
])
->tag('kernel.event_subscriber')

View File

@@ -979,6 +979,7 @@
<xsd:element name="service" type="xsd:string" minOccurs="0" maxOccurs="1" />
<xsd:element name="secret" type="xsd:string" minOccurs="0" maxOccurs="1" />
</xsd:sequence>
<xsd:attribute name="type" type="xsd:string" use="required" />
</xsd:complexType>
<xsd:complexType name="remote-event">

View File

@@ -19,7 +19,7 @@ if (false) {
/**
* @deprecated since Symfony 6.4, to be removed in 7.0, use {@link AttributeRouteControllerLoader} instead
*/
class AnnotatedRouteControllerLoader
class AnnotatedRouteControllerLoader extends AttributeRouteControllerLoader
{
}
}

View File

@@ -82,7 +82,7 @@
"symfony/asset": "<5.4",
"symfony/asset-mapper": "<6.4",
"symfony/clock": "<6.3",
"symfony/console": "<5.4",
"symfony/console": "<5.4|>=7.0",
"symfony/dotenv": "<5.4",
"symfony/dom-crawler": "<6.4",
"symfony/http-client": "<6.3",

View File

@@ -88,7 +88,7 @@ class RequestMatcher implements RequestMatcherInterface
}
/**
* Adds a check for the the URL port.
* Adds a check for the URL port.
*
* @param int|null $port The port number to connect to
*

View File

@@ -73,6 +73,7 @@ final class ArgumentResolver implements ArgumentResolverInterface
$argumentValueResolvers = [
$this->namedResolvers->get($resolverName),
new RequestAttributeValueResolver(),
new DefaultValueResolver(),
];
}

View File

@@ -86,7 +86,7 @@ class BackedEnumValueResolver implements ArgumentValueResolverInterface, ValueRe
try {
return [$enumType::from($value)];
} catch (\ValueError $e) {
} catch (\ValueError|\TypeError $e) {
throw new NotFoundHttpException(sprintf('Could not resolve the "%s $%s" controller argument: ', $argument->getType(), $argument->getName()).$e->getMessage(), $e);
}
}

View File

@@ -119,7 +119,7 @@ class RequestPayloadValueResolver implements ValueResolverInterface, EventSubscr
$payload = $e->getData();
}
if (null !== $payload) {
if (null !== $payload && !\count($violations)) {
$violations->addAll($this->validator->validate($payload, null, $argument->validationGroups ?? null));
}

View File

@@ -25,7 +25,7 @@ if (false) {
/**
* @deprecated since Symfony 6.4, use FileLinkFormatter from the ErrorHandler component instead
*/
class FileLinkFormatter
class FileLinkFormatter extends ErrorHandlerFileLinkFormatter
{
}
}

View File

@@ -128,6 +128,8 @@ class RegisterControllerArgumentLocatorsPass implements CompilerPassInterface
$type = preg_replace('/(^|[(|&])\\\\/', '\1', $target = ltrim(ProxyHelper::exportType($p) ?? '', '?'));
$invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE;
$autowireAttributes = $autowire ? $emptyAutowireAttributes : [];
$parsedName = $p->name;
$k = null;
if (isset($arguments[$r->name][$p->name])) {
$target = $arguments[$r->name][$p->name];
@@ -138,7 +140,11 @@ class RegisterControllerArgumentLocatorsPass implements CompilerPassInterface
} elseif ($p->allowsNull() && !$p->isOptional()) {
$invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE;
}
} elseif (isset($bindings[$bindingName = $type.' $'.$name = Target::parseName($p)]) || isset($bindings[$bindingName = '$'.$name]) || isset($bindings[$bindingName = $type])) {
} elseif (isset($bindings[$bindingName = $type.' $'.$name = Target::parseName($p, $k, $parsedName)])
|| isset($bindings[$bindingName = $type.' $'.$parsedName])
|| isset($bindings[$bindingName = '$'.$name])
|| isset($bindings[$bindingName = $type])
) {
$binding = $bindings[$bindingName];
[$bindingValue, $bindingId, , $bindingType, $bindingFile] = $binding->getValues();

View File

@@ -35,14 +35,16 @@ use Symfony\Contracts\Service\ResetInterface;
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
* @author Tobias Schultze <http://tobion.de>
*
* @internal
*/
abstract class AbstractSessionListener implements EventSubscriberInterface, ResetInterface
{
public const NO_AUTO_CACHE_CONTROL_HEADER = 'Symfony-Session-NoAutoCacheControl';
/**
* @internal
*/
protected ?ContainerInterface $container;
private bool $debug;
/**
@@ -50,6 +52,9 @@ abstract class AbstractSessionListener implements EventSubscriberInterface, Rese
*/
private array $sessionOptions;
/**
* @internal
*/
public function __construct(ContainerInterface $container = null, bool $debug = false, array $sessionOptions = [])
{
$this->container = $container;
@@ -57,6 +62,9 @@ abstract class AbstractSessionListener implements EventSubscriberInterface, Rese
$this->sessionOptions = $sessionOptions;
}
/**
* @internal
*/
public function onKernelRequest(RequestEvent $event): void
{
if (!$event->isMainRequest()) {
@@ -90,6 +98,9 @@ abstract class AbstractSessionListener implements EventSubscriberInterface, Rese
}
}
/**
* @internal
*/
public function onKernelResponse(ResponseEvent $event): void
{
if (!$event->isMainRequest() || (!$this->container->has('initialized_session') && !$event->getRequest()->hasSession())) {
@@ -218,6 +229,9 @@ abstract class AbstractSessionListener implements EventSubscriberInterface, Rese
}
}
/**
* @internal
*/
public function onSessionUsage(): void
{
if (!$this->debug) {
@@ -253,6 +267,9 @@ abstract class AbstractSessionListener implements EventSubscriberInterface, Rese
throw new UnexpectedSessionUsageException('Session was used while the request was declared stateless.');
}
/**
* @internal
*/
public static function getSubscribedEvents(): array
{
return [
@@ -262,6 +279,9 @@ abstract class AbstractSessionListener implements EventSubscriberInterface, Rese
];
}
/**
* @internal
*/
public function reset(): void
{
if (\PHP_SESSION_ACTIVE === session_status()) {
@@ -278,6 +298,8 @@ abstract class AbstractSessionListener implements EventSubscriberInterface, Rese
/**
* Gets the session object.
*
* @internal
*/
abstract protected function getSession(): ?SessionInterface;

View File

@@ -69,7 +69,7 @@ class LocaleListener implements EventSubscriberInterface
if ($locale = $request->attributes->get('_locale')) {
$request->setLocale($locale);
} elseif ($this->useAcceptLanguageHeader) {
if ($preferredLanguage = $request->getPreferredLanguage($this->enabledLocales)) {
if ($request->getLanguages() && $preferredLanguage = $request->getPreferredLanguage($this->enabledLocales)) {
$request->setLocale($preferredLanguage);
}
$request->attributes->set('_vary_by_language', true);

View File

@@ -76,11 +76,11 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl
*/
private static array $freshCache = [];
public const VERSION = '6.4.0';
public const VERSION_ID = 60400;
public const VERSION = '6.4.2';
public const VERSION_ID = 60402;
public const MAJOR_VERSION = 6;
public const MINOR_VERSION = 4;
public const RELEASE_VERSION = 0;
public const RELEASE_VERSION = 2;
public const EXTRA_VERSION = '';
public const END_OF_MAINTENANCE = '11/2026';

View File

@@ -21,7 +21,7 @@ if (false) {
/**
* @deprecated since Symfony 6.4, to be removed in 7.0, use {@link HttpFoundationUriSigner} instead
*/
class UriSigner
class UriSigner extends HttpFoundationUriSigner
{
}
}

View File

@@ -19,7 +19,7 @@ if (false) {
/**
* @deprecated since Symfony 6.4, to be removed in 7.0, use {@link AttributeClassLoader} instead
*/
class AnnotationClassLoader
abstract class AnnotationClassLoader extends AttributeClassLoader
{
}
}

View File

@@ -19,7 +19,7 @@ if (false) {
/**
* @deprecated since Symfony 6.4, to be removed in 7.0, use {@link AttributeDirectoryLoader} instead
*/
class AnnotationDirectoryLoader
class AnnotationDirectoryLoader extends AttributeDirectoryLoader
{
}
}

View File

@@ -19,7 +19,7 @@ if (false) {
/**
* @deprecated since Symfony 6.4, to be removed in 7.0, use {@link AttributeFileLoader} instead
*/
class AnnotationFileLoader
class AnnotationFileLoader extends AttributeFileLoader
{
}
}

View File

@@ -144,7 +144,9 @@ abstract class AttributeClassLoader implements LoaderInterface
if (1 === $collection->count() - \count($routeNamesBefore)) {
$newRouteName = current(array_diff(array_keys($collection->all()), $routeNamesBefore));
$collection->addAlias(sprintf('%s::%s', $class->name, $method->name), $newRouteName);
if ($newRouteName !== $aliasName = sprintf('%s::%s', $class->name, $method->name)) {
$collection->addAlias($aliasName, $newRouteName);
}
}
}
if (0 === $collection->count() && $class->hasMethod('__invoke')) {
@@ -155,8 +157,14 @@ abstract class AttributeClassLoader implements LoaderInterface
}
}
if ($fqcnAlias && 1 === $collection->count()) {
$collection->addAlias($class->name, $invokeRouteName = key($collection->all()));
$collection->addAlias(sprintf('%s::__invoke', $class->name), $invokeRouteName);
$invokeRouteName = key($collection->all());
if ($invokeRouteName !== $class->name) {
$collection->addAlias($class->name, $invokeRouteName);
}
if ($invokeRouteName !== $aliasName = sprintf('%s::__invoke', $class->name)) {
$collection->addAlias($aliasName, $invokeRouteName);
}
}
if ($this->hasDeprecatedAnnotations) {

View File

@@ -147,9 +147,24 @@ class RouteCollection implements \IteratorAggregate, \Countable
*/
public function remove(string|array $name)
{
$routes = [];
foreach ((array) $name as $n) {
if (isset($this->routes[$n])) {
$routes[] = $n;
}
unset($this->routes[$n], $this->priorities[$n], $this->aliases[$n]);
}
if (!$routes) {
return;
}
foreach ($this->aliases as $k => $alias) {
if (\in_array($alias->getId(), $routes, true)) {
unset($this->aliases[$k]);
}
}
}
/**

View File

@@ -1,3 +0,0 @@
vendor/
composer.lock
phpunit.xml

View File

@@ -11,9 +11,14 @@
namespace Symfony\Contracts\Service\Attribute;
use Symfony\Contracts\Service\ServiceSubscriberInterface;
use Symfony\Contracts\Service\ServiceSubscriberTrait;
/**
* For use as the return value for {@see ServiceSubscriberInterface}.
*
* @example new SubscribedService('http_client', HttpClientInterface::class, false, new Target('githubApi'))
*
* Use with {@see ServiceSubscriberTrait} to mark a method's return type
* as a subscribed service.
*
@@ -22,12 +27,21 @@ use Symfony\Contracts\Service\ServiceSubscriberTrait;
#[\Attribute(\Attribute::TARGET_METHOD)]
final class SubscribedService
{
/** @var object[] */
public array $attributes;
/**
* @param string|null $key The key to use for the service
* If null, use "ClassName::methodName"
* @param string|null $key The key to use for the service
* @param class-string|null $type The service class
* @param bool $nullable Whether the service is optional
* @param object|object[] $attributes One or more dependency injection attributes to use
*/
public function __construct(
public ?string $key = null
public ?string $key = null,
public ?string $type = null,
public bool $nullable = false,
array|object $attributes = [],
) {
$this->attributes = \is_array($attributes) ? $attributes : [$attributes];
}
}

View File

@@ -1,4 +1,4 @@
Copyright (c) 2018-2022 Fabien Potencier
Copyright (c) 2018-present Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -3,7 +3,7 @@ Symfony Service Contracts
A set of abstractions extracted out of the Symfony components.
Can be used to build on semantics that the Symfony components proved useful - and
Can be used to build on semantics that the Symfony components proved useful and
that already have battle tested implementations.
See https://github.com/symfony/contracts/blob/main/README.md for more information.

View File

@@ -26,5 +26,8 @@ namespace Symfony\Contracts\Service;
*/
interface ResetInterface
{
/**
* @return void
*/
public function reset();
}

View File

@@ -26,34 +26,24 @@ class_exists(NotFoundExceptionInterface::class);
*/
trait ServiceLocatorTrait
{
private $factories;
private $loading = [];
private $providedTypes;
private array $factories;
private array $loading = [];
private array $providedTypes;
/**
* @param callable[] $factories
* @param array<string, callable> $factories
*/
public function __construct(array $factories)
{
$this->factories = $factories;
}
/**
* {@inheritdoc}
*
* @return bool
*/
public function has(string $id)
public function has(string $id): bool
{
return isset($this->factories[$id]);
}
/**
* {@inheritdoc}
*
* @return mixed
*/
public function get(string $id)
public function get(string $id): mixed
{
if (!isset($this->factories[$id])) {
throw $this->createNotFoundException($id);
@@ -75,12 +65,9 @@ trait ServiceLocatorTrait
}
}
/**
* {@inheritdoc}
*/
public function getProvidedServices(): array
{
if (null === $this->providedTypes) {
if (!isset($this->providedTypes)) {
$this->providedTypes = [];
foreach ($this->factories as $name => $factory) {

View File

@@ -18,9 +18,18 @@ use Psr\Container\ContainerInterface;
*
* @author Nicolas Grekas <p@tchwork.com>
* @author Mateusz Sip <mateusz.sip@gmail.com>
*
* @template-covariant T of mixed
*/
interface ServiceProviderInterface extends ContainerInterface
{
/**
* @return T
*/
public function get(string $id): mixed;
public function has(string $id): bool;
/**
* Returns an associative array of service types keyed by the identifiers provided by the current container.
*
@@ -30,7 +39,7 @@ interface ServiceProviderInterface extends ContainerInterface
* * ['foo' => '?'] means the container provides service name "foo" of unspecified type
* * ['bar' => '?Bar\Baz'] means the container provides a service "bar" of type Bar\Baz|null
*
* @return string[] The provided service types, keyed by service names
* @return array<string, string> The provided service types, keyed by service names
*/
public function getProvidedServices(): array;
}

View File

@@ -11,6 +11,8 @@
namespace Symfony\Contracts\Service;
use Symfony\Contracts\Service\Attribute\SubscribedService;
/**
* A ServiceSubscriber exposes its dependencies via the static {@link getSubscribedServices} method.
*
@@ -29,7 +31,8 @@ namespace Symfony\Contracts\Service;
interface ServiceSubscriberInterface
{
/**
* Returns an array of service types required by such instances, optionally keyed by the service names used internally.
* Returns an array of service types (or {@see SubscribedService} objects) required
* by such instances, optionally keyed by the service names used internally.
*
* For mandatory dependencies:
*
@@ -47,7 +50,13 @@ interface ServiceSubscriberInterface
* * ['?Psr\Log\LoggerInterface'] is a shortcut for
* * ['Psr\Log\LoggerInterface' => '?Psr\Log\LoggerInterface']
*
* @return string[] The required service types, optionally keyed by service names
* additionally, an array of {@see SubscribedService}'s can be returned:
*
* * [new SubscribedService('logger', Psr\Log\LoggerInterface::class)]
* * [new SubscribedService(type: Psr\Log\LoggerInterface::class, nullable: true)]
* * [new SubscribedService('http_client', HttpClientInterface::class, attributes: new Target('githubApi'))]
*
* @return string[]|SubscribedService[] The required service types, optionally keyed by service names
*/
public static function getSubscribedServices();
public static function getSubscribedServices(): array;
}

View File

@@ -12,6 +12,7 @@
namespace Symfony\Contracts\Service;
use Psr\Container\ContainerInterface;
use Symfony\Contracts\Service\Attribute\Required;
use Symfony\Contracts\Service\Attribute\SubscribedService;
/**
@@ -25,85 +26,53 @@ trait ServiceSubscriberTrait
/** @var ContainerInterface */
protected $container;
/**
* {@inheritdoc}
*/
public static function getSubscribedServices(): array
{
$services = method_exists(get_parent_class(self::class) ?: '', __FUNCTION__) ? parent::getSubscribedServices() : [];
$attributeOptIn = false;
if (\PHP_VERSION_ID >= 80000) {
foreach ((new \ReflectionClass(self::class))->getMethods() as $method) {
if (self::class !== $method->getDeclaringClass()->name) {
continue;
}
if (!$attribute = $method->getAttributes(SubscribedService::class)[0] ?? null) {
continue;
}
if ($method->isStatic() || $method->isAbstract() || $method->isGenerator() || $method->isInternal() || $method->getNumberOfRequiredParameters()) {
throw new \LogicException(sprintf('Cannot use "%s" on method "%s::%s()" (can only be used on non-static, non-abstract methods with no parameters).', SubscribedService::class, self::class, $method->name));
}
if (!$returnType = $method->getReturnType()) {
throw new \LogicException(sprintf('Cannot use "%s" on methods without a return type in "%s::%s()".', SubscribedService::class, $method->name, self::class));
}
$serviceId = $returnType instanceof \ReflectionNamedType ? $returnType->getName() : (string) $returnType;
if ($returnType->allowsNull()) {
$serviceId = '?'.$serviceId;
}
$services[$attribute->newInstance()->key ?? self::class.'::'.$method->name] = $serviceId;
$attributeOptIn = true;
foreach ((new \ReflectionClass(self::class))->getMethods() as $method) {
if (self::class !== $method->getDeclaringClass()->name) {
continue;
}
}
if (!$attributeOptIn) {
foreach ((new \ReflectionClass(self::class))->getMethods() as $method) {
if ($method->isStatic() || $method->isAbstract() || $method->isGenerator() || $method->isInternal() || $method->getNumberOfRequiredParameters()) {
continue;
}
if (!$attribute = $method->getAttributes(SubscribedService::class)[0] ?? null) {
continue;
}
if (self::class !== $method->getDeclaringClass()->name) {
continue;
}
if ($method->isStatic() || $method->isAbstract() || $method->isGenerator() || $method->isInternal() || $method->getNumberOfRequiredParameters()) {
throw new \LogicException(sprintf('Cannot use "%s" on method "%s::%s()" (can only be used on non-static, non-abstract methods with no parameters).', SubscribedService::class, self::class, $method->name));
}
if (!($returnType = $method->getReturnType()) instanceof \ReflectionNamedType) {
continue;
}
if (!$returnType = $method->getReturnType()) {
throw new \LogicException(sprintf('Cannot use "%s" on methods without a return type in "%s::%s()".', SubscribedService::class, $method->name, self::class));
}
if ($returnType->isBuiltin()) {
continue;
}
/* @var SubscribedService $attribute */
$attribute = $attribute->newInstance();
$attribute->key ??= self::class.'::'.$method->name;
$attribute->type ??= $returnType instanceof \ReflectionNamedType ? $returnType->getName() : (string) $returnType;
$attribute->nullable = $returnType->allowsNull();
if (\PHP_VERSION_ID >= 80000) {
trigger_deprecation('symfony/service-contracts', '2.5', 'Using "%s" in "%s" without using the "%s" attribute on any method is deprecated.', ServiceSubscriberTrait::class, self::class, SubscribedService::class);
}
$services[self::class.'::'.$method->name] = '?'.($returnType instanceof \ReflectionNamedType ? $returnType->getName() : $returnType);
if ($attribute->attributes) {
$services[] = $attribute;
} else {
$services[$attribute->key] = ($attribute->nullable ? '?' : '').$attribute->type;
}
}
return $services;
}
/**
* @required
*
* @return ContainerInterface|null
*/
public function setContainer(ContainerInterface $container)
#[Required]
public function setContainer(ContainerInterface $container): ?ContainerInterface
{
$this->container = $container;
$ret = null;
if (method_exists(get_parent_class(self::class) ?: '', __FUNCTION__)) {
return parent::setContainer($container);
$ret = parent::setContainer($container);
}
return null;
$this->container = $container;
return $ret;
}
}

View File

@@ -16,23 +16,22 @@
}
],
"require": {
"php": ">=7.2.5",
"psr/container": "^1.1",
"symfony/deprecation-contracts": "^2.1|^3"
"php": ">=8.1",
"psr/container": "^1.1|^2.0"
},
"conflict": {
"ext-psr": "<1.1|>=2"
},
"suggest": {
"symfony/service-implementation": ""
},
"autoload": {
"psr-4": { "Symfony\\Contracts\\Service\\": "" }
"psr-4": { "Symfony\\Contracts\\Service\\": "" },
"exclude-from-classmap": [
"/Test/"
]
},
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-main": "2.5-dev"
"dev-main": "3.4-dev"
},
"thanks": {
"name": "symfony/contracts",

View File

@@ -664,7 +664,7 @@ pre.sf-dump:after {
clear: both;
}
pre.sf-dump span {
display: inline;
display: inline-flex;
}
pre.sf-dump a {
text-decoration: none;

View File

@@ -166,8 +166,9 @@ trait LazyGhostTrait
if ($state && (null === $scope || isset($propertyScopes["\0$scope\0$name"]))) {
if (LazyObjectState::STATUS_INITIALIZED_FULL === $state->status) {
// Work around php/php-src#12695
$property = $propertyScopes[null === $scope ? $name : "\0$scope\0$name"][3]
?? (Hydrator::$propertyScopes[$this::class] = Hydrator::getPropertyScopes($this::class))[3];
$property = null === $scope ? $name : "\0$scope\0$name";
$property = $propertyScopes[$property][3]
?? Hydrator::$propertyScopes[$this::class][$property][3] = new \ReflectionProperty($scope ?? $class, $name);
} else {
$property = null;
}

View File

@@ -7,10 +7,10 @@ of objects:
- `VarExporter::export()` allows exporting any serializable PHP data structure to
plain PHP code. While doing so, it preserves all the semantics associated with
the serialization mechanism of PHP (`__wakeup`, `__sleep`, `Serializable`,
`__serialize`, `__unserialize`.)
`__serialize`, `__unserialize`);
- `Instantiator::instantiate()` creates an object and sets its properties without
calling its constructor nor any other methods.
- `Hydrator::hydrate()` can set the properties of an existing object.
calling its constructor nor any other methods;
- `Hydrator::hydrate()` can set the properties of an existing object;
- `Lazy*Trait` can make a class behave as a lazy-loading ghost or virtual proxy.
VarExporter::export()
@@ -26,7 +26,7 @@ Unlike `var_export()`, this works on any serializable PHP value.
It also provides a few improvements over `var_export()`/`serialize()`:
* the output is PSR-2 compatible;
* the output can be re-indented without messing up with `\r` or `\n` in the data
* the output can be re-indented without messing up with `\r` or `\n` in the data;
* missing classes throw a `ClassNotFoundException` instead of being unserialized
to `PHP_Incomplete_Class` objects;
* references involving `SplObjectStorage`, `ArrayObject` or `ArrayIterator`
@@ -61,7 +61,7 @@ Hydrator::hydrate($object, [], [
------------
The component provides two lazy-loading patterns: ghost objects and virtual
proxies (see https://martinfowler.com/eaaCatalog/lazyLoad.html for reference.)
proxies (see https://martinfowler.com/eaaCatalog/lazyLoad.html for reference).
Ghost objects work only with concrete and non-internal classes. In the generic
case, they are not compatible with using factories in their initializer.

View File

@@ -13,6 +13,7 @@
constructor() {
this.#createTabs();
this.#createToggles();
this.#createCopyToClipboard();
this.#convertDateTimesToUserTimezone();
}
@@ -161,18 +162,39 @@
});
});
/* Prevents from disallowing clicks on "copy to clipboard" elements inside toggles */
const copyToClipboardElements = toggle.querySelectorAll('span[data-clipboard-text]');
copyToClipboardElements.forEach((copyToClipboardElement) => {
copyToClipboardElement.addEventListener('click', (e) => {
e.stopPropagation();
});
});
toggle.setAttribute('data-processed', 'true');
});
}
#createCopyToClipboard() {
if (!navigator.clipboard) {
return;
}
const copyToClipboardElements = document.querySelectorAll('[data-clipboard-text]');
copyToClipboardElements.forEach((copyToClipboardElement) => {
copyToClipboardElement.classList.remove('hidden');
copyToClipboardElement.addEventListener('click', (e) => {
/* Prevents from disallowing clicks on "copy to clipboard" elements inside toggles */
e.stopPropagation();
navigator.clipboard.writeText(copyToClipboardElement.getAttribute('data-clipboard-text'));
let oldContent = copyToClipboardElement.textContent;
copyToClipboardElement.textContent = `✅ Copied!`;
copyToClipboardElement.disabled = true;
setTimeout(() => {
copyToClipboardElement.textContent = oldContent;
copyToClipboardElement.disabled = false;
}, 7000);
});
});
}
#convertDateTimesToUserTimezone() {
const userTimezoneName = Intl.DateTimeFormat().resolvedOptions().timeZone;

View File

@@ -47,6 +47,8 @@
}
.sf-minitoolbar {
--sf-toolbar-gray-800: #262626;
background-color: var(--sf-toolbar-gray-800);
border-top-left-radius: 4px;
bottom: 0;
@@ -66,6 +68,8 @@
}
.sf-minitoolbar svg,
.sf-minitoolbar img {
--sf-toolbar-gray-200: #e5e5e5;
color: var(--sf-toolbar-gray-200);
max-height: 24px;
max-width: 24px;

View File

@@ -50,33 +50,6 @@
};
}
if (navigator.clipboard) {
document.addEventListener('readystatechange', () => {
if (document.readyState !== 'complete') {
return;
}
document.querySelectorAll('[data-clipboard-text]').forEach(function (element) {
removeClass(element, 'hidden');
element.addEventListener('click', function () {
navigator.clipboard.writeText(element.getAttribute('data-clipboard-text'));
if (element.classList.contains("label")) {
let oldContent = element.textContent;
element.textContent = "✅ Copied!";
element.classList.add("status-success");
setTimeout(() => {
element.textContent = oldContent;
element.classList.remove("status-success");
}, 7000);
}
});
});
});
}
var request = function(url, onSuccess, onError, payload, options, tries) {
var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
options = options || {};
@@ -577,7 +550,9 @@
/* Evaluate in global scope scripts embedded inside the toolbar */
var i, scripts = [].slice.call(el.querySelectorAll('script'));
for (i = 0; i < scripts.length; ++i) {
eval.call({}, scripts[i].firstChild.nodeValue);
if (scripts[i].firstChild) {
eval.call({}, scripts[i].firstChild.nodeValue);
}
}
el.style.display = -1 !== xhr.responseText.indexOf('sf-toolbarreset') ? 'block' : 'none';
@@ -648,7 +623,7 @@
sfwdt.innerHTML = '\
<div class="sf-toolbarreset notranslate">\
<div class="sf-toolbar-icon"><svg width="26" height="28" xmlns="http://www.w3.org/2000/svg" version="1.1" x="0px" y="0px" viewBox="0 0 26 28" enable-background="new 0 0 26 28" xml:space="preserve"><path fill="#FFFFFF" d="M13 0C5.8 0 0 5.8 0 13c0 7.2 5.8 13 13 13c7.2 0 13-5.8 13-13C26 5.8 20.2 0 13 0z M20 7.5 c-0.6 0-1-0.3-1-0.9c0-0.2 0-0.4 0.2-0.6c0.1-0.3 0.2-0.3 0.2-0.4c0-0.3-0.5-0.4-0.7-0.4c-2 0.1-2.5 2.7-2.9 4.8l-0.2 1.1 c1.1 0.2 1.9 0 2.4-0.3c0.6-0.4-0.2-0.8-0.1-1.3C18 9.2 18.4 9 18.7 8.9c0.5 0 0.8 0.5 0.8 1c0 0.8-1.1 2-3.3 1.9 c-0.3 0-0.5 0-0.7-0.1L15 14.1c-0.4 1.7-0.9 4.1-2.6 6.2c-1.5 1.8-3.1 2.1-3.8 2.1c-1.3 0-2.1-0.6-2.2-1.6c0-0.9 0.8-1.4 1.3-1.4 c0.7 0 1.2 0.5 1.2 1.1c0 0.5-0.2 0.6-0.4 0.7c-0.1 0.1-0.3 0.2-0.3 0.4c0 0.1 0.1 0.3 0.4 0.3c0.5 0 0.9-0.3 1.2-0.5 c1.3-1 1.7-2.9 2.4-6.2l0.1-0.8c0.2-1.1 0.5-2.3 0.8-3.5c-0.9-0.7-1.4-1.5-2.6-1.8c-0.8-0.2-1.3 0-1.7 0.4C8.4 10 8.6 10.7 9 11.1 l0.7 0.7c0.8 0.9 1.3 1.7 1.1 2.7c-0.3 1.6-2.1 2.8-4.3 2.1c-1.9-0.6-2.2-1.9-2-2.7c0.2-0.6 0.7-0.8 1.2-0.6 c0.5 0.2 0.7 0.8 0.6 1.3c0 0.1 0 0.1-0.1 0.3C6 15 5.9 15.2 5.9 15.3c-0.1 0.4 0.4 0.7 0.8 0.8c0.8 0.3 1.7-0.2 1.9-0.9 c0.2-0.6-0.2-1.1-0.4-1.2l-0.8-0.9c-0.4-0.4-1.2-1.5-0.8-2.8c0.2-0.5 0.5-1 0.9-1.4c1-0.7 2-0.8 3-0.6c1.3 0.4 1.9 1.2 2.8 1.9 c0.5-1.3 1.1-2.6 2-3.8c0.9-1 2-1.7 3.3-1.8C20 4.8 21 5.4 21 6.3C21 6.7 20.8 7.5 20 7.5z"/></svg></div>\
An error occurred while loading the web debug toolbar. <a href="{{ url("_profiler_home")|escape('js') }}' + newToken + '>Open the web profiler.</a>\
An error occurred while loading the web debug toolbar. <a href="{{ url("_profiler_home")|escape('js') }}' + newToken + '">Open the web profiler.</a>\
</div>\
';
sfwdt.setAttribute('class', 'sf-toolbar sf-error-toolbar');

View File

@@ -39,10 +39,10 @@
<div class="block-exception">
<h1>Redirection Intercepted</h1>
{% set absolute_url = host in location ? location : host ~ location %}
{% set absolute_url = absolute_url(location) %}
<p>This request redirects to <strong>{{ absolute_url }}</strong></p>
<p><a class="btn" href="{{ location }}">Follow redirect</a></p>
<p><a class="btn" href="{{ absolute_url }}">Follow redirect</a></p>
<p class="sf-redirection-help">
The redirect was intercepted by the Symfony Web Debug toolbar to help debugging.

View File

@@ -14,6 +14,7 @@ namespace Symfony\Bundle\WebProfilerBundle\Twig;
use Symfony\Component\VarDumper\Cloner\Data;
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
use Twig\Environment;
use Twig\Extension\EscaperExtension;
use Twig\Extension\ProfilerExtension;
use Twig\Profiler\Profile;
use Twig\TwigFunction;
@@ -78,12 +79,12 @@ class WebProfilerExtension extends ProfilerExtension
public function dumpLog(Environment $env, string $message, Data $context = null): string
{
$message = twig_escape_filter($env, $message);
$message = self::escape($env, $message);
$message = preg_replace('/&quot;(.*?)&quot;/', '&quot;<b>$1</b>&quot;', $message);
$replacements = [];
foreach ($context ?? [] as $k => $v) {
$k = '{'.twig_escape_filter($env, $k).'}';
$k = '{'.self::escape($env, $k).'}';
if (str_contains($message, $k)) {
$replacements[$k] = $v;
}
@@ -104,4 +105,14 @@ class WebProfilerExtension extends ProfilerExtension
{
return 'profiler';
}
private static function escape(Environment $env, string $s): string
{
if (method_exists(EscaperExtension::class, 'escape')) {
return EscaperExtension::escape($env, $s);
}
// to be removed when support for Twig 3 is dropped
return twig_escape_filter($env, $s);
}
}

View File

@@ -1,18 +0,0 @@
; top-most EditorConfig file
root = true
; Unix-style newlines
[*]
end_of_line = LF
[*.php]
indent_style = space
indent_size = 4
[*.test]
indent_style = space
indent_size = 4
[*.rst]
indent_style = space
indent_size = 4

View File

@@ -1,4 +0,0 @@
/doc/ export-ignore
/extra/ export-ignore
/tests/ export-ignore
/phpunit.xml.dist export-ignore

View File

@@ -1,149 +0,0 @@
name: "CI"
on:
pull_request:
push:
branches:
- '3.x'
env:
SYMFONY_PHPUNIT_DISABLE_RESULT_CACHE: 1
permissions:
contents: read
jobs:
tests:
name: "PHP ${{ matrix.php-version }}"
runs-on: 'ubuntu-latest'
continue-on-error: ${{ matrix.experimental }}
strategy:
matrix:
php-version:
- '7.2.5'
- '7.3'
- '7.4'
- '8.0'
- '8.1'
experimental: [false]
steps:
- name: "Checkout code"
uses: actions/checkout@v2
- name: "Install PHP with extensions"
uses: shivammathur/setup-php@v2
with:
coverage: "none"
php-version: ${{ matrix.php-version }}
ini-values: memory_limit=-1
- name: "Add PHPUnit matcher"
run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
- run: composer install
- name: "Install PHPUnit"
run: vendor/bin/simple-phpunit install
- name: "PHPUnit version"
run: vendor/bin/simple-phpunit --version
- name: "Run tests"
run: vendor/bin/simple-phpunit
extension-tests:
needs:
- 'tests'
name: "${{ matrix.extension }} with PHP ${{ matrix.php-version }}"
runs-on: 'ubuntu-latest'
continue-on-error: true
strategy:
matrix:
php-version:
- '7.2.5'
- '7.3'
- '7.4'
- '8.0'
- '8.1'
extension:
- 'extra/cache-extra'
- 'extra/cssinliner-extra'
- 'extra/html-extra'
- 'extra/inky-extra'
- 'extra/intl-extra'
- 'extra/markdown-extra'
- 'extra/string-extra'
- 'extra/twig-extra-bundle'
experimental: [false]
steps:
- name: "Checkout code"
uses: actions/checkout@v2
- name: "Install PHP with extensions"
uses: shivammathur/setup-php@v2
with:
coverage: "none"
php-version: ${{ matrix.php-version }}
ini-values: memory_limit=-1
- name: "Add PHPUnit matcher"
run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
- run: composer install
- name: "Install PHPUnit"
run: vendor/bin/simple-phpunit install
- name: "PHPUnit version"
run: vendor/bin/simple-phpunit --version
- name: "Composer install"
working-directory: ${{ matrix.extension}}
run: composer install
- name: "Run tests"
working-directory: ${{ matrix.extension}}
run: ../../vendor/bin/simple-phpunit
#
# Drupal does not support Twig 3 now!
#
# integration-tests:
# needs:
# - 'tests'
#
# name: "Integration tests with PHP ${{ matrix.php-version }}"
#
# runs-on: 'ubuntu-20.04'
#
# continue-on-error: true
#
# strategy:
# matrix:
# php-version:
# - '7.3'
#
# steps:
# - name: "Checkout code"
# uses: actions/checkout@v2
#
# - name: "Install PHP with extensions"
# uses: shivammathur/setup-php@2
# with:
# coverage: "none"
# extensions: "gd, pdo_sqlite"
# php-version: ${{ matrix.php-version }}
# ini-values: memory_limit=-1
# tools: composer:v2
#
# - run: bash ./tests/drupal_test.sh
# shell: "bash"

View File

@@ -1,64 +0,0 @@
name: "Documentation"
on:
pull_request:
push:
branches:
- '2.x'
- '3.x'
permissions:
contents: read
jobs:
build:
name: "Build"
runs-on: ubuntu-latest
steps:
- name: "Checkout code"
uses: actions/checkout@v2
- name: "Set-up PHP"
uses: shivammathur/setup-php@v2
with:
php-version: 8.1
coverage: none
tools: "composer:v2"
- name: Get composer cache directory
id: composercache
working-directory: doc/_build
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- name: Cache dependencies
uses: actions/cache@v2
with:
path: ${{ steps.composercache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-
- name: "Install dependencies"
working-directory: doc/_build
run: composer install --prefer-dist --no-progress
- name: "Build the docs"
working-directory: doc/_build
run: php build.php --disable-cache
doctor-rst:
name: "DOCtor-RST"
runs-on: ubuntu-latest
steps:
- name: "Checkout code"
uses: actions/checkout@v2
- name: "Run DOCtor-RST"
uses: docker://oskarstark/doctor-rst
with:
args: --short
env:
DOCS_DIR: 'doc/'

View File

@@ -1,6 +0,0 @@
/doc/_build/vendor
/doc/_build/output
/composer.lock
/phpunit.xml
/vendor
.phpunit.result.cache

View File

@@ -1,20 +0,0 @@
<?php
return (new PhpCsFixer\Config())
->setRules([
'@Symfony' => true,
'@Symfony:risky' => true,
'@PHPUnit75Migration:risky' => true,
'php_unit_dedicate_assert' => ['target' => '5.6'],
'array_syntax' => ['syntax' => 'short'],
'php_unit_fqcn_annotation' => true,
'no_unreachable_default_argument_value' => false,
'braces' => ['allow_single_line_closure' => true],
'heredoc_to_nowdoc' => false,
'ordered_imports' => true,
'phpdoc_types_order' => ['null_adjustment' => 'always_last', 'sort_algorithm' => 'none'],
'native_function_invocation' => ['include' => ['@compiler_optimized'], 'scope' => 'all'],
])
->setRiskyAllowed(true)
->setFinder((new PhpCsFixer\Finder())->in(__DIR__))
;

View File

@@ -1,3 +1,48 @@
# 3.8.0 (2023-11-21)
* Catch errors thrown during template rendering
* Fix IntlExtension::formatDateTime use of date formatter prototype
* Fix premature loop exit in Security Policy lookup of allowed methods/properties
* Remove NumberFormatter::TYPE_CURRENCY (deprecated in PHP 8.3)
* Restore return type annotations
* Allow Symfony 7 packages to be installed
* Deprecate `twig_test_iterable` function. Use the native `is_iterable` instead.
# 3.7.1 (2023-08-28)
* Fix some phpdocs
# 3.7.0 (2023-07-26)
* Add support for the ...spread operator on arrays and hashes
# 3.6.1 (2023-06-08)
* Suppress some native return type deprecation messages
# 3.6.0 (2023-05-03)
* Allow psr/container 2.0
* Add the new PHP 8.0 IntlDateFormatter::RELATIVE_* constants for date formatting
* Make the Lexer initialize itself lazily
# 3.5.1 (2023-02-08)
* Arrow functions passed to the "reduce" filter now accept the current key as a third argument
* Restores the leniency of the matches twig comparison
* Fix error messages in sandboxed mode for "has some" and "has every"
# 3.5.0 (2022-12-27)
* Make Twig\ExpressionParser non-internal
* Add "has some" and "has every" operators
* Add Compile::reset()
* Throw a better runtime error when the "matches" regexp is not valid
* Add "twig *_names" intl functions
* Fix optimizing closures callbacks
* Add a better exception when getting an undefined constant via `constant`
* Fix `if` nodes when outside of a block and with an empty body
# 3.4.3 (2022-09-28)
* Fix a security issue on filesystem loader (possibility to load a template outside a configured directory)

View File

@@ -1,4 +1,4 @@
Copyright (c) 2009-2022 by the Twig Team.
Copyright (c) 2009-present by the Twig Team.
All rights reserved.

View File

@@ -25,12 +25,13 @@
],
"require": {
"php": ">=7.2.5",
"symfony/polyfill-php80": "^1.22",
"symfony/polyfill-mbstring": "^1.3",
"symfony/polyfill-ctype": "^1.8"
},
"require-dev": {
"symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0",
"psr/container": "^1.0"
"symfony/phpunit-bridge": "^5.4.9|^6.3|^7.0",
"psr/container": "^1.0|^2.0"
},
"autoload": {
"psr-4" : {
@@ -41,10 +42,5 @@
"psr-4" : {
"Twig\\Tests\\" : "tests/"
}
},
"extra": {
"branch-alias": {
"dev-master": "3.4-dev"
}
}
}

View File

@@ -63,7 +63,7 @@ class FilesystemCache implements CacheInterface
if (self::FORCE_BYTECODE_INVALIDATION == ($this->options & self::FORCE_BYTECODE_INVALIDATION)) {
// Compile cached file into bytecode cache
if (\function_exists('opcache_invalidate') && filter_var(ini_get('opcache.enable'), \FILTER_VALIDATE_BOOLEAN)) {
if (\function_exists('opcache_invalidate') && filter_var(\ini_get('opcache.enable'), \FILTER_VALIDATE_BOOLEAN)) {
@opcache_invalidate($key, true);
} elseif (\function_exists('apc_compile_file')) {
apc_compile_file($key);

View File

@@ -46,7 +46,7 @@ class Compiler
/**
* @return $this
*/
public function compile(Node $node, int $indentation = 0)
public function reset(int $indentation = 0)
{
$this->lastLine = null;
$this->source = '';
@@ -57,6 +57,15 @@ class Compiler
$this->indentation = $indentation;
$this->varNameSalt = 0;
return $this;
}
/**
* @return $this
*/
public function compile(Node $node, int $indentation = 0)
{
$this->reset($indentation);
$node->compile($this);
return $this;

View File

@@ -25,6 +25,8 @@ use Twig\Extension\OptimizerExtension;
use Twig\Loader\ArrayLoader;
use Twig\Loader\ChainLoader;
use Twig\Loader\LoaderInterface;
use Twig\Node\Expression\Binary\AbstractBinary;
use Twig\Node\Expression\Unary\AbstractUnary;
use Twig\Node\ModuleNode;
use Twig\Node\Node;
use Twig\NodeVisitor\NodeVisitorInterface;
@@ -38,11 +40,11 @@ use Twig\TokenParser\TokenParserInterface;
*/
class Environment
{
public const VERSION = '3.4.3';
public const VERSION_ID = 30403;
public const VERSION = '3.8.0';
public const VERSION_ID = 30800;
public const MAJOR_VERSION = 3;
public const MINOR_VERSION = 4;
public const RELEASE_VERSION = 3;
public const MINOR_VERSION = 8;
public const RELEASE_VERSION = 0;
public const EXTRA_VERSION = '';
private $charset;
@@ -53,6 +55,7 @@ class Environment
private $lexer;
private $parser;
private $compiler;
/** @var array<string, mixed> */
private $globals = [];
private $resolvedGlobals;
private $loadedTemplates;
@@ -342,7 +345,6 @@ class Environment
$this->cache->load($key);
}
$source = null;
if (!class_exists($cls, false)) {
$source = $this->getLoader()->getSourceContext($name);
$content = $this->compileSource($source);
@@ -775,6 +777,8 @@ class Environment
/**
* @internal
*
* @return array<string, mixed>
*/
public function getGlobals(): array
{
@@ -804,6 +808,8 @@ class Environment
/**
* @internal
*
* @return array<string, array{precedence: int, class: class-string<AbstractUnary>}>
*/
public function getUnaryOperators(): array
{
@@ -812,6 +818,8 @@ class Environment
/**
* @internal
*
* @return array<string, array{precedence: int, class: class-string<AbstractBinary>, associativity: ExpressionParser::OPERATOR_*}>
*/
public function getBinaryOperators(): array
{

View File

@@ -53,7 +53,7 @@ class Error extends \Exception
* @param int $lineno The template line where the error occurred
* @param Source|null $source The source context where the error occurred
*/
public function __construct(string $message, int $lineno = -1, Source $source = null, \Exception $previous = null)
public function __construct(string $message, int $lineno = -1, Source $source = null, \Throwable $previous = null)
{
parent::__construct('', 0, $previous);
@@ -130,13 +130,13 @@ class Error extends \Exception
}
$dot = false;
if ('.' === substr($this->message, -1)) {
if (str_ends_with($this->message, '.')) {
$this->message = substr($this->message, 0, -1);
$dot = true;
}
$questionMark = false;
if ('?' === substr($this->message, -1)) {
if (str_ends_with($this->message, '?')) {
$this->message = substr($this->message, 0, -1);
$questionMark = true;
}
@@ -172,7 +172,7 @@ class Error extends \Exception
foreach ($backtrace as $trace) {
if (isset($trace['object']) && $trace['object'] instanceof Template) {
$currentClass = \get_class($trace['object']);
$isEmbedContainer = null === $templateClass ? false : 0 === strpos($templateClass, $currentClass);
$isEmbedContainer = null === $templateClass ? false : str_starts_with($templateClass, $currentClass);
if (null === $this->name || ($this->name == $trace['object']->getTemplateName() && !$isEmbedContainer)) {
$template = $trace['object'];
$templateClass = \get_class($trace['object']);

View File

@@ -30,7 +30,7 @@ class SyntaxError extends Error
$alternatives = [];
foreach ($items as $item) {
$lev = levenshtein($name, $item);
if ($lev <= \strlen($name) / 3 || false !== strpos($item, $name)) {
if ($lev <= \strlen($name) / 3 || str_contains($item, $name)) {
$alternatives[$item] = $lev;
}
}

View File

@@ -17,6 +17,7 @@ use Twig\Node\Expression\AbstractExpression;
use Twig\Node\Expression\ArrayExpression;
use Twig\Node\Expression\ArrowFunctionExpression;
use Twig\Node\Expression\AssignNameExpression;
use Twig\Node\Expression\Binary\AbstractBinary;
use Twig\Node\Expression\Binary\ConcatBinary;
use Twig\Node\Expression\BlockReferenceExpression;
use Twig\Node\Expression\ConditionalExpression;
@@ -26,6 +27,7 @@ use Twig\Node\Expression\MethodCallExpression;
use Twig\Node\Expression\NameExpression;
use Twig\Node\Expression\ParentExpression;
use Twig\Node\Expression\TestExpression;
use Twig\Node\Expression\Unary\AbstractUnary;
use Twig\Node\Expression\Unary\NegUnary;
use Twig\Node\Expression\Unary\NotUnary;
use Twig\Node\Expression\Unary\PosUnary;
@@ -40,8 +42,6 @@ use Twig\Node\Node;
* @see https://en.wikipedia.org/wiki/Operator-precedence_parser
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @internal
*/
class ExpressionParser
{
@@ -50,7 +50,9 @@ class ExpressionParser
private $parser;
private $env;
/** @var array<string, array{precedence: int, class: class-string<AbstractUnary>}> */
private $unaryOperators;
/** @var array<string, array{precedence: int, class: class-string<AbstractBinary>, associativity: self::OPERATOR_*}> */
private $binaryOperators;
public function __construct(Parser $parser, Environment $env)
@@ -80,7 +82,7 @@ class ExpressionParser
} elseif (isset($op['callable'])) {
$expr = $op['callable']($this->parser, $expr);
} else {
$expr1 = $this->parseExpression(self::OPERATOR_LEFT === $op['associativity'] ? $op['precedence'] + 1 : $op['precedence']);
$expr1 = $this->parseExpression(self::OPERATOR_LEFT === $op['associativity'] ? $op['precedence'] + 1 : $op['precedence'], true);
$class = $op['class'];
$expr = new $class($expr, $expr1, $token->getLine());
}
@@ -181,11 +183,14 @@ class ExpressionParser
if (!$this->parser->getStream()->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ':')) {
$expr2 = $this->parseExpression();
if ($this->parser->getStream()->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ':')) {
// Ternary operator (expr ? expr2 : expr3)
$expr3 = $this->parseExpression();
} else {
// Ternary without else (expr ? expr2)
$expr3 = new ConstantExpression('', $this->parser->getCurrentToken()->getLine());
}
} else {
// Ternary without then (expr ?: expr3)
$expr2 = $expr;
$expr3 = $this->parseExpression();
}
@@ -332,7 +337,14 @@ class ExpressionParser
}
$first = false;
$node->addElement($this->parseExpression());
if ($stream->test(/* Token::SPREAD_TYPE */ 13)) {
$stream->next();
$expr = $this->parseExpression();
$expr->setAttribute('spread', true);
$node->addElement($expr);
} else {
$node->addElement($this->parseExpression());
}
}
$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ']', 'An opened array is not properly closed');
@@ -357,6 +369,14 @@ class ExpressionParser
}
$first = false;
if ($stream->test(/* Token::SPREAD_TYPE */ 13)) {
$stream->next();
$value = $this->parseExpression();
$value->setAttribute('spread', true);
$node->addElement($value);
continue;
}
// a hash key can be:
//
// * a number -- 12
@@ -489,10 +509,6 @@ class ExpressionParser
}
if ($node instanceof NameExpression && null !== $this->parser->getImportedSymbol('template', $node->getAttribute('name'))) {
if (!$arg instanceof ConstantExpression) {
throw new SyntaxError(sprintf('Dynamic macro names are not supported (called on "%s").', $node->getAttribute('name')), $token->getLine(), $stream->getSourceContext());
}
$name = $arg->getAttribute('value');
$node = new MethodCallExpression($node, 'macro_'.$name, $arguments, $lineno);

View File

@@ -23,6 +23,8 @@ use Twig\Node\Expression\Binary\EqualBinary;
use Twig\Node\Expression\Binary\FloorDivBinary;
use Twig\Node\Expression\Binary\GreaterBinary;
use Twig\Node\Expression\Binary\GreaterEqualBinary;
use Twig\Node\Expression\Binary\HasEveryBinary;
use Twig\Node\Expression\Binary\HasSomeBinary;
use Twig\Node\Expression\Binary\InBinary;
use Twig\Node\Expression\Binary\LessBinary;
use Twig\Node\Expression\Binary\LessEqualBinary;
@@ -249,7 +251,7 @@ final class CoreExtension extends AbstractExtension
new TwigTest('divisible by', null, ['node_class' => DivisiblebyTest::class, 'one_mandatory_argument' => true]),
new TwigTest('constant', null, ['node_class' => ConstantTest::class]),
new TwigTest('empty', 'twig_test_empty'),
new TwigTest('iterable', 'twig_test_iterable'),
new TwigTest('iterable', 'is_iterable'),
];
}
@@ -284,6 +286,8 @@ final class CoreExtension extends AbstractExtension
'matches' => ['precedence' => 20, 'class' => MatchesBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
'starts with' => ['precedence' => 20, 'class' => StartsWithBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
'ends with' => ['precedence' => 20, 'class' => EndsWithBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
'has some' => ['precedence' => 20, 'class' => HasSomeBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
'has every' => ['precedence' => 20, 'class' => HasEveryBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
'..' => ['precedence' => 25, 'class' => RangeBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
'+' => ['precedence' => 30, 'class' => AddBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
'-' => ['precedence' => 30, 'class' => SubBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT],
@@ -339,9 +343,9 @@ function twig_cycle($values, $position)
* @param \Traversable|array|int|float|string $values The values to pick a random item from
* @param int|null $max Maximum value used when $values is an int
*
* @throws RuntimeError when $values is an empty array (does not apply to an empty string which is returned as is)
*
* @return mixed A random value from the given sequence
*
* @throws RuntimeError when $values is an empty array (does not apply to an empty string which is returned as is)
*/
function twig_random(Environment $env, $values = null, $max = null)
{
@@ -360,7 +364,6 @@ function twig_random(Environment $env, $values = null, $max = null)
}
} else {
$min = $values;
$max = $max;
}
return mt_rand((int) $min, (int) $max);
@@ -388,7 +391,7 @@ function twig_random(Environment $env, $values = null, $max = null)
}
}
if (!twig_test_iterable($values)) {
if (!is_iterable($values)) {
return $values;
}
@@ -525,7 +528,7 @@ function twig_date_converter(Environment $env, $date = null, $timezone = null)
*/
function twig_replace_filter($str, $from)
{
if (!twig_test_iterable($from)) {
if (!is_iterable($from)) {
throw new RuntimeError(sprintf('The "replace" filter expects an array or "Traversable" as replace values, got "%s".', \is_object($from) ? \get_class($from) : \gettype($from)));
}
@@ -605,32 +608,34 @@ function twig_urlencode_filter($url)
}
/**
* Merges an array with another one.
* Merges any number of arrays or Traversable objects.
*
* {% set items = { 'apple': 'fruit', 'orange': 'fruit' } %}
*
* {% set items = items|merge({ 'peugeot': 'car' }) %}
* {% set items = items|merge({ 'peugeot': 'car' }, { 'banana': 'fruit' }) %}
*
* {# items now contains { 'apple': 'fruit', 'orange': 'fruit', 'peugeot': 'car' } #}
* {# items now contains { 'apple': 'fruit', 'orange': 'fruit', 'peugeot': 'car', 'banana': 'fruit' } #}
*
* @param array|\Traversable $arr1 An array
* @param array|\Traversable $arr2 An array
* @param array|\Traversable ...$arrays Any number of arrays or Traversable objects to merge
*
* @return array The merged array
*/
function twig_array_merge($arr1, $arr2)
function twig_array_merge(...$arrays)
{
if (!twig_test_iterable($arr1)) {
throw new RuntimeError(sprintf('The merge filter only works with arrays or "Traversable", got "%s" as first argument.', \gettype($arr1)));
$result = [];
foreach ($arrays as $argNumber => $array) {
if (!is_iterable($array)) {
throw new RuntimeError(sprintf('The merge filter only works with arrays or "Traversable", got "%s" for argument %d.', \gettype($array), $argNumber + 1));
}
$result = array_merge($result, twig_to_array($array));
}
if (!twig_test_iterable($arr2)) {
throw new RuntimeError(sprintf('The merge filter only works with arrays or "Traversable", got "%s" as second argument.', \gettype($arr2)));
}
return array_merge(twig_to_array($arr1), twig_to_array($arr2));
return $result;
}
/**
* Slices a variable.
*
@@ -650,7 +655,7 @@ function twig_slice(Environment $env, $item, $start, $length = null, $preserveKe
if ($start >= 0 && $length >= 0 && $item instanceof \Iterator) {
try {
return iterator_to_array(new \LimitIterator($item, $start, null === $length ? -1 : $length), $preserveKeys);
return iterator_to_array(new \LimitIterator($item, $start, $length ?? -1), $preserveKeys);
} catch (\OutOfBoundsException $e) {
return [];
}
@@ -663,7 +668,7 @@ function twig_slice(Environment $env, $item, $start, $length = null, $preserveKe
return \array_slice($item, $start, $length, $preserveKeys);
}
return (string) mb_substr((string) $item, $start, $length, $env->getCharset());
return mb_substr((string) $item, $start, $length, $env->getCharset());
}
/**
@@ -716,7 +721,7 @@ function twig_last(Environment $env, $item)
*/
function twig_join_filter($value, $glue = '', $and = null)
{
if (!twig_test_iterable($value)) {
if (!is_iterable($value)) {
$value = (array) $value;
}
@@ -762,7 +767,7 @@ function twig_split_filter(Environment $env, $value, $delimiter, $limit = null)
{
$value = $value ?? '';
if (\strlen($delimiter) > 0) {
if ('' !== $delimiter) {
return null === $limit ? explode($delimiter, $value) : explode($delimiter, $value, $limit);
}
@@ -920,7 +925,7 @@ function twig_in_filter($value, $compare)
if (\is_string($compare)) {
if (\is_string($value) || \is_int($value) || \is_float($value)) {
return '' === $value || false !== strpos($compare, (string) $value);
return '' === $value || str_contains($compare, (string) $value);
}
return false;
@@ -1015,6 +1020,23 @@ function twig_compare($a, $b)
return $a <=> $b;
}
/**
* @return int
*
* @throws RuntimeError When an invalid pattern is used
*/
function twig_matches(string $regexp, ?string $str)
{
set_error_handler(function ($t, $m) use ($regexp) {
throw new RuntimeError(sprintf('Regexp "%s" passed to "matches" is not valid', $regexp).substr($m, 12));
});
try {
return preg_match($regexp, $str ?? '');
} finally {
restore_error_handler();
}
}
/**
* Returns a trimmed string.
*
@@ -1097,7 +1119,7 @@ function twig_length_filter(Environment $env, $thing)
return 0;
}
if (is_scalar($thing)) {
if (\is_scalar($thing)) {
return mb_strlen($thing, $env->getCharset());
}
@@ -1207,7 +1229,7 @@ function twig_call_macro(Template $template, string $method, array $args, int $l
*/
function twig_ensure_traversable($seq)
{
if ($seq instanceof \Traversable || \is_array($seq)) {
if (is_iterable($seq)) {
return $seq;
}
@@ -1270,21 +1292,23 @@ function twig_test_empty($value)
* @param mixed $value A variable
*
* @return bool true if the value is traversable
*
* @deprecated since Twig 3.8, to be removed in 4.0 (use the native "is_iterable" function instead)
*/
function twig_test_iterable($value)
{
return $value instanceof \Traversable || \is_array($value);
return is_iterable($value);
}
/**
* Renders a template.
*
* @param array $context
* @param string|array $template The template to render or an array of templates to try consecutively
* @param array $variables The variables to pass to the template
* @param bool $withContext
* @param bool $ignoreMissing Whether to ignore missing templates or not
* @param bool $sandboxed Whether to sandbox the template or not
* @param array $context
* @param string|array|TemplateWrapper $template The template to render or an array of templates to try consecutively
* @param array $variables The variables to pass to the template
* @param bool $withContext
* @param bool $ignoreMissing Whether to ignore missing templates or not
* @param bool $sandboxed Whether to sandbox the template or not
*
* @return string The rendered template
*/
@@ -1366,6 +1390,10 @@ function twig_constant($constant, $object = null)
$constant = \get_class($object).'::'.$constant;
}
if (!\defined($constant)) {
throw new RuntimeError(sprintf('Constant "%s" is undefined.', $constant));
}
return \constant($constant);
}
@@ -1401,7 +1429,7 @@ function twig_constant_is_defined($constant, $object = null)
*/
function twig_array_batch($items, $size, $fill = null, $preserveKeys = true)
{
if (!twig_test_iterable($items)) {
if (!is_iterable($items)) {
throw new RuntimeError(sprintf('The "batch" filter expects an array or "Traversable", got "%s".', \is_object($items) ? \get_class($items) : \gettype($items)));
}
@@ -1543,13 +1571,13 @@ function twig_get_attribute(Environment $env, Source $source, $object, $item, ar
$classCache[$method] = $method;
$classCache[$lcName = $lcMethods[$i]] = $method;
if ('g' === $lcName[0] && 0 === strpos($lcName, 'get')) {
if ('g' === $lcName[0] && str_starts_with($lcName, 'get')) {
$name = substr($method, 3);
$lcName = substr($lcName, 3);
} elseif ('i' === $lcName[0] && 0 === strpos($lcName, 'is')) {
} elseif ('i' === $lcName[0] && str_starts_with($lcName, 'is')) {
$name = substr($method, 2);
$lcName = substr($lcName, 2);
} elseif ('h' === $lcName[0] && 0 === strpos($lcName, 'has')) {
} elseif ('h' === $lcName[0] && str_starts_with($lcName, 'has')) {
$name = substr($method, 3);
$lcName = substr($lcName, 3);
if (\in_array('is'.$lcName, $lcMethods)) {
@@ -1645,7 +1673,7 @@ function twig_array_column($array, $name, $index = null): array
function twig_array_filter(Environment $env, $array, $arrow)
{
if (!twig_test_iterable($array)) {
if (!is_iterable($array)) {
throw new RuntimeError(sprintf('The "filter" filter expects an array or "Traversable", got "%s".', \is_object($array) ? \get_class($array) : \gettype($array)));
}
@@ -1675,15 +1703,42 @@ function twig_array_reduce(Environment $env, $array, $arrow, $initial = null)
{
twig_check_arrow_in_sandbox($env, $arrow, 'reduce', 'filter');
if (!\is_array($array)) {
if (!$array instanceof \Traversable) {
throw new RuntimeError(sprintf('The "reduce" filter only works with arrays or "Traversable", got "%s" as first argument.', \gettype($array)));
}
$array = iterator_to_array($array);
if (!\is_array($array) && !$array instanceof \Traversable) {
throw new RuntimeError(sprintf('The "reduce" filter only works with arrays or "Traversable", got "%s" as first argument.', \gettype($array)));
}
return array_reduce($array, $arrow, $initial);
$accumulator = $initial;
foreach ($array as $key => $value) {
$accumulator = $arrow($accumulator, $value, $key);
}
return $accumulator;
}
function twig_array_some(Environment $env, $array, $arrow)
{
twig_check_arrow_in_sandbox($env, $arrow, 'has some', 'operator');
foreach ($array as $k => $v) {
if ($arrow($v, $k)) {
return true;
}
}
return false;
}
function twig_array_every(Environment $env, $array, $arrow)
{
twig_check_arrow_in_sandbox($env, $arrow, 'has every', 'operator');
foreach ($array as $k => $v) {
if (!$arrow($v, $k)) {
return false;
}
}
return true;
}
function twig_check_arrow_in_sandbox(Environment $env, $arrow, $thing, $type)

View File

@@ -19,10 +19,10 @@ final class DebugExtension extends AbstractExtension
// dump is safe if var_dump is overridden by xdebug
$isDumpOutputHtmlSafe = \extension_loaded('xdebug')
// false means that it was not set (and the default is on) or it explicitly enabled
&& (false === ini_get('xdebug.overload_var_dump') || ini_get('xdebug.overload_var_dump'))
&& (false === \ini_get('xdebug.overload_var_dump') || \ini_get('xdebug.overload_var_dump'))
// false means that it was not set (and the default is on) or it explicitly enabled
// xdebug.overload_var_dump produces HTML only when html_errors is also enabled
&& (false === ini_get('html_errors') || ini_get('html_errors'))
&& (false === \ini_get('html_errors') || \ini_get('html_errors'))
|| 'cli' === \PHP_SAPI
;

View File

@@ -341,7 +341,7 @@ function twig_escape_filter(Environment $env, $string, $strategy = 'html', $char
* The following replaces characters undefined in HTML with the
* hex entity for the Unicode replacement character.
*/
if (($ord <= 0x1f && "\t" != $chr && "\n" != $chr && "\r" != $chr) || ($ord >= 0x7f && $ord <= 0x9f)) {
if (($ord <= 0x1F && "\t" != $chr && "\n" != $chr && "\r" != $chr) || ($ord >= 0x7F && $ord <= 0x9F)) {
return '&#xFFFD;';
}
@@ -388,7 +388,7 @@ function twig_escape_filter(Environment $env, $string, $strategy = 'html', $char
default:
$escapers = $env->getExtension(EscaperExtension::class)->getEscapers();
if (array_key_exists($strategy, $escapers)) {
if (\array_key_exists($strategy, $escapers)) {
return $escapers[$strategy]($env, $string, $charset);
}

View File

@@ -11,6 +11,9 @@
namespace Twig\Extension;
use Twig\ExpressionParser;
use Twig\Node\Expression\Binary\AbstractBinary;
use Twig\Node\Expression\Unary\AbstractUnary;
use Twig\NodeVisitor\NodeVisitorInterface;
use Twig\TokenParser\TokenParserInterface;
use Twig\TwigFilter;
@@ -63,6 +66,11 @@ interface ExtensionInterface
* Returns a list of operators to add to the existing list.
*
* @return array<array> First array of unary operators, second array of binary operators
*
* @psalm-return array{
* array<string, array{precedence: int, class: class-string<AbstractUnary>}>,
* array<string, array{precedence: int, class: class-string<AbstractBinary>, associativity: ExpressionParser::OPERATOR_*}>
* }
*/
public function getOperators();
}

View File

@@ -21,5 +21,8 @@ namespace Twig\Extension;
*/
interface GlobalsInterface
{
/**
* @return array<string, mixed>
*/
public function getGlobals(): array;
}

View File

@@ -15,6 +15,8 @@ use Twig\Error\RuntimeError;
use Twig\Extension\ExtensionInterface;
use Twig\Extension\GlobalsInterface;
use Twig\Extension\StagingExtension;
use Twig\Node\Expression\Binary\AbstractBinary;
use Twig\Node\Expression\Unary\AbstractUnary;
use Twig\NodeVisitor\NodeVisitorInterface;
use Twig\TokenParser\TokenParserInterface;
@@ -31,11 +33,17 @@ final class ExtensionSet
private $staging;
private $parsers;
private $visitors;
/** @var array<string, TwigFilter> */
private $filters;
/** @var array<string, TwigTest> */
private $tests;
/** @var array<string, TwigFunction> */
private $functions;
/** @var array<string, array{precedence: int, class: class-string<AbstractUnary>}> */
private $unaryOperators;
/** @var array<string, array{precedence: int, class: class-string<AbstractBinary>, associativity: ExpressionParser::OPERATOR_*}> */
private $binaryOperators;
/** @var array<string, mixed> */
private $globals;
private $functionCallbacks = [];
private $filterCallbacks = [];
@@ -305,6 +313,9 @@ final class ExtensionSet
$this->parserCallbacks[] = $callable;
}
/**
* @return array<string, mixed>
*/
public function getGlobals(): array
{
if (null !== $this->globals) {
@@ -379,6 +390,9 @@ final class ExtensionSet
return null;
}
/**
* @return array<string, array{precedence: int, class: class-string<AbstractUnary>}>
*/
public function getUnaryOperators(): array
{
if (!$this->initialized) {
@@ -388,6 +402,9 @@ final class ExtensionSet
return $this->unaryOperators;
}
/**
* @return array<string, array{precedence: int, class: class-string<AbstractBinary>, associativity: ExpressionParser::OPERATOR_*}>
*/
public function getBinaryOperators(): array
{
if (!$this->initialized) {

View File

@@ -37,7 +37,7 @@ class FileExtensionEscapingStrategy
return 'html'; // return html for directories
}
if ('.twig' === substr($name, -5)) {
if (str_ends_with($name, '.twig')) {
$name = substr($name, 0, -5);
}

View File

@@ -19,6 +19,8 @@ use Twig\Error\SyntaxError;
*/
class Lexer
{
private $isInitialized = false;
private $tokens;
private $code;
private $cursor;
@@ -61,6 +63,15 @@ class Lexer
'whitespace_line_chars' => ' \t\0\x0B',
'interpolation' => ['#{', '}'],
], $options);
}
private function initialize()
{
if ($this->isInitialized) {
return;
}
$this->isInitialized = true;
// when PHP 7.3 is the min version, we will be able to remove the '#' part in preg_quote as it's part of the default
$this->regexes = [
@@ -153,6 +164,8 @@ class Lexer
public function tokenize(Source $source): TokenStream
{
$this->initialize();
$this->source = $source;
$this->code = str_replace(["\r\n", "\r"], "\n", $source->getCode());
$this->cursor = 0;
@@ -302,8 +315,13 @@ class Lexer
}
}
// spread operator
if ('.' === $this->code[$this->cursor] && ($this->cursor + 2 < $this->end) && '.' === $this->code[$this->cursor + 1] && '.' === $this->code[$this->cursor + 2]) {
$this->pushToken(Token::SPREAD_TYPE, '...');
$this->moveCursor('...');
}
// arrow function
if ('=' === $this->code[$this->cursor] && '>' === $this->code[$this->cursor + 1]) {
elseif ('=' === $this->code[$this->cursor] && '>' === $this->code[$this->cursor + 1]) {
$this->pushToken(Token::ARROW_TYPE, '=>');
$this->moveCursor('=>');
}
@@ -327,13 +345,13 @@ class Lexer
$this->moveCursor($match[0]);
}
// punctuation
elseif (false !== strpos(self::PUNCTUATION, $this->code[$this->cursor])) {
elseif (str_contains(self::PUNCTUATION, $this->code[$this->cursor])) {
// opening bracket
if (false !== strpos('([{', $this->code[$this->cursor])) {
if (str_contains('([{', $this->code[$this->cursor])) {
$this->brackets[] = [$this->code[$this->cursor], $this->lineno];
}
// closing bracket
elseif (false !== strpos(')]}', $this->code[$this->cursor])) {
elseif (str_contains(')]}', $this->code[$this->cursor])) {
if (empty($this->brackets)) {
throw new SyntaxError(sprintf('Unexpected "%s".', $this->code[$this->cursor]), $this->lineno, $this->source);
}
@@ -404,7 +422,7 @@ class Lexer
$this->pushToken(/* Token::INTERPOLATION_START_TYPE */ 10);
$this->moveCursor($match[0]);
$this->pushState(self::STATE_INTERPOLATION);
} elseif (preg_match(self::REGEX_DQ_STRING_PART, $this->code, $match, 0, $this->cursor) && \strlen($match[0]) > 0) {
} elseif (preg_match(self::REGEX_DQ_STRING_PART, $this->code, $match, 0, $this->cursor) && '' !== $match[0]) {
$this->pushToken(/* Token::STRING_TYPE */ 7, stripcslashes($match[0]));
$this->moveCursor($match[0]);
} elseif (preg_match(self::REGEX_DQ_STRING_DELIM, $this->code, $match, 0, $this->cursor)) {

View File

@@ -36,7 +36,7 @@ class FilesystemLoader implements LoaderInterface
*/
public function __construct($paths = [], string $rootPath = null)
{
$this->rootPath = (null === $rootPath ? getcwd() : $rootPath).\DIRECTORY_SEPARATOR;
$this->rootPath = ($rootPath ?? getcwd()).\DIRECTORY_SEPARATOR;
if (null !== $rootPath && false !== ($realPath = realpath($rootPath))) {
$this->rootPath = $realPath.\DIRECTORY_SEPARATOR;
}
@@ -250,7 +250,7 @@ class FilesystemLoader implements LoaderInterface
private function validateName(string $name): void
{
if (false !== strpos($name, "\0")) {
if (str_contains($name, "\0")) {
throw new LoaderError('A template name cannot contain NUL bytes.');
}

View File

@@ -66,20 +66,70 @@ class ArrayExpression extends AbstractExpression
public function compile(Compiler $compiler): void
{
$keyValuePairs = $this->getKeyValuePairs();
$needsArrayMergeSpread = \PHP_VERSION_ID < 80100 && $this->hasSpreadItem($keyValuePairs);
if ($needsArrayMergeSpread) {
$compiler->raw('twig_array_merge(');
}
$compiler->raw('[');
$first = true;
foreach ($this->getKeyValuePairs() as $pair) {
$reopenAfterMergeSpread = false;
$nextIndex = 0;
foreach ($keyValuePairs as $pair) {
if ($reopenAfterMergeSpread) {
$compiler->raw(', [');
$reopenAfterMergeSpread = false;
}
if ($needsArrayMergeSpread && $pair['value']->hasAttribute('spread')) {
$compiler->raw('], ')->subcompile($pair['value']);
$first = true;
$reopenAfterMergeSpread = true;
continue;
}
if (!$first) {
$compiler->raw(', ');
}
$first = false;
$compiler
->subcompile($pair['key'])
->raw(' => ')
->subcompile($pair['value'])
;
if ($pair['value']->hasAttribute('spread') && !$needsArrayMergeSpread) {
$compiler->raw('...')->subcompile($pair['value']);
++$nextIndex;
} else {
$key = $pair['key'] instanceof ConstantExpression ? $pair['key']->getAttribute('value') : null;
if ($nextIndex !== $key) {
if (\is_int($key)) {
$nextIndex = $key + 1;
}
$compiler
->subcompile($pair['key'])
->raw(' => ')
;
} else {
++$nextIndex;
}
$compiler->subcompile($pair['value']);
}
}
$compiler->raw(']');
if (!$reopenAfterMergeSpread) {
$compiler->raw(']');
}
if ($needsArrayMergeSpread) {
$compiler->raw(')');
}
}
private function hasSpreadItem(array $pairs): bool
{
foreach ($pairs as $pair) {
if ($pair['value']->hasAttribute('spread')) {
return true;
}
}
return false;
}
}

View File

@@ -24,7 +24,7 @@ class EndsWithBinary extends AbstractBinary
->subcompile($this->getNode('left'))
->raw(sprintf(') && is_string($%s = ', $right))
->subcompile($this->getNode('right'))
->raw(sprintf(') && (\'\' === $%2$s || $%2$s === substr($%1$s, -strlen($%2$s))))', $left, $right))
->raw(sprintf(') && str_ends_with($%1$s, $%2$s))', $left, $right))
;
}

View File

@@ -0,0 +1,33 @@
<?php
/*
* This file is part of Twig.
*
* (c) Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Twig\Node\Expression\Binary;
use Twig\Compiler;
class HasEveryBinary extends AbstractBinary
{
public function compile(Compiler $compiler): void
{
$compiler
->raw('twig_array_every($this->env, ')
->subcompile($this->getNode('left'))
->raw(', ')
->subcompile($this->getNode('right'))
->raw(')')
;
}
public function operator(Compiler $compiler): Compiler
{
return $compiler->raw('');
}
}

View File

@@ -0,0 +1,33 @@
<?php
/*
* This file is part of Twig.
*
* (c) Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Twig\Node\Expression\Binary;
use Twig\Compiler;
class HasSomeBinary extends AbstractBinary
{
public function compile(Compiler $compiler): void
{
$compiler
->raw('twig_array_some($this->env, ')
->subcompile($this->getNode('left'))
->raw(', ')
->subcompile($this->getNode('right'))
->raw(')')
;
}
public function operator(Compiler $compiler): Compiler
{
return $compiler->raw('');
}
}

View File

@@ -18,7 +18,7 @@ class MatchesBinary extends AbstractBinary
public function compile(Compiler $compiler): void
{
$compiler
->raw('preg_match(')
->raw('twig_matches(')
->subcompile($this->getNode('right'))
->raw(', ')
->subcompile($this->getNode('left'))

View File

@@ -24,7 +24,7 @@ class StartsWithBinary extends AbstractBinary
->subcompile($this->getNode('left'))
->raw(sprintf(') && is_string($%s = ', $right))
->subcompile($this->getNode('right'))
->raw(sprintf(') && (\'\' === $%2$s || 0 === strpos($%1$s, $%2$s)))', $left, $right))
->raw(sprintf(') && str_starts_with($%1$s, $%2$s))', $left, $right))
;
}

View File

@@ -24,7 +24,7 @@ abstract class CallExpression extends AbstractExpression
{
$callable = $this->getAttribute('callable');
if (\is_string($callable) && false === strpos($callable, '::')) {
if (\is_string($callable) && !str_contains($callable, '::')) {
$compiler->raw($callable);
} else {
[$r, $callable] = $this->reflectCallable($callable);
@@ -297,14 +297,16 @@ abstract class CallExpression extends AbstractExpression
}
$r = new \ReflectionFunction($closure);
if (false !== strpos($r->name, '{closure}')) {
if (str_contains($r->name, '{closure}')) {
return $this->reflector = [$r, $callable, 'Closure'];
}
if ($object = $r->getClosureThis()) {
$callable = [$object, $r->name];
$callableName = (\function_exists('get_debug_type') ? get_debug_type($object) : \get_class($object)).'::'.$r->name;
} elseif ($class = $r->getClosureScopeClass()) {
$callableName = get_debug_type($object).'::'.$r->name;
} elseif (\PHP_VERSION_ID >= 80111 && $class = $r->getClosureCalledClass()) {
$callableName = $class->name.'::'.$r->name;
} elseif (\PHP_VERSION_ID < 80111 && $class = $r->getClosureScopeClass()) {
$callableName = (\is_array($callable) ? $callable[0] : $class->name).'::'.$r->name;
} else {
$callable = $callableName = $r->name;

View File

@@ -23,14 +23,23 @@ class ConditionalExpression extends AbstractExpression
public function compile(Compiler $compiler): void
{
$compiler
->raw('((')
->subcompile($this->getNode('expr1'))
->raw(') ? (')
->subcompile($this->getNode('expr2'))
->raw(') : (')
->subcompile($this->getNode('expr3'))
->raw('))')
;
// Ternary with no then uses Elvis operator
if ($this->getNode('expr1') === $this->getNode('expr2')) {
$compiler
->raw('((')
->subcompile($this->getNode('expr1'))
->raw(') ?: (')
->subcompile($this->getNode('expr3'))
->raw('))');
} else {
$compiler
->raw('((')
->subcompile($this->getNode('expr1'))
->raw(') ? (')
->subcompile($this->getNode('expr2'))
->raw(') : (')
->subcompile($this->getNode('expr3'))
->raw('))');
}
}
}

View File

@@ -50,8 +50,11 @@ class IfNode extends Node
->subcompile($this->getNode('tests')->getNode($i))
->raw(") {\n")
->indent()
->subcompile($this->getNode('tests')->getNode($i + 1))
;
// The node might not exists if the content is empty
if ($this->getNode('tests')->hasNode($i + 1)) {
$compiler->subcompile($this->getNode('tests')->getNode($i + 1));
}
}
if ($this->hasNode('else')) {

View File

@@ -355,6 +355,9 @@ final class ModuleNode extends Node
protected function compileGetTemplateName(Compiler $compiler)
{
$compiler
->write("/**\n")
->write(" * @codeCoverageIgnore\n")
->write(" */\n")
->write("public function getTemplateName()\n", "{\n")
->indent()
->write('return ')
@@ -409,6 +412,9 @@ final class ModuleNode extends Node
}
$compiler
->write("/**\n")
->write(" * @codeCoverageIgnore\n")
->write(" */\n")
->write("public function isTraitable()\n", "{\n")
->indent()
->write(sprintf("return %s;\n", $traitable ? 'true' : 'false'))
@@ -420,6 +426,9 @@ final class ModuleNode extends Node
protected function compileDebugInfo(Compiler $compiler)
{
$compiler
->write("/**\n")
->write(" * @codeCoverageIgnore\n")
->write(" */\n")
->write("public function getDebugInfo()\n", "{\n")
->indent()
->write(sprintf("return %s;\n", str_replace("\n", '', var_export(array_reverse($compiler->getDebugInfo(), true), true))))

View File

@@ -27,7 +27,6 @@ class Node implements \Countable, \IteratorAggregate
protected $lineno;
protected $tag;
private $name;
private $sourceContext;
/**

View File

@@ -45,7 +45,7 @@ class WithNode extends Node
->write(sprintf('$%s = ', $varsName))
->subcompile($node)
->raw(";\n")
->write(sprintf("if (!twig_test_iterable(\$%s)) {\n", $varsName))
->write(sprintf("if (!is_iterable(\$%s)) {\n", $varsName))
->indent()
->write("throw new RuntimeError('Variables passed to the \"with\" tag must be a hash.', ")
->repr($node->getTemplateLine())

View File

@@ -57,7 +57,7 @@ final class EscaperNodeVisitor implements NodeVisitorInterface
} elseif ($node instanceof AutoEscapeNode) {
$this->statusStack[] = $node->getAttribute('value');
} elseif ($node instanceof BlockNode) {
$this->statusStack[] = isset($this->blocks[$node->getAttribute('name')]) ? $this->blocks[$node->getAttribute('name')] : $this->needEscaping($env);
$this->statusStack[] = $this->blocks[$node->getAttribute('name')] ?? $this->needEscaping();
} elseif ($node instanceof ImportNode) {
$this->safeVars[] = $node->getNode('var')->getAttribute('name');
}
@@ -73,7 +73,7 @@ final class EscaperNodeVisitor implements NodeVisitorInterface
$this->blocks = [];
} elseif ($node instanceof FilterExpression) {
return $this->preEscapeFilterNode($node, $env);
} elseif ($node instanceof PrintNode && false !== $type = $this->needEscaping($env)) {
} elseif ($node instanceof PrintNode && false !== $type = $this->needEscaping()) {
$expression = $node->getNode('expr');
if ($expression instanceof ConditionalExpression && $this->shouldUnwrapConditional($expression, $env, $type)) {
return new DoNode($this->unwrapConditional($expression, $env, $type), $expression->getTemplateLine());
@@ -85,7 +85,7 @@ final class EscaperNodeVisitor implements NodeVisitorInterface
if ($node instanceof AutoEscapeNode || $node instanceof BlockNode) {
array_pop($this->statusStack);
} elseif ($node instanceof BlockReferenceNode) {
$this->blocks[$node->getAttribute('name')] = $this->needEscaping($env);
$this->blocks[$node->getAttribute('name')] = $this->needEscaping();
}
return $node;
@@ -183,13 +183,13 @@ final class EscaperNodeVisitor implements NodeVisitorInterface
return \in_array($type, $safe) || \in_array('all', $safe);
}
private function needEscaping(Environment $env)
private function needEscaping()
{
if (\count($this->statusStack)) {
return $this->statusStack[\count($this->statusStack) - 1];
}
return $this->defaultStrategy ? $this->defaultStrategy : false;
return $this->defaultStrategy ?: false;
}
private function getEscaperFilter(string $type, Node $node): FilterExpression

View File

@@ -50,10 +50,10 @@ final class MacroAutoImportNodeVisitor implements NodeVisitorInterface
}
} elseif ($this->inAModule) {
if (
$node instanceof GetAttrExpression &&
$node->getNode('node') instanceof NameExpression &&
'_self' === $node->getNode('node')->getAttribute('name') &&
$node->getNode('attribute') instanceof ConstantExpression
$node instanceof GetAttrExpression
&& $node->getNode('node') instanceof NameExpression
&& '_self' === $node->getNode('node')->getAttribute('name')
&& $node->getNode('attribute') instanceof ConstantExpression
) {
$this->hasMacroCalls = true;

View File

@@ -63,7 +63,7 @@ final class OptimizerNodeVisitor implements NodeVisitorInterface
public function enterNode(Node $node, Environment $env): Node
{
if (self::OPTIMIZE_FOR === (self::OPTIMIZE_FOR & $this->optimizers)) {
$this->enterOptimizeFor($node, $env);
$this->enterOptimizeFor($node);
}
return $node;
@@ -72,14 +72,14 @@ final class OptimizerNodeVisitor implements NodeVisitorInterface
public function leaveNode(Node $node, Environment $env): ?Node
{
if (self::OPTIMIZE_FOR === (self::OPTIMIZE_FOR & $this->optimizers)) {
$this->leaveOptimizeFor($node, $env);
$this->leaveOptimizeFor($node);
}
if (self::OPTIMIZE_RAW_FILTER === (self::OPTIMIZE_RAW_FILTER & $this->optimizers)) {
$node = $this->optimizeRawFilter($node, $env);
$node = $this->optimizeRawFilter($node);
}
$node = $this->optimizePrintNode($node, $env);
$node = $this->optimizePrintNode($node);
return $node;
}
@@ -91,7 +91,7 @@ final class OptimizerNodeVisitor implements NodeVisitorInterface
*
* * "echo $this->render(Parent)Block()" with "$this->display(Parent)Block()"
*/
private function optimizePrintNode(Node $node, Environment $env): Node
private function optimizePrintNode(Node $node): Node
{
if (!$node instanceof PrintNode) {
return $node;
@@ -99,8 +99,8 @@ final class OptimizerNodeVisitor implements NodeVisitorInterface
$exprNode = $node->getNode('expr');
if (
$exprNode instanceof BlockReferenceExpression ||
$exprNode instanceof ParentExpression
$exprNode instanceof BlockReferenceExpression
|| $exprNode instanceof ParentExpression
) {
$exprNode->setAttribute('output', true);
@@ -113,7 +113,7 @@ final class OptimizerNodeVisitor implements NodeVisitorInterface
/**
* Removes "raw" filters.
*/
private function optimizeRawFilter(Node $node, Environment $env): Node
private function optimizeRawFilter(Node $node): Node
{
if ($node instanceof FilterExpression && 'raw' == $node->getNode('filter')->getAttribute('value')) {
return $node->getNode('node');
@@ -125,7 +125,7 @@ final class OptimizerNodeVisitor implements NodeVisitorInterface
/**
* Optimizes "for" tag by removing the "loop" variable creation whenever possible.
*/
private function enterOptimizeFor(Node $node, Environment $env): void
private function enterOptimizeFor(Node $node): void
{
if ($node instanceof ForNode) {
// disable the loop variable by default
@@ -166,7 +166,7 @@ final class OptimizerNodeVisitor implements NodeVisitorInterface
&& 'include' === $node->getAttribute('name')
&& (!$node->getNode('arguments')->hasNode('with_context')
|| false !== $node->getNode('arguments')->getNode('with_context')->getAttribute('value')
)
)
) {
$this->addLoopToAll();
}
@@ -175,12 +175,12 @@ final class OptimizerNodeVisitor implements NodeVisitorInterface
elseif ($node instanceof GetAttrExpression
&& (!$node->getNode('attribute') instanceof ConstantExpression
|| 'parent' === $node->getNode('attribute')->getAttribute('value')
)
)
&& (true === $this->loops[0]->getAttribute('with_loop')
|| ($node->getNode('node') instanceof NameExpression
&& 'loop' === $node->getNode('node')->getAttribute('name')
)
)
|| ($node->getNode('node') instanceof NameExpression
&& 'loop' === $node->getNode('node')->getAttribute('name')
)
)
) {
$this->addLoopToAll();
}
@@ -189,7 +189,7 @@ final class OptimizerNodeVisitor implements NodeVisitorInterface
/**
* Optimizes "for" tag by removing the "loop" variable creation whenever possible.
*/
private function leaveOptimizeFor(Node $node, Environment $env): void
private function leaveOptimizeFor(Node $node): void
{
if ($node instanceof ForNode) {
array_shift($this->loops);

View File

@@ -303,10 +303,9 @@ class Parser
// check that the body does not contain non-empty output nodes
if (
($node instanceof TextNode && !ctype_space($node->getAttribute('data')))
||
(!$node instanceof TextNode && !$node instanceof BlockReferenceNode && $node instanceof NodeOutputInterface)
|| (!$node instanceof TextNode && !$node instanceof BlockReferenceNode && $node instanceof NodeOutputInterface)
) {
if (false !== strpos((string) $node, \chr(0xEF).\chr(0xBB).\chr(0xBF))) {
if (str_contains((string) $node, \chr(0xEF).\chr(0xBB).\chr(0xBF))) {
$t = substr($node->getAttribute('data'), 3);
if ('' === $t || ctype_space($t)) {
// bypass empty nodes starting with a BOM

View File

@@ -37,7 +37,7 @@ final class HtmlDumper extends BaseDumper
protected function formatNonTemplate(Profile $profile, $prefix): string
{
return sprintf('%s└ %s::%s(<span style="background-color: %s">%s</span>)', $prefix, $profile->getTemplate(), $profile->getType(), isset(self::$colors[$profile->getType()]) ? self::$colors[$profile->getType()] : 'auto', $profile->getName());
return sprintf('%s└ %s::%s(<span style="background-color: %s">%s</span>)', $prefix, $profile->getTemplate(), $profile->getType(), self::$colors[$profile->getType()] ?? 'auto', $profile->getName());
}
protected function formatTime(Profile $profile, $percent): string

View File

@@ -32,7 +32,7 @@ final class Profile implements \IteratorAggregate, \Serializable
{
$this->template = $template;
$this->type = $type;
$this->name = 0 === strpos($name, '__internal_') ? 'INTERNAL' : $name;
$this->name = str_starts_with($name, '__internal_') ? 'INTERNAL' : $name;
$this->enter();
}

Some files were not shown because too many files have changed in this diff Show More