Compare commits

...

86 commits

Author SHA1 Message Date
9153ca05fd Merge pull request '[Hotfix] Fix REPO_URL for woodpecker' (#1491) from nupplaPhil/friendica-addons:bug/stable_hf into develop
Reviewed-on: friendica/friendica-addons#1491
Reviewed-by: Hypolite Petovan <hypolite@mrpetovan.com>
2024-03-22 16:30:56 +01:00
3bdbcd3b02
[Hotfix] Fix REPO_URL for woodpecker 2024-03-22 16:23:20 +01:00
b4eee553d1 Merge pull request 'Bluesky: Support Restrictions / Updated Friendica handle description' (#1489) from heluecht/friendica-addons:restrictions into develop
Reviewed-on: friendica/friendica-addons#1489
Reviewed-by: Hypolite Petovan <hypolite@mrpetovan.com>
2024-03-22 14:14:03 +01:00
04ce1fd2b4 Bluesky: Support Restrictions / Updated Friendica handle description 2024-03-22 05:32:17 +00:00
569931986d Merge pull request '[various] Update Composer dependencies ahead of release' (#1487) from MrPetovan/friendica-addons:task/composer into 2024.03-rc
Reviewed-on: friendica/friendica-addons#1487
2024-03-20 10:16:50 +01:00
0a69c66d09 [securemail] Update Composer dependency ahead of release
- Updating phpseclib/phpseclib (3.0.19 => 3.0.37)
2024-03-19 23:11:09 -04:00
7bff983d21 [saml] Update Composer dependency ahead of release
- Updating onelogin/php-saml (4.0.0 => 4.1.0)
2024-03-19 23:10:10 -04:00
d910502a17 [s3_storage] Update Composer dependency ahead of release
- Updating akeeba/s3 (2.3.1 => 2.3.2)
2024-03-19 23:10:09 -04:00
c6b2ed96d7 [phpmailer] Update Composer dependency ahead of release
- Updating phpmailer/phpmailer (v6.5.0 => v6.9.1)
2024-03-19 23:10:09 -04:00
a020ac4309 [monolog] Update Composer dependencies ahead of release
- Updating monolog/monolog (2.9.1 => 2.9.2)
2024-03-19 23:10:09 -04:00
d838fc6421 [blockbot] Update Composer dependency ahead of release
- Updating jaybizzle/crawler-detect (v1.2.80 => v1.2.116)
2024-03-19 23:10:09 -04:00
b0ee9fdf2a [advancedcontentfilter] Update Composer dependencies ahead of release
- Removing symfony/polyfill-apcu (v1.28.0)
- Removing psr/simple-cache (1.0.1)
- Updating psr/http-message (1.1 => 2.0)
- Downgrading psr/container (2.0.2 => 1.1.2)
- Updating slim/slim (4.12.0 => 4.13.0)
- Installing symfony/polyfill-php80 (v1.29.0)
- Installing symfony/var-exporter (v5.4.35)
- Installing symfony/deprecation-contracts (v2.5.2)
- Installing symfony/service-contracts (v2.5.2)
- Installing symfony/polyfill-php73 (v1.29.0)
- Installing symfony/cache-contracts (v2.5.2)
- Updating symfony/cache (v3.4.47 => v4.4.48)
2024-03-19 23:10:07 -04:00
46c65b79be [advancedcontentfilter] Improve error handling
- Add Logger to Slim application to log to Friendica log file
- Show more specific error message when rule syntax check fails
- Align editorconfig with Composer style
- Add minimum PHP version to composer.json
2024-03-19 22:57:32 -04:00
11cc359434 Merge pull request 'Updated URL Replace addon' (#1483) from toddy/friendica-addons:2024.03-rc into 2024.03-rc
Reviewed-on: friendica/friendica-addons#1483
Reviewed-by: Hypolite Petovan <hypolite@mrpetovan.com>
2024-03-19 18:00:45 +01:00
872a438dcf [nitter] Drop support for the addon
- Please use the URL replace addon instead
2024-03-19 17:57:40 +01:00
da8681c8c4 [invidious] Drop support for the addon
- Please use the URL replace addon instead
2024-03-19 17:57:40 +01:00
67c44792fd [url_replace] Simplify config setting conditions
- Add default values for config values in url_replace_addon_admin()
- Capitalize brand names
2024-03-19 17:57:40 +01:00
Dr. Tobias Quathamer
cfbbeaac62 Use invidio.us as default 2024-03-19 17:57:40 +01:00
Dr. Tobias Quathamer
32f698ce10 Only show small info about replacements if the original has changed 2024-03-19 17:57:40 +01:00
Dr. Tobias Quathamer
a110b2f6c1 Reformat code to apply standards 2024-03-19 17:57:40 +01:00
Dr. Tobias Quathamer
fc33555cd6 Remove configuration value if no server URL is provided 2024-03-19 17:57:40 +01:00
Dr. Tobias Quathamer
e24e3f758a Fix formatting 2024-03-19 17:57:40 +01:00
Dr. Tobias Quathamer
127ab370fc Update message catalog 2024-03-19 17:57:40 +01:00
Dr. Tobias Quathamer
890bc3712f Use constants for the default servers 2024-03-19 17:57:40 +01:00
Dr. Tobias Quathamer
2cff751b12 Increment version 2024-03-19 17:57:40 +01:00
Dr. Tobias Quathamer
89134542b4 Update message catalog 2024-03-19 17:57:40 +01:00
Dr. Tobias Quathamer
453772e393 Remove now unnecessary variable 2024-03-19 17:57:40 +01:00
Dr. Tobias Quathamer
0bcf2d7c89 Reformat code, no content changes 2024-03-19 17:57:40 +01:00
Dr. Tobias Quathamer
c6daf2381c Enable individual replacement preferences 2024-03-19 17:57:40 +01:00
Dr. Tobias Quathamer
51797d975b Update README 2024-03-19 17:57:40 +01:00
Dr. Tobias Quathamer
a40bd7009b Add proxigram for instagram redirection.
Based on an idea and patch from loma-one. Thanks!
2024-03-19 17:57:40 +01:00
Dr. Tobias Quathamer
1bcd23f684 Simplify providing a default empty array 2024-03-19 17:57:40 +01:00
f3bddaf7d6 Merge pull request 'Blockbot: More agents added' (#1486) from heluecht/friendica-addons:blockbot-agents into 2024.03-rc
Reviewed-on: friendica/friendica-addons#1486
Reviewed-by: Hypolite Petovan <hypolite@mrpetovan.com>
2024-03-16 09:06:25 +01:00
4a14bc47ee Blockbot: More agents added 2024-03-16 09:06:25 +01:00
09b3f01558 Merge pull request '[CI] Add PHP 8.3 and upgrade CI images' (#1485) from nupplaPhil/friendica-addons:feat/CI_php83 into 2024.03-rc
Reviewed-on: friendica/friendica-addons#1485
Reviewed-by: Hypolite Petovan <hypolite@mrpetovan.com>
2024-03-15 23:19:15 +01:00
e1f27d88b7
Fixup :) 2024-03-15 23:15:42 +01:00
57c4735ad6
Use PHP 8.2 for other pipelines 2024-03-15 23:08:52 +01:00
372a850103
Use PHP 8.2 for codecoverage 2024-03-15 23:06:13 +01:00
7e890124a8
Update PHP 8.1 and PHP 8.2 CI image 2024-03-15 23:03:44 +01:00
904bf11e54
[CI] Add PHP 8.3 2024-03-15 22:55:48 +01:00
1af97e5c9e Merge pull request 'Blockbot: More agents added' (#1484) from heluecht/friendica-addons:blockbots into 2024.03-rc
Reviewed-on: friendica/friendica-addons#1484
2024-03-14 07:30:29 +01:00
02a6fdd9a2 Blockbot: More agents added 2024-03-14 05:19:54 +00:00
d11efc108c Merge pull request 'Bluesky: fix the fetching of media in quoted posts' (#1481) from heluecht/friendica-addons:bluesky-media into 2024.03-rc
Reviewed-on: friendica/friendica-addons#1481
2024-03-10 15:02:28 +01:00
d5dfa8028c Bluesky: fix the fetching of media in quoted posts 2024-03-10 15:02:28 +01:00
3da448b01f Merge pull request 'Tumblr: Handle quote shares' (#1480) from heluecht/friendica-addons:issue-13972 into 2024.03-rc
Reviewed-on: friendica/friendica-addons#1480
2024-03-10 15:01:33 +01:00
0fc8285f87 Tumblr: Handle quote shares 2024-03-10 15:01:33 +01:00
167b7f9466 Merge pull request 'Blockbot: New user agents added' (#1482) from heluecht/friendica-addons:blockbot-forte into 2024.03-rc
Reviewed-on: friendica/friendica-addons#1482
2024-03-10 14:59:56 +01:00
ef7548f5bc Blockbot: New user agents added 2024-03-10 06:46:41 +00:00
c9923e47de Merge pull request '[url_replace] Add support for empty config value' (#1478) from MrPetovan/friendica-addons:bug/13968-url_replace-fatal into 2024.03-rc
Reviewed-on: friendica/friendica-addons#1478
2024-03-07 19:52:53 +01:00
6a9287dc6f [url_replace] Add support for empty config value 2024-03-07 07:22:41 -05:00
eeb783d71d [url_replace] Normalize formatting 2024-03-07 07:21:56 -05:00
0812886b61 Merge pull request 'Blockbot: HTTP library section added' (#1477) from heluecht/friendica-addons:blockbot-forte into 2024.03-rc
Reviewed-on: friendica/friendica-addons#1477
2024-03-06 08:38:32 +01:00
09ae5cfaff More agents added 2024-03-06 08:38:32 +01:00
0751b2ac16 Blockbot: HTTP library section added 2024-03-06 08:38:32 +01:00
ed641e6ccb Merge pull request 'Blockbot: Misskey-Crawler added' (#1476) from heluecht/friendica-addons:blockbot into 2024.03-rc
Reviewed-on: friendica/friendica-addons#1476
2024-03-04 16:32:29 +01:00
a05e429470 Blockbot: Misskey-Crawler added 2024-03-04 15:27:44 +00:00
6e355979e8 Merge pull request '[advancedcontentfilter] Stop using advancedcontentfilter_get_rules() outside of router context' (#1475) from MrPetovan/friendica-addons:bug/13950-advancedcontentfilter-wsod into 2024.03-rc
Reviewed-on: friendica/friendica-addons#1475
2024-03-04 07:23:51 +01:00
3c0f4e3926 [advancedcontentfilter] Stop using advancedcontentfilter_get_rules() outside of router context
- This used to work with Slim v2, but the new requirements for module functions broke it
2024-03-04 01:16:49 -05:00
affa8829d5 Merge pull request 'Blockbot: You can now allow social media agents' (#1474) from heluecht/friendica-addons:blockbot into 2024.03-rc
Reviewed-on: friendica/friendica-addons#1474
2024-03-04 07:00:49 +01:00
b6d706822a Blockbot: You can now allow social media agents 2024-03-04 05:37:04 +00:00
c49b61be8b Merge pull request 'Pnut: Client Id/Secret can be set by admins' (#1473) from heluecht/friendica-addons:pnut into 2024.03-rc
Reviewed-on: friendica/friendica-addons#1473
2024-03-03 18:29:53 +01:00
90ec1bc838 Newlines added 2024-03-03 18:29:34 +01:00
dcafad573e Pnut: Client Id/Secret can be set by admins 2024-03-03 18:29:34 +01:00
dc709c699a Merge pull request 'mailstream: do not mail dislike messages' (#1472) from mexon/friendica-addons:mailstream-dislike into 2024.03-rc
Reviewed-on: friendica/friendica-addons#1472
2024-03-03 13:05:27 +01:00
Matthew Exon
58cb933779 do not mail dislike messages 2024-03-03 13:05:27 +01:00
53be7d9423 Merge pull request 'Bluesky: Handle media links and shared posts' (#1471) from heluecht/friendica-addons:bluesky-media into 2024.03-rc
Reviewed-on: friendica/friendica-addons#1471
2024-03-02 15:38:28 +01:00
2ee78d2f0b Bluesky: Handle media links and shared posts 2024-03-02 13:32:11 +00:00
d19b9ba9e3 Merge pull request 'Bluesky: Enabled support for Friendica handles' (#1469) from heluecht/friendica-addons:bluesky-friendica-handles into 2024.03-rc
Reviewed-on: friendica/friendica-addons#1469
2024-03-01 07:45:35 +01:00
5f7233fd20 Bluesky: Enabled support for Friendica handles 2024-03-01 07:45:35 +01:00
9168f3d167 Merge pull request 'Bluesky: fix problems with links and hashtags' (#1470) from heluecht/friendica-addons:bluesky-tags into 2024.03-rc
Reviewed-on: friendica/friendica-addons#1470
2024-03-01 07:44:23 +01:00
7580054394 Bluesky: fix problems with links and hashtags 2024-03-01 05:53:42 +00:00
bdc27184fc Merge pull request 'Tumblr: Fixed token exchange' (#1468) from heluecht/friendica-addons:Tumblr--Fixed-token-exchange into 2024.03-rc
Reviewed-on: friendica/friendica-addons#1468
2024-02-27 01:34:22 +01:00
346e22c5f5 Tumblr: Fixed token exchange 2024-02-27 01:34:22 +01:00
de784bdc95 Merge pull request 'Bluesky: Several improvements and fixes' (#1467) from heluecht/friendica-addons:bluesky into 2024.03-rc
Reviewed-on: friendica/friendica-addons#1467
2024-02-27 01:33:44 +01:00
6d578d1495 Changed post reason 2024-02-27 01:33:44 +01:00
bcef83e148 Set post reasons 2024-02-27 01:33:44 +01:00
2b5f8e9c82 Fix coding standards 2024-02-27 01:33:44 +01:00
e4bb463b5b Bluesky: Several improvements and fixes 2024-02-27 01:33:44 +01:00
52f7910f4c Merge pull request 'Pnut: add connector for pnut.io' (#1466) from spacenerdmo/friendica-addons:pnut into 2024.03-rc
Reviewed-on: friendica/friendica-addons#1466
2024-02-21 18:22:44 +01:00
9208fd46a4 Pnut: add connector for pnut.io 2024-02-20 17:53:21 -08:00
810dc02dd4 Merge pull request 'Bluesky/Twitter: New parameter added for rhe picture creation' (#1465) from heluecht/friendica-addons:images into 2024.03-rc
Reviewed-on: friendica/friendica-addons#1465
2024-02-17 07:47:00 +01:00
f678468d42 Bluesky/Twitter: New parameter added for rhe picture creation 2024-02-16 02:29:12 +00:00
52ea77eabb Merge pull request 'Markdown: Avoid problems with [*] BBCode element' (#1463) from heluecht/friendica-addons:bbcode-list into develop
Reviewed-on: friendica/friendica-addons#1463
2024-02-11 04:17:28 +01:00
84fc5ba922 Markdown: Avoid problems with [*] BBCode element 2024-02-11 04:17:28 +01:00
d5c7a49db2 Merge pull request 'remove .php from nomserver in config to fix created links' (#1462) from haheute/friendica-addons:openstreetmap into develop
Reviewed-on: friendica/friendica-addons#1462
2024-02-02 17:02:14 +01:00
c9f985d842 remove .php from openstreetmap server in config to fix created links 2024-02-02 14:08:06 +01:00
350 changed files with 16836 additions and 3492 deletions

View file

@ -27,3 +27,6 @@ indent_size = 2
[*.json] [*.json]
indent_style = space indent_style = space
indent_size = 2 indent_size = 2
[composer.json]
indent_size = 4

View file

@ -13,7 +13,7 @@ pipeline:
commands: commands:
- git config --global user.email "no-reply@friendi.ca" - git config --global user.email "no-reply@friendi.ca"
- git config --global user.name "Friendica" - git config --global user.name "Friendica"
- git clone $CI_REPO_LINK addon - git clone $CI_REPO_CLONE_URL addon
- cd addon/ - cd addon/
- git checkout $CI_COMMIT_BRANCH - git checkout $CI_COMMIT_BRANCH
- git fetch origin $CI_COMMIT_REF - git fetch origin $CI_COMMIT_REF

View file

@ -20,7 +20,7 @@ pipeline:
commands: commands:
- git config --global user.email "no-reply@friendi.ca" - git config --global user.email "no-reply@friendi.ca"
- git config --global user.name "Friendica" - git config --global user.name "Friendica"
- git clone $CI_REPO_LINK addon - git clone $CI_REPO_CLONE_URL addon
- cd addon/ - cd addon/
- git checkout $CI_COMMIT_BRANCH - git checkout $CI_COMMIT_BRANCH
- git fetch origin $CI_COMMIT_REF - git fetch origin $CI_COMMIT_REF
@ -45,7 +45,7 @@ pipeline:
branch: [ develop, '*-rc' ] branch: [ develop, '*-rc' ]
event: push event: push
composer_install: composer_install:
image: friendicaci/php7.4:php7.4.33 image: friendicaci/php8.2:php8.2.16
commands: commands:
- export COMPOSER_HOME=.composer - export COMPOSER_HOME=.composer
- composer validate - composer validate

View file

@ -13,7 +13,7 @@ pipeline:
commands: commands:
- git config --global user.email "no-reply@friendi.ca" - git config --global user.email "no-reply@friendi.ca"
- git config --global user.name "Friendica" - git config --global user.name "Friendica"
- git clone $CI_REPO_LINK addon - git clone $CI_REPO_CLONE_URL addon
- cd addon/ - cd addon/
- git checkout $CI_COMMIT_BRANCH - git checkout $CI_COMMIT_BRANCH
- git fetch origin $CI_COMMIT_REF - git fetch origin $CI_COMMIT_REF

View file

@ -5,9 +5,11 @@ matrix:
- PHP_MAJOR_VERSION: 8.0 - PHP_MAJOR_VERSION: 8.0
PHP_VERSION: 8.0.30 PHP_VERSION: 8.0.30
- PHP_MAJOR_VERSION: 8.1 - PHP_MAJOR_VERSION: 8.1
PHP_VERSION: 8.1.23 PHP_VERSION: 8.1.27
- PHP_MAJOR_VERSION: 8.2 - PHP_MAJOR_VERSION: 8.2
PHP_VERSION: 8.2.11 PHP_VERSION: 8.2.16
- PHP_MAJOR_VERSION: 8.3
PHP_VERSION: 8.3.3
# This forces PHP Unit executions at the "opensocial" labeled location (because of much more power...) # This forces PHP Unit executions at the "opensocial" labeled location (because of much more power...)
labels: labels:
@ -26,7 +28,7 @@ pipeline:
commands: commands:
- git config --global user.email "no-reply@friendi.ca" - git config --global user.email "no-reply@friendi.ca"
- git config --global user.name "Friendica" - git config --global user.name "Friendica"
- git clone $CI_REPO_LINK addon - git clone $CI_REPO_CLONE_URL addon
- cd addon/ - cd addon/
- git checkout $CI_COMMIT_BRANCH - git checkout $CI_COMMIT_BRANCH
- git fetch origin $CI_COMMIT_REF - git fetch origin $CI_COMMIT_REF
@ -76,7 +78,7 @@ pipeline:
- cp config/local-sample.config.php config/local.config.php - cp config/local-sample.config.php config/local.config.php
- if ! bin/wait-for-connection $MYSQL_HOST $MYSQL_PORT 300; then echo "[ERROR] Waited 300 seconds, no response" >&2; exit 1; fi - if ! bin/wait-for-connection $MYSQL_HOST $MYSQL_PORT 300; then echo "[ERROR] Waited 300 seconds, no response" >&2; exit 1; fi
- mysql -h$MYSQL_HOST -P$MYSQL_PORT -p$MYSQL_PASSWORD -u$MYSQL_USER $MYSQL_DATABASE < database.sql - mysql -h$MYSQL_HOST -P$MYSQL_PORT -p$MYSQL_PASSWORD -u$MYSQL_USER $MYSQL_DATABASE < database.sql
- if [ "${PHP_MAJOR_VERSION}" = "7.4" -a "${CI_REPO}" = "friendica/friendica-addons" ]; then - if [ "${PHP_MAJOR_VERSION}" = "8.2" -a "${CI_REPO}" = "friendica/friendica-addons" ]; then
phpenmod xdebug; phpenmod xdebug;
export XDEBUG_MODE=coverage; export XDEBUG_MODE=coverage;
phpunit --configuration tests/phpunit-addons.xml --coverage-clover clover.xml; phpunit --configuration tests/phpunit-addons.xml --coverage-clover clover.xml;
@ -87,8 +89,8 @@ pipeline:
image: friendicaci/codecov image: friendicaci/codecov
when: when:
matrix: matrix:
PHP_MAJOR_VERSION: 7.4 PHP_MAJOR_VERSION: 8.2
PHP_VERSION: 7.4.33 PHP_VERSION: 8.2.16
repo: repo:
- friendica/friendica-addons - friendica/friendica-addons
commands: commands:

View file

@ -19,7 +19,7 @@ pipeline:
commands: commands:
- git config --global user.email "no-reply@friendi.ca" - git config --global user.email "no-reply@friendi.ca"
- git config --global user.name "Friendica" - git config --global user.name "Friendica"
- git clone $CI_REPO_LINK addon - git clone $CI_REPO_CLONE_URL addon
- cd addon/ - cd addon/
- git checkout $CI_COMMIT_BRANCH - git checkout $CI_COMMIT_BRANCH
- git fetch origin $CI_COMMIT_REF - git fetch origin $CI_COMMIT_REF
@ -42,7 +42,7 @@ pipeline:
repo: friendica/friendica-addons repo: friendica/friendica-addons
event: tag event: tag
composer_install: composer_install:
image: friendicaci/php7.4:php7.4.33 image: friendicaci/php8.2:php8.2.16
commands: commands:
- export COMPOSER_HOME=.composer - export COMPOSER_HOME=.composer
- composer validate - composer validate

View file

@ -54,7 +54,7 @@ new Vue({
self.rules.push(responseJSON.rule); self.rules.push(responseJSON.rule);
self.resetForm(); self.resetForm();
}, function (response) { }, function (response) {
self.errorMessage = response.responseJSON.message; self.errorMessage = response.responseJSON.exception[0].message;
}); });
} }
}, },
@ -74,7 +74,7 @@ new Vue({
self.rules[self.editedIndex] = rule; self.rules[self.editedIndex] = rule;
self.resetForm(); self.resetForm();
}, function (response) { }, function (response) {
self.errorMessage = response.responseJSON.message; self.errorMessage = response.responseJSON.exception[0].message;
}); });
}, },

View file

@ -253,7 +253,7 @@ function advancedcontentfilter_content()
'cancel' => DI::l10n()->t('Cancel'), 'cancel' => DI::l10n()->t('Cancel'),
], ],
'$current_theme' => DI::app()->getCurrentTheme(), '$current_theme' => DI::app()->getCurrentTheme(),
'$rules' => advancedcontentfilter_get_rules(), '$rules' => DBA::toArray(DBA::select('advancedcontentfilter_rules', [], ['uid' => DI::userSession()->getLocalUserId()])),
'$form_security_token' => BaseModule::getFormSecurityToken() '$form_security_token' => BaseModule::getFormSecurityToken()
]); ]);
} }
@ -305,7 +305,7 @@ function advancedcontentfilter_build_fields($data)
* API * API
*/ */
function advancedcontentfilter_get_rules(ServerRequestInterface $request, ResponseInterface $response) function advancedcontentfilter_get_rules(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
{ {
if (!DI::userSession()->getLocalUserId()) { if (!DI::userSession()->getLocalUserId()) {
throw new HTTPException\UnauthorizedException(DI::l10n()->t('You must be logged in to use this method')); throw new HTTPException\UnauthorizedException(DI::l10n()->t('You must be logged in to use this method'));

View file

@ -17,6 +17,9 @@
"license": "3-clause BSD license", "license": "3-clause BSD license",
"minimum-stability": "stable", "minimum-stability": "stable",
"config": { "config": {
"platform": {
"php": "7.4"
},
"optimize-autoloader": true, "optimize-autoloader": true,
"autoloader-suffix": "AdvancedContentFilterAddon", "autoloader-suffix": "AdvancedContentFilterAddon",
"preferred-install": "dist" "preferred-install": "dist"

View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "3e87f0369e4799fc35d98f399c67f1e9", "content-hash": "a7276eb2d2108a26699f69c750d02d27",
"packages": [ "packages": [
{ {
"name": "nikic/fast-route", "name": "nikic/fast-route",
@ -100,27 +100,22 @@
}, },
{ {
"name": "psr/container", "name": "psr/container",
"version": "2.0.2", "version": "1.1.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/php-fig/container.git", "url": "https://github.com/php-fig/container.git",
"reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" "reference": "513e0666f7216c7459170d56df27dfcefe1689ea"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea",
"reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", "reference": "513e0666f7216c7459170d56df27dfcefe1689ea",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=7.4.0" "php": ">=7.4.0"
}, },
"type": "library", "type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0.x-dev"
}
},
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"Psr\\Container\\": "src/" "Psr\\Container\\": "src/"
@ -145,7 +140,7 @@
"container-interop", "container-interop",
"psr" "psr"
], ],
"time": "2021-11-05T16:47:00+00:00" "time": "2021-11-05T16:50:12+00:00"
}, },
{ {
"name": "psr/http-factory", "name": "psr/http-factory",
@ -201,16 +196,16 @@
}, },
{ {
"name": "psr/http-message", "name": "psr/http-message",
"version": "1.1", "version": "2.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/php-fig/http-message.git", "url": "https://github.com/php-fig/http-message.git",
"reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba" "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/php-fig/http-message/zipball/cb6ce4845ce34a8ad9e68117c10ee90a29919eba", "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71",
"reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba", "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -219,7 +214,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "1.1.x-dev" "dev-master": "2.0.x-dev"
} }
}, },
"autoload": { "autoload": {
@ -234,7 +229,7 @@
"authors": [ "authors": [
{ {
"name": "PHP-FIG", "name": "PHP-FIG",
"homepage": "http://www.php-fig.org/" "homepage": "https://www.php-fig.org/"
} }
], ],
"description": "Common interface for HTTP messages", "description": "Common interface for HTTP messages",
@ -247,7 +242,7 @@
"request", "request",
"response" "response"
], ],
"time": "2023-04-04T09:50:52+00:00" "time": "2023-04-04T09:54:51+00:00"
}, },
{ {
"name": "psr/http-server-handler", "name": "psr/http-server-handler",
@ -402,66 +397,18 @@
], ],
"time": "2021-05-03T11:20:27+00:00" "time": "2021-05-03T11:20:27+00:00"
}, },
{
"name": "psr/simple-cache",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/php-fig/simple-cache.git",
"reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
"reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\SimpleCache\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interfaces for simple caching",
"keywords": [
"cache",
"caching",
"psr",
"psr-16",
"simple-cache"
],
"time": "2017-10-23T01:57:42+00:00"
},
{ {
"name": "slim/slim", "name": "slim/slim",
"version": "4.12.0", "version": "4.13.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/slimphp/Slim.git", "url": "https://github.com/slimphp/Slim.git",
"reference": "e9e99c2b24398b967841c6c4c3048622cc7e2b18" "reference": "038fd5713d5a41636fdff0e8dcceedecdd17fc17"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/slimphp/Slim/zipball/e9e99c2b24398b967841c6c4c3048622cc7e2b18", "url": "https://api.github.com/repos/slimphp/Slim/zipball/038fd5713d5a41636fdff0e8dcceedecdd17fc17",
"reference": "e9e99c2b24398b967841c6c4c3048622cc7e2b18", "reference": "038fd5713d5a41636fdff0e8dcceedecdd17fc17",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -470,7 +417,7 @@
"php": "^7.4 || ^8.0", "php": "^7.4 || ^8.0",
"psr/container": "^1.0 || ^2.0", "psr/container": "^1.0 || ^2.0",
"psr/http-factory": "^1.0", "psr/http-factory": "^1.0",
"psr/http-message": "^1.1", "psr/http-message": "^1.1 || ^2.0",
"psr/http-server-handler": "^1.0", "psr/http-server-handler": "^1.0",
"psr/http-server-middleware": "^1.0", "psr/http-server-middleware": "^1.0",
"psr/log": "^1.1 || ^2.0 || ^3.0" "psr/log": "^1.1 || ^2.0 || ^3.0"
@ -478,19 +425,19 @@
"require-dev": { "require-dev": {
"adriansuter/php-autoload-override": "^1.4", "adriansuter/php-autoload-override": "^1.4",
"ext-simplexml": "*", "ext-simplexml": "*",
"guzzlehttp/psr7": "^2.5", "guzzlehttp/psr7": "^2.6",
"httpsoft/http-message": "^1.1", "httpsoft/http-message": "^1.1",
"httpsoft/http-server-request": "^1.1", "httpsoft/http-server-request": "^1.1",
"laminas/laminas-diactoros": "^2.17", "laminas/laminas-diactoros": "^2.17 || ^3",
"nyholm/psr7": "^1.8", "nyholm/psr7": "^1.8",
"nyholm/psr7-server": "^1.0", "nyholm/psr7-server": "^1.1",
"phpspec/prophecy": "^1.17", "phpspec/prophecy": "^1.19",
"phpspec/prophecy-phpunit": "^2.0", "phpspec/prophecy-phpunit": "^2.1",
"phpstan/phpstan": "^1.10", "phpstan/phpstan": "^1.10",
"phpunit/phpunit": "^9.6", "phpunit/phpunit": "^9.6",
"slim/http": "^1.3", "slim/http": "^1.3",
"slim/psr7": "^1.6", "slim/psr7": "^1.6",
"squizlabs/php_codesniffer": "^3.7" "squizlabs/php_codesniffer": "^3.9"
}, },
"suggest": { "suggest": {
"ext-simplexml": "Needed to support XML format in BodyParsingMiddleware", "ext-simplexml": "Needed to support XML format in BodyParsingMiddleware",
@ -553,41 +500,54 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2023-07-23T04:54:29+00:00" "time": "2024-03-03T21:25:30+00:00"
}, },
{ {
"name": "symfony/cache", "name": "symfony/cache",
"version": "v3.4.47", "version": "v4.4.48",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/cache.git", "url": "https://github.com/symfony/cache.git",
"reference": "a7a14c4832760bd1fbd31be2859ffedc9b6ff813" "reference": "3b98ed664887ad197b8ede3da2432787212eb915"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/cache/zipball/a7a14c4832760bd1fbd31be2859ffedc9b6ff813", "url": "https://api.github.com/repos/symfony/cache/zipball/3b98ed664887ad197b8ede3da2432787212eb915",
"reference": "a7a14c4832760bd1fbd31be2859ffedc9b6ff813", "reference": "3b98ed664887ad197b8ede3da2432787212eb915",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^5.5.9|>=7.0.8", "php": ">=7.1.3",
"psr/cache": "~1.0", "psr/cache": "^1.0|^2.0",
"psr/log": "~1.0", "psr/log": "^1|^2|^3",
"psr/simple-cache": "^1.0", "symfony/cache-contracts": "^1.1.7|^2",
"symfony/polyfill-apcu": "~1.1" "symfony/polyfill-php73": "^1.9",
"symfony/polyfill-php80": "^1.16",
"symfony/service-contracts": "^1.1|^2",
"symfony/var-exporter": "^4.2|^5.0"
}, },
"conflict": { "conflict": {
"symfony/var-dumper": "<3.3" "doctrine/dbal": "<2.7",
"symfony/dependency-injection": "<3.4",
"symfony/http-kernel": "<4.4|>=5.0",
"symfony/var-dumper": "<4.4"
}, },
"provide": { "provide": {
"psr/cache-implementation": "1.0", "psr/cache-implementation": "1.0|2.0",
"psr/simple-cache-implementation": "1.0" "psr/simple-cache-implementation": "1.0|2.0",
"symfony/cache-implementation": "1.0|2.0"
}, },
"require-dev": { "require-dev": {
"cache/integration-tests": "dev-master", "cache/integration-tests": "dev-master",
"doctrine/cache": "^1.6", "doctrine/cache": "^1.6|^2.0",
"doctrine/dbal": "^2.4|^3.0", "doctrine/dbal": "^2.7|^3.0",
"predis/predis": "^1.0" "predis/predis": "^1.1",
"psr/simple-cache": "^1.0|^2.0",
"symfony/config": "^4.2|^5.0",
"symfony/dependency-injection": "^3.4|^4.1|^5.0",
"symfony/filesystem": "^4.4|^5.0",
"symfony/http-kernel": "^4.4",
"symfony/var-dumper": "^4.4|^5.0"
}, },
"type": "library", "type": "library",
"autoload": { "autoload": {
@ -612,7 +572,7 @@
"homepage": "https://symfony.com/contributors" "homepage": "https://symfony.com/contributors"
} }
], ],
"description": "Symfony Cache component with PSR-6, PSR-16, and tags", "description": "Provides extended PSR-6, PSR-16 (and tags) implementations",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"keywords": [ "keywords": [
"caching", "caching",
@ -632,7 +592,147 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2020-10-24T10:57:07+00:00" "time": "2022-10-17T20:21:54+00:00"
},
{
"name": "symfony/cache-contracts",
"version": "v2.5.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/cache-contracts.git",
"reference": "64be4a7acb83b6f2bf6de9a02cee6dad41277ebc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/cache-contracts/zipball/64be4a7acb83b6f2bf6de9a02cee6dad41277ebc",
"reference": "64be4a7acb83b6f2bf6de9a02cee6dad41277ebc",
"shasum": ""
},
"require": {
"php": ">=7.2.5",
"psr/cache": "^1.0|^2.0|^3.0"
},
"suggest": {
"symfony/cache-implementation": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "2.5-dev"
},
"thanks": {
"name": "symfony/contracts",
"url": "https://github.com/symfony/contracts"
}
},
"autoload": {
"psr-4": {
"Symfony\\Contracts\\Cache\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Generic abstractions related to caching",
"homepage": "https://symfony.com",
"keywords": [
"abstractions",
"contracts",
"decoupling",
"interfaces",
"interoperability",
"standards"
],
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2022-01-02T09:53:40+00:00"
},
{
"name": "symfony/deprecation-contracts",
"version": "v2.5.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
"reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e8b495ea28c1d97b5e0c121748d6f9b53d075c66",
"reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "2.5-dev"
},
"thanks": {
"name": "symfony/contracts",
"url": "https://github.com/symfony/contracts"
}
},
"autoload": {
"files": [
"function.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2022-01-02T09:53:40+00:00"
}, },
{ {
"name": "symfony/expression-language", "name": "symfony/expression-language",
@ -694,80 +794,6 @@
], ],
"time": "2020-10-24T10:57:07+00:00" "time": "2020-10-24T10:57:07+00:00"
}, },
{
"name": "symfony/polyfill-apcu",
"version": "v1.28.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-apcu.git",
"reference": "c6c2c0f5f4cb0b100c5dfea807ef5cd27bbe9899"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-apcu/zipball/c6c2c0f5f4cb0b100c5dfea807ef5cd27bbe9899",
"reference": "c6c2c0f5f4cb0b100c5dfea807ef5cd27bbe9899",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Apcu\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill backporting apcu_* functions to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"apcu",
"compatibility",
"polyfill",
"portable",
"shim"
],
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2023-01-26T09:26:14+00:00"
},
{ {
"name": "symfony/polyfill-php70", "name": "symfony/polyfill-php70",
"version": "v1.20.0", "version": "v1.20.0",
@ -832,6 +858,306 @@
} }
], ],
"time": "2020-10-23T14:02:19+00:00" "time": "2020-10-23T14:02:19+00:00"
},
{
"name": "symfony/polyfill-php73",
"version": "v1.29.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php73.git",
"reference": "21bd091060673a1177ae842c0ef8fe30893114d2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/21bd091060673a1177ae842c0ef8fe30893114d2",
"reference": "21bd091060673a1177ae842c0ef8fe30893114d2",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"type": "library",
"extra": {
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Php73\\": ""
},
"classmap": [
"Resources/stubs"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"polyfill",
"portable",
"shim"
],
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-01-29T20:11:03+00:00"
},
{
"name": "symfony/polyfill-php80",
"version": "v1.29.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
"reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/87b68208d5c1188808dd7839ee1e6c8ec3b02f1b",
"reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"type": "library",
"extra": {
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Php80\\": ""
},
"classmap": [
"Resources/stubs"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Ion Bazan",
"email": "ion.bazan@gmail.com"
},
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"polyfill",
"portable",
"shim"
],
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-01-29T20:11:03+00:00"
},
{
"name": "symfony/service-contracts",
"version": "v2.5.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/service-contracts.git",
"reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/4b426aac47d6427cc1a1d0f7e2ac724627f5966c",
"reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c",
"shasum": ""
},
"require": {
"php": ">=7.2.5",
"psr/container": "^1.1",
"symfony/deprecation-contracts": "^2.1|^3"
},
"conflict": {
"ext-psr": "<1.1|>=2"
},
"suggest": {
"symfony/service-implementation": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "2.5-dev"
},
"thanks": {
"name": "symfony/contracts",
"url": "https://github.com/symfony/contracts"
}
},
"autoload": {
"psr-4": {
"Symfony\\Contracts\\Service\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Generic abstractions related to writing services",
"homepage": "https://symfony.com",
"keywords": [
"abstractions",
"contracts",
"decoupling",
"interfaces",
"interoperability",
"standards"
],
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2022-05-30T19:17:29+00:00"
},
{
"name": "symfony/var-exporter",
"version": "v5.4.35",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-exporter.git",
"reference": "abb0a151b62d6b07e816487e20040464af96cae7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/var-exporter/zipball/abb0a151b62d6b07e816487e20040464af96cae7",
"reference": "abb0a151b62d6b07e816487e20040464af96cae7",
"shasum": ""
},
"require": {
"php": ">=7.2.5",
"symfony/polyfill-php80": "^1.16"
},
"require-dev": {
"symfony/var-dumper": "^4.4.9|^5.0.9|^6.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\VarExporter\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Allows exporting any serializable PHP data structure to plain PHP code",
"homepage": "https://symfony.com",
"keywords": [
"clone",
"construct",
"export",
"hydrate",
"instantiate",
"serialize"
],
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-01-23T13:51:25+00:00"
} }
], ],
"packages-dev": [], "packages-dev": [],
@ -840,9 +1166,10 @@
"stability-flags": [], "stability-flags": [],
"prefer-stable": false, "prefer-stable": false,
"prefer-lowest": false, "prefer-lowest": false,
"platform": { "platform": [],
"php": ">=5.6.0"
},
"platform-dev": [], "platform-dev": [],
"platform-overrides": {
"php": "7.4"
},
"plugin-api-version": "1.1.0" "plugin-api-version": "1.1.0"
} }

View file

@ -29,4 +29,4 @@ use Friendica\DI;
*/ */
$slim->addRoutingMiddleware(); $slim->addRoutingMiddleware();
$errorMiddleware = $slim->addErrorMiddleware(true, true, true); $errorMiddleware = $slim->addErrorMiddleware(true, true, true, DI::logger());

View file

@ -3,7 +3,7 @@
<div id="rules"></div> <div id="rules"></div>
<script> <script>
var existingRules = {{$rules nofilter}}; var existingRules = {{$rules|json_encode nofilter}};
var messages = { var messages = {
{{foreach $messages as $key => $value}} {{foreach $messages as $key => $value}}

View file

@ -6,6 +6,7 @@ $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir); $baseDir = dirname($vendorDir);
return array( return array(
'Attribute' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
'FastRoute\\BadRouteException' => $vendorDir . '/nikic/fast-route/src/BadRouteException.php', 'FastRoute\\BadRouteException' => $vendorDir . '/nikic/fast-route/src/BadRouteException.php',
'FastRoute\\DataGenerator' => $vendorDir . '/nikic/fast-route/src/DataGenerator.php', 'FastRoute\\DataGenerator' => $vendorDir . '/nikic/fast-route/src/DataGenerator.php',
'FastRoute\\DataGenerator\\CharCountBased' => $vendorDir . '/nikic/fast-route/src/DataGenerator/CharCountBased.php', 'FastRoute\\DataGenerator\\CharCountBased' => $vendorDir . '/nikic/fast-route/src/DataGenerator/CharCountBased.php',
@ -23,6 +24,8 @@ return array(
'FastRoute\\RouteCollector' => $vendorDir . '/nikic/fast-route/src/RouteCollector.php', 'FastRoute\\RouteCollector' => $vendorDir . '/nikic/fast-route/src/RouteCollector.php',
'FastRoute\\RouteParser' => $vendorDir . '/nikic/fast-route/src/RouteParser.php', 'FastRoute\\RouteParser' => $vendorDir . '/nikic/fast-route/src/RouteParser.php',
'FastRoute\\RouteParser\\Std' => $vendorDir . '/nikic/fast-route/src/RouteParser/Std.php', 'FastRoute\\RouteParser\\Std' => $vendorDir . '/nikic/fast-route/src/RouteParser/Std.php',
'JsonException' => $vendorDir . '/symfony/polyfill-php73/Resources/stubs/JsonException.php',
'PhpToken' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
'Psr\\Cache\\CacheException' => $vendorDir . '/psr/cache/src/CacheException.php', 'Psr\\Cache\\CacheException' => $vendorDir . '/psr/cache/src/CacheException.php',
'Psr\\Cache\\CacheItemInterface' => $vendorDir . '/psr/cache/src/CacheItemInterface.php', 'Psr\\Cache\\CacheItemInterface' => $vendorDir . '/psr/cache/src/CacheItemInterface.php',
'Psr\\Cache\\CacheItemPoolInterface' => $vendorDir . '/psr/cache/src/CacheItemPoolInterface.php', 'Psr\\Cache\\CacheItemPoolInterface' => $vendorDir . '/psr/cache/src/CacheItemPoolInterface.php',
@ -56,9 +59,6 @@ return array(
'Psr\\Log\\Test\\DummyTest' => $vendorDir . '/psr/log/Psr/Log/Test/DummyTest.php', 'Psr\\Log\\Test\\DummyTest' => $vendorDir . '/psr/log/Psr/Log/Test/DummyTest.php',
'Psr\\Log\\Test\\LoggerInterfaceTest' => $vendorDir . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php', 'Psr\\Log\\Test\\LoggerInterfaceTest' => $vendorDir . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php',
'Psr\\Log\\Test\\TestLogger' => $vendorDir . '/psr/log/Psr/Log/Test/TestLogger.php', 'Psr\\Log\\Test\\TestLogger' => $vendorDir . '/psr/log/Psr/Log/Test/TestLogger.php',
'Psr\\SimpleCache\\CacheException' => $vendorDir . '/psr/simple-cache/src/CacheException.php',
'Psr\\SimpleCache\\CacheInterface' => $vendorDir . '/psr/simple-cache/src/CacheInterface.php',
'Psr\\SimpleCache\\InvalidArgumentException' => $vendorDir . '/psr/simple-cache/src/InvalidArgumentException.php',
'Slim\\App' => $vendorDir . '/slim/slim/Slim/App.php', 'Slim\\App' => $vendorDir . '/slim/slim/Slim/App.php',
'Slim\\CallableResolver' => $vendorDir . '/slim/slim/Slim/CallableResolver.php', 'Slim\\CallableResolver' => $vendorDir . '/slim/slim/Slim/CallableResolver.php',
'Slim\\Error\\AbstractErrorRenderer' => $vendorDir . '/slim/slim/Slim/Error/AbstractErrorRenderer.php', 'Slim\\Error\\AbstractErrorRenderer' => $vendorDir . '/slim/slim/Slim/Error/AbstractErrorRenderer.php',
@ -75,6 +75,7 @@ return array(
'Slim\\Exception\\HttpNotFoundException' => $vendorDir . '/slim/slim/Slim/Exception/HttpNotFoundException.php', 'Slim\\Exception\\HttpNotFoundException' => $vendorDir . '/slim/slim/Slim/Exception/HttpNotFoundException.php',
'Slim\\Exception\\HttpNotImplementedException' => $vendorDir . '/slim/slim/Slim/Exception/HttpNotImplementedException.php', 'Slim\\Exception\\HttpNotImplementedException' => $vendorDir . '/slim/slim/Slim/Exception/HttpNotImplementedException.php',
'Slim\\Exception\\HttpSpecializedException' => $vendorDir . '/slim/slim/Slim/Exception/HttpSpecializedException.php', 'Slim\\Exception\\HttpSpecializedException' => $vendorDir . '/slim/slim/Slim/Exception/HttpSpecializedException.php',
'Slim\\Exception\\HttpTooManyRequestsException' => $vendorDir . '/slim/slim/Slim/Exception/HttpTooManyRequestsException.php',
'Slim\\Exception\\HttpUnauthorizedException' => $vendorDir . '/slim/slim/Slim/Exception/HttpUnauthorizedException.php', 'Slim\\Exception\\HttpUnauthorizedException' => $vendorDir . '/slim/slim/Slim/Exception/HttpUnauthorizedException.php',
'Slim\\Factory\\AppFactory' => $vendorDir . '/slim/slim/Slim/Factory/AppFactory.php', 'Slim\\Factory\\AppFactory' => $vendorDir . '/slim/slim/Slim/Factory/AppFactory.php',
'Slim\\Factory\\Psr17\\GuzzlePsr17Factory' => $vendorDir . '/slim/slim/Slim/Factory/Psr17/GuzzlePsr17Factory.php', 'Slim\\Factory\\Psr17\\GuzzlePsr17Factory' => $vendorDir . '/slim/slim/Slim/Factory/Psr17/GuzzlePsr17Factory.php',
@ -130,20 +131,25 @@ return array(
'Slim\\Routing\\RouteResolver' => $vendorDir . '/slim/slim/Slim/Routing/RouteResolver.php', 'Slim\\Routing\\RouteResolver' => $vendorDir . '/slim/slim/Slim/Routing/RouteResolver.php',
'Slim\\Routing\\RouteRunner' => $vendorDir . '/slim/slim/Slim/Routing/RouteRunner.php', 'Slim\\Routing\\RouteRunner' => $vendorDir . '/slim/slim/Slim/Routing/RouteRunner.php',
'Slim\\Routing\\RoutingResults' => $vendorDir . '/slim/slim/Slim/Routing/RoutingResults.php', 'Slim\\Routing\\RoutingResults' => $vendorDir . '/slim/slim/Slim/Routing/RoutingResults.php',
'Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
'Symfony\\Component\\Cache\\Adapter\\AbstractAdapter' => $vendorDir . '/symfony/cache/Adapter/AbstractAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\AbstractAdapter' => $vendorDir . '/symfony/cache/Adapter/AbstractAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\AbstractTagAwareAdapter' => $vendorDir . '/symfony/cache/Adapter/AbstractTagAwareAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\AdapterInterface' => $vendorDir . '/symfony/cache/Adapter/AdapterInterface.php', 'Symfony\\Component\\Cache\\Adapter\\AdapterInterface' => $vendorDir . '/symfony/cache/Adapter/AdapterInterface.php',
'Symfony\\Component\\Cache\\Adapter\\ApcuAdapter' => $vendorDir . '/symfony/cache/Adapter/ApcuAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\ApcuAdapter' => $vendorDir . '/symfony/cache/Adapter/ApcuAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\ArrayAdapter' => $vendorDir . '/symfony/cache/Adapter/ArrayAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\ArrayAdapter' => $vendorDir . '/symfony/cache/Adapter/ArrayAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\ChainAdapter' => $vendorDir . '/symfony/cache/Adapter/ChainAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\ChainAdapter' => $vendorDir . '/symfony/cache/Adapter/ChainAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\DoctrineAdapter' => $vendorDir . '/symfony/cache/Adapter/DoctrineAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\DoctrineAdapter' => $vendorDir . '/symfony/cache/Adapter/DoctrineAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\FilesystemAdapter' => $vendorDir . '/symfony/cache/Adapter/FilesystemAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\FilesystemAdapter' => $vendorDir . '/symfony/cache/Adapter/FilesystemAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\FilesystemTagAwareAdapter' => $vendorDir . '/symfony/cache/Adapter/FilesystemTagAwareAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\MemcachedAdapter' => $vendorDir . '/symfony/cache/Adapter/MemcachedAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\MemcachedAdapter' => $vendorDir . '/symfony/cache/Adapter/MemcachedAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\NullAdapter' => $vendorDir . '/symfony/cache/Adapter/NullAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\NullAdapter' => $vendorDir . '/symfony/cache/Adapter/NullAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\PdoAdapter' => $vendorDir . '/symfony/cache/Adapter/PdoAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\PdoAdapter' => $vendorDir . '/symfony/cache/Adapter/PdoAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\PhpArrayAdapter' => $vendorDir . '/symfony/cache/Adapter/PhpArrayAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\PhpArrayAdapter' => $vendorDir . '/symfony/cache/Adapter/PhpArrayAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\PhpFilesAdapter' => $vendorDir . '/symfony/cache/Adapter/PhpFilesAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\PhpFilesAdapter' => $vendorDir . '/symfony/cache/Adapter/PhpFilesAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\ProxyAdapter' => $vendorDir . '/symfony/cache/Adapter/ProxyAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\ProxyAdapter' => $vendorDir . '/symfony/cache/Adapter/ProxyAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\Psr16Adapter' => $vendorDir . '/symfony/cache/Adapter/Psr16Adapter.php',
'Symfony\\Component\\Cache\\Adapter\\RedisAdapter' => $vendorDir . '/symfony/cache/Adapter/RedisAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\RedisAdapter' => $vendorDir . '/symfony/cache/Adapter/RedisAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\RedisTagAwareAdapter' => $vendorDir . '/symfony/cache/Adapter/RedisTagAwareAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\SimpleCacheAdapter' => $vendorDir . '/symfony/cache/Adapter/SimpleCacheAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\SimpleCacheAdapter' => $vendorDir . '/symfony/cache/Adapter/SimpleCacheAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\TagAwareAdapter' => $vendorDir . '/symfony/cache/Adapter/TagAwareAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\TagAwareAdapter' => $vendorDir . '/symfony/cache/Adapter/TagAwareAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\TagAwareAdapterInterface' => $vendorDir . '/symfony/cache/Adapter/TagAwareAdapterInterface.php', 'Symfony\\Component\\Cache\\Adapter\\TagAwareAdapterInterface' => $vendorDir . '/symfony/cache/Adapter/TagAwareAdapterInterface.php',
@ -152,10 +158,21 @@ return array(
'Symfony\\Component\\Cache\\Adapter\\TraceableTagAwareAdapter' => $vendorDir . '/symfony/cache/Adapter/TraceableTagAwareAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\TraceableTagAwareAdapter' => $vendorDir . '/symfony/cache/Adapter/TraceableTagAwareAdapter.php',
'Symfony\\Component\\Cache\\CacheItem' => $vendorDir . '/symfony/cache/CacheItem.php', 'Symfony\\Component\\Cache\\CacheItem' => $vendorDir . '/symfony/cache/CacheItem.php',
'Symfony\\Component\\Cache\\DataCollector\\CacheDataCollector' => $vendorDir . '/symfony/cache/DataCollector/CacheDataCollector.php', 'Symfony\\Component\\Cache\\DataCollector\\CacheDataCollector' => $vendorDir . '/symfony/cache/DataCollector/CacheDataCollector.php',
'Symfony\\Component\\Cache\\DependencyInjection\\CacheCollectorPass' => $vendorDir . '/symfony/cache/DependencyInjection/CacheCollectorPass.php',
'Symfony\\Component\\Cache\\DependencyInjection\\CachePoolClearerPass' => $vendorDir . '/symfony/cache/DependencyInjection/CachePoolClearerPass.php',
'Symfony\\Component\\Cache\\DependencyInjection\\CachePoolPass' => $vendorDir . '/symfony/cache/DependencyInjection/CachePoolPass.php',
'Symfony\\Component\\Cache\\DependencyInjection\\CachePoolPrunerPass' => $vendorDir . '/symfony/cache/DependencyInjection/CachePoolPrunerPass.php',
'Symfony\\Component\\Cache\\DoctrineProvider' => $vendorDir . '/symfony/cache/DoctrineProvider.php', 'Symfony\\Component\\Cache\\DoctrineProvider' => $vendorDir . '/symfony/cache/DoctrineProvider.php',
'Symfony\\Component\\Cache\\Exception\\CacheException' => $vendorDir . '/symfony/cache/Exception/CacheException.php', 'Symfony\\Component\\Cache\\Exception\\CacheException' => $vendorDir . '/symfony/cache/Exception/CacheException.php',
'Symfony\\Component\\Cache\\Exception\\InvalidArgumentException' => $vendorDir . '/symfony/cache/Exception/InvalidArgumentException.php', 'Symfony\\Component\\Cache\\Exception\\InvalidArgumentException' => $vendorDir . '/symfony/cache/Exception/InvalidArgumentException.php',
'Symfony\\Component\\Cache\\Exception\\LogicException' => $vendorDir . '/symfony/cache/Exception/LogicException.php',
'Symfony\\Component\\Cache\\LockRegistry' => $vendorDir . '/symfony/cache/LockRegistry.php',
'Symfony\\Component\\Cache\\Marshaller\\DefaultMarshaller' => $vendorDir . '/symfony/cache/Marshaller/DefaultMarshaller.php',
'Symfony\\Component\\Cache\\Marshaller\\DeflateMarshaller' => $vendorDir . '/symfony/cache/Marshaller/DeflateMarshaller.php',
'Symfony\\Component\\Cache\\Marshaller\\MarshallerInterface' => $vendorDir . '/symfony/cache/Marshaller/MarshallerInterface.php',
'Symfony\\Component\\Cache\\Marshaller\\TagAwareMarshaller' => $vendorDir . '/symfony/cache/Marshaller/TagAwareMarshaller.php',
'Symfony\\Component\\Cache\\PruneableInterface' => $vendorDir . '/symfony/cache/PruneableInterface.php', 'Symfony\\Component\\Cache\\PruneableInterface' => $vendorDir . '/symfony/cache/PruneableInterface.php',
'Symfony\\Component\\Cache\\Psr16Cache' => $vendorDir . '/symfony/cache/Psr16Cache.php',
'Symfony\\Component\\Cache\\ResettableInterface' => $vendorDir . '/symfony/cache/ResettableInterface.php', 'Symfony\\Component\\Cache\\ResettableInterface' => $vendorDir . '/symfony/cache/ResettableInterface.php',
'Symfony\\Component\\Cache\\Simple\\AbstractCache' => $vendorDir . '/symfony/cache/Simple/AbstractCache.php', 'Symfony\\Component\\Cache\\Simple\\AbstractCache' => $vendorDir . '/symfony/cache/Simple/AbstractCache.php',
'Symfony\\Component\\Cache\\Simple\\ApcuCache' => $vendorDir . '/symfony/cache/Simple/ApcuCache.php', 'Symfony\\Component\\Cache\\Simple\\ApcuCache' => $vendorDir . '/symfony/cache/Simple/ApcuCache.php',
@ -172,17 +189,22 @@ return array(
'Symfony\\Component\\Cache\\Simple\\RedisCache' => $vendorDir . '/symfony/cache/Simple/RedisCache.php', 'Symfony\\Component\\Cache\\Simple\\RedisCache' => $vendorDir . '/symfony/cache/Simple/RedisCache.php',
'Symfony\\Component\\Cache\\Simple\\TraceableCache' => $vendorDir . '/symfony/cache/Simple/TraceableCache.php', 'Symfony\\Component\\Cache\\Simple\\TraceableCache' => $vendorDir . '/symfony/cache/Simple/TraceableCache.php',
'Symfony\\Component\\Cache\\Simple\\TraceableCacheEvent' => $vendorDir . '/symfony/cache/Simple/TraceableCache.php', 'Symfony\\Component\\Cache\\Simple\\TraceableCacheEvent' => $vendorDir . '/symfony/cache/Simple/TraceableCache.php',
'Symfony\\Component\\Cache\\Traits\\AbstractAdapterTrait' => $vendorDir . '/symfony/cache/Traits/AbstractAdapterTrait.php',
'Symfony\\Component\\Cache\\Traits\\AbstractTrait' => $vendorDir . '/symfony/cache/Traits/AbstractTrait.php', 'Symfony\\Component\\Cache\\Traits\\AbstractTrait' => $vendorDir . '/symfony/cache/Traits/AbstractTrait.php',
'Symfony\\Component\\Cache\\Traits\\ApcuTrait' => $vendorDir . '/symfony/cache/Traits/ApcuTrait.php', 'Symfony\\Component\\Cache\\Traits\\ApcuTrait' => $vendorDir . '/symfony/cache/Traits/ApcuTrait.php',
'Symfony\\Component\\Cache\\Traits\\ArrayTrait' => $vendorDir . '/symfony/cache/Traits/ArrayTrait.php', 'Symfony\\Component\\Cache\\Traits\\ArrayTrait' => $vendorDir . '/symfony/cache/Traits/ArrayTrait.php',
'Symfony\\Component\\Cache\\Traits\\ContractsTrait' => $vendorDir . '/symfony/cache/Traits/ContractsTrait.php',
'Symfony\\Component\\Cache\\Traits\\DoctrineTrait' => $vendorDir . '/symfony/cache/Traits/DoctrineTrait.php', 'Symfony\\Component\\Cache\\Traits\\DoctrineTrait' => $vendorDir . '/symfony/cache/Traits/DoctrineTrait.php',
'Symfony\\Component\\Cache\\Traits\\FilesystemCommonTrait' => $vendorDir . '/symfony/cache/Traits/FilesystemCommonTrait.php', 'Symfony\\Component\\Cache\\Traits\\FilesystemCommonTrait' => $vendorDir . '/symfony/cache/Traits/FilesystemCommonTrait.php',
'Symfony\\Component\\Cache\\Traits\\FilesystemTrait' => $vendorDir . '/symfony/cache/Traits/FilesystemTrait.php', 'Symfony\\Component\\Cache\\Traits\\FilesystemTrait' => $vendorDir . '/symfony/cache/Traits/FilesystemTrait.php',
'Symfony\\Component\\Cache\\Traits\\LazyValue' => $vendorDir . '/symfony/cache/Traits/PhpFilesTrait.php',
'Symfony\\Component\\Cache\\Traits\\MemcachedTrait' => $vendorDir . '/symfony/cache/Traits/MemcachedTrait.php', 'Symfony\\Component\\Cache\\Traits\\MemcachedTrait' => $vendorDir . '/symfony/cache/Traits/MemcachedTrait.php',
'Symfony\\Component\\Cache\\Traits\\PdoTrait' => $vendorDir . '/symfony/cache/Traits/PdoTrait.php', 'Symfony\\Component\\Cache\\Traits\\PdoTrait' => $vendorDir . '/symfony/cache/Traits/PdoTrait.php',
'Symfony\\Component\\Cache\\Traits\\PhpArrayTrait' => $vendorDir . '/symfony/cache/Traits/PhpArrayTrait.php', 'Symfony\\Component\\Cache\\Traits\\PhpArrayTrait' => $vendorDir . '/symfony/cache/Traits/PhpArrayTrait.php',
'Symfony\\Component\\Cache\\Traits\\PhpFilesTrait' => $vendorDir . '/symfony/cache/Traits/PhpFilesTrait.php', 'Symfony\\Component\\Cache\\Traits\\PhpFilesTrait' => $vendorDir . '/symfony/cache/Traits/PhpFilesTrait.php',
'Symfony\\Component\\Cache\\Traits\\ProxyTrait' => $vendorDir . '/symfony/cache/Traits/ProxyTrait.php', 'Symfony\\Component\\Cache\\Traits\\ProxyTrait' => $vendorDir . '/symfony/cache/Traits/ProxyTrait.php',
'Symfony\\Component\\Cache\\Traits\\RedisClusterNodeProxy' => $vendorDir . '/symfony/cache/Traits/RedisClusterNodeProxy.php',
'Symfony\\Component\\Cache\\Traits\\RedisClusterProxy' => $vendorDir . '/symfony/cache/Traits/RedisClusterProxy.php',
'Symfony\\Component\\Cache\\Traits\\RedisProxy' => $vendorDir . '/symfony/cache/Traits/RedisProxy.php', 'Symfony\\Component\\Cache\\Traits\\RedisProxy' => $vendorDir . '/symfony/cache/Traits/RedisProxy.php',
'Symfony\\Component\\Cache\\Traits\\RedisTrait' => $vendorDir . '/symfony/cache/Traits/RedisTrait.php', 'Symfony\\Component\\Cache\\Traits\\RedisTrait' => $vendorDir . '/symfony/cache/Traits/RedisTrait.php',
'Symfony\\Component\\ExpressionLanguage\\Compiler' => $vendorDir . '/symfony/expression-language/Compiler.php', 'Symfony\\Component\\ExpressionLanguage\\Compiler' => $vendorDir . '/symfony/expression-language/Compiler.php',
@ -210,5 +232,32 @@ return array(
'Symfony\\Component\\ExpressionLanguage\\SyntaxError' => $vendorDir . '/symfony/expression-language/SyntaxError.php', 'Symfony\\Component\\ExpressionLanguage\\SyntaxError' => $vendorDir . '/symfony/expression-language/SyntaxError.php',
'Symfony\\Component\\ExpressionLanguage\\Token' => $vendorDir . '/symfony/expression-language/Token.php', 'Symfony\\Component\\ExpressionLanguage\\Token' => $vendorDir . '/symfony/expression-language/Token.php',
'Symfony\\Component\\ExpressionLanguage\\TokenStream' => $vendorDir . '/symfony/expression-language/TokenStream.php', 'Symfony\\Component\\ExpressionLanguage\\TokenStream' => $vendorDir . '/symfony/expression-language/TokenStream.php',
'Symfony\\Polyfill\\Apcu\\Apcu' => $vendorDir . '/symfony/polyfill-apcu/Apcu.php', 'Symfony\\Component\\VarExporter\\Exception\\ClassNotFoundException' => $vendorDir . '/symfony/var-exporter/Exception/ClassNotFoundException.php',
'Symfony\\Component\\VarExporter\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/var-exporter/Exception/ExceptionInterface.php',
'Symfony\\Component\\VarExporter\\Exception\\NotInstantiableTypeException' => $vendorDir . '/symfony/var-exporter/Exception/NotInstantiableTypeException.php',
'Symfony\\Component\\VarExporter\\Instantiator' => $vendorDir . '/symfony/var-exporter/Instantiator.php',
'Symfony\\Component\\VarExporter\\Internal\\Exporter' => $vendorDir . '/symfony/var-exporter/Internal/Exporter.php',
'Symfony\\Component\\VarExporter\\Internal\\Hydrator' => $vendorDir . '/symfony/var-exporter/Internal/Hydrator.php',
'Symfony\\Component\\VarExporter\\Internal\\Reference' => $vendorDir . '/symfony/var-exporter/Internal/Reference.php',
'Symfony\\Component\\VarExporter\\Internal\\Registry' => $vendorDir . '/symfony/var-exporter/Internal/Registry.php',
'Symfony\\Component\\VarExporter\\Internal\\Values' => $vendorDir . '/symfony/var-exporter/Internal/Values.php',
'Symfony\\Component\\VarExporter\\VarExporter' => $vendorDir . '/symfony/var-exporter/VarExporter.php',
'Symfony\\Contracts\\Cache\\CacheInterface' => $vendorDir . '/symfony/cache-contracts/CacheInterface.php',
'Symfony\\Contracts\\Cache\\CacheTrait' => $vendorDir . '/symfony/cache-contracts/CacheTrait.php',
'Symfony\\Contracts\\Cache\\CallbackInterface' => $vendorDir . '/symfony/cache-contracts/CallbackInterface.php',
'Symfony\\Contracts\\Cache\\ItemInterface' => $vendorDir . '/symfony/cache-contracts/ItemInterface.php',
'Symfony\\Contracts\\Cache\\TagAwareCacheInterface' => $vendorDir . '/symfony/cache-contracts/TagAwareCacheInterface.php',
'Symfony\\Contracts\\Service\\Attribute\\Required' => $vendorDir . '/symfony/service-contracts/Attribute/Required.php',
'Symfony\\Contracts\\Service\\Attribute\\SubscribedService' => $vendorDir . '/symfony/service-contracts/Attribute/SubscribedService.php',
'Symfony\\Contracts\\Service\\ResetInterface' => $vendorDir . '/symfony/service-contracts/ResetInterface.php',
'Symfony\\Contracts\\Service\\ServiceLocatorTrait' => $vendorDir . '/symfony/service-contracts/ServiceLocatorTrait.php',
'Symfony\\Contracts\\Service\\ServiceProviderInterface' => $vendorDir . '/symfony/service-contracts/ServiceProviderInterface.php',
'Symfony\\Contracts\\Service\\ServiceSubscriberInterface' => $vendorDir . '/symfony/service-contracts/ServiceSubscriberInterface.php',
'Symfony\\Contracts\\Service\\ServiceSubscriberTrait' => $vendorDir . '/symfony/service-contracts/ServiceSubscriberTrait.php',
'Symfony\\Contracts\\Service\\Test\\ServiceLocatorTest' => $vendorDir . '/symfony/service-contracts/Test/ServiceLocatorTest.php',
'Symfony\\Polyfill\\Php73\\Php73' => $vendorDir . '/symfony/polyfill-php73/Php73.php',
'Symfony\\Polyfill\\Php80\\Php80' => $vendorDir . '/symfony/polyfill-php80/Php80.php',
'Symfony\\Polyfill\\Php80\\PhpToken' => $vendorDir . '/symfony/polyfill-php80/PhpToken.php',
'UnhandledMatchError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
'ValueError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
); );

View file

@ -6,6 +6,8 @@ $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir); $baseDir = dirname($vendorDir);
return array( return array(
'32dcc8afd4335739640db7d200c1971d' => $vendorDir . '/symfony/polyfill-apcu/bootstrap.php', 'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
'6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
'0d59ee240a4cd96ddbb4ff164fccea4d' => $vendorDir . '/symfony/polyfill-php73/bootstrap.php',
'253c157292f75eb38082b5acb06f3f01' => $vendorDir . '/nikic/fast-route/src/functions.php', '253c157292f75eb38082b5acb06f3f01' => $vendorDir . '/nikic/fast-route/src/functions.php',
); );

View file

@ -6,11 +6,14 @@ $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir); $baseDir = dirname($vendorDir);
return array( return array(
'Symfony\\Polyfill\\Apcu\\' => array($vendorDir . '/symfony/polyfill-apcu'), 'Symfony\\Polyfill\\Php80\\' => array($vendorDir . '/symfony/polyfill-php80'),
'Symfony\\Polyfill\\Php73\\' => array($vendorDir . '/symfony/polyfill-php73'),
'Symfony\\Contracts\\Service\\' => array($vendorDir . '/symfony/service-contracts'),
'Symfony\\Contracts\\Cache\\' => array($vendorDir . '/symfony/cache-contracts'),
'Symfony\\Component\\VarExporter\\' => array($vendorDir . '/symfony/var-exporter'),
'Symfony\\Component\\ExpressionLanguage\\' => array($vendorDir . '/symfony/expression-language'), 'Symfony\\Component\\ExpressionLanguage\\' => array($vendorDir . '/symfony/expression-language'),
'Symfony\\Component\\Cache\\' => array($vendorDir . '/symfony/cache'), 'Symfony\\Component\\Cache\\' => array($vendorDir . '/symfony/cache'),
'Slim\\' => array($vendorDir . '/slim/slim/Slim'), 'Slim\\' => array($vendorDir . '/slim/slim/Slim'),
'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'),
'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'), 'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
'Psr\\Http\\Server\\' => array($vendorDir . '/psr/http-server-handler/src', $vendorDir . '/psr/http-server-middleware/src'), 'Psr\\Http\\Server\\' => array($vendorDir . '/psr/http-server-handler/src', $vendorDir . '/psr/http-server-middleware/src'),
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-factory/src', $vendorDir . '/psr/http-message/src'), 'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-factory/src', $vendorDir . '/psr/http-message/src'),

View file

@ -7,21 +7,26 @@ namespace Composer\Autoload;
class ComposerStaticInitAdvancedContentFilterAddon class ComposerStaticInitAdvancedContentFilterAddon
{ {
public static $files = array ( public static $files = array (
'32dcc8afd4335739640db7d200c1971d' => __DIR__ . '/..' . '/symfony/polyfill-apcu/bootstrap.php', 'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
'6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
'0d59ee240a4cd96ddbb4ff164fccea4d' => __DIR__ . '/..' . '/symfony/polyfill-php73/bootstrap.php',
'253c157292f75eb38082b5acb06f3f01' => __DIR__ . '/..' . '/nikic/fast-route/src/functions.php', '253c157292f75eb38082b5acb06f3f01' => __DIR__ . '/..' . '/nikic/fast-route/src/functions.php',
); );
public static $prefixLengthsPsr4 = array ( public static $prefixLengthsPsr4 = array (
'S' => 'S' =>
array ( array (
'Symfony\\Polyfill\\Apcu\\' => 22, 'Symfony\\Polyfill\\Php80\\' => 23,
'Symfony\\Polyfill\\Php73\\' => 23,
'Symfony\\Contracts\\Service\\' => 26,
'Symfony\\Contracts\\Cache\\' => 24,
'Symfony\\Component\\VarExporter\\' => 30,
'Symfony\\Component\\ExpressionLanguage\\' => 37, 'Symfony\\Component\\ExpressionLanguage\\' => 37,
'Symfony\\Component\\Cache\\' => 24, 'Symfony\\Component\\Cache\\' => 24,
'Slim\\' => 5, 'Slim\\' => 5,
), ),
'P' => 'P' =>
array ( array (
'Psr\\SimpleCache\\' => 16,
'Psr\\Log\\' => 8, 'Psr\\Log\\' => 8,
'Psr\\Http\\Server\\' => 16, 'Psr\\Http\\Server\\' => 16,
'Psr\\Http\\Message\\' => 17, 'Psr\\Http\\Message\\' => 17,
@ -35,9 +40,25 @@ class ComposerStaticInitAdvancedContentFilterAddon
); );
public static $prefixDirsPsr4 = array ( public static $prefixDirsPsr4 = array (
'Symfony\\Polyfill\\Apcu\\' => 'Symfony\\Polyfill\\Php80\\' =>
array ( array (
0 => __DIR__ . '/..' . '/symfony/polyfill-apcu', 0 => __DIR__ . '/..' . '/symfony/polyfill-php80',
),
'Symfony\\Polyfill\\Php73\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-php73',
),
'Symfony\\Contracts\\Service\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/service-contracts',
),
'Symfony\\Contracts\\Cache\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/cache-contracts',
),
'Symfony\\Component\\VarExporter\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/var-exporter',
), ),
'Symfony\\Component\\ExpressionLanguage\\' => 'Symfony\\Component\\ExpressionLanguage\\' =>
array ( array (
@ -51,10 +72,6 @@ class ComposerStaticInitAdvancedContentFilterAddon
array ( array (
0 => __DIR__ . '/..' . '/slim/slim/Slim', 0 => __DIR__ . '/..' . '/slim/slim/Slim',
), ),
'Psr\\SimpleCache\\' =>
array (
0 => __DIR__ . '/..' . '/psr/simple-cache/src',
),
'Psr\\Log\\' => 'Psr\\Log\\' =>
array ( array (
0 => __DIR__ . '/..' . '/psr/log/Psr/Log', 0 => __DIR__ . '/..' . '/psr/log/Psr/Log',
@ -84,6 +101,7 @@ class ComposerStaticInitAdvancedContentFilterAddon
); );
public static $classMap = array ( public static $classMap = array (
'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
'FastRoute\\BadRouteException' => __DIR__ . '/..' . '/nikic/fast-route/src/BadRouteException.php', 'FastRoute\\BadRouteException' => __DIR__ . '/..' . '/nikic/fast-route/src/BadRouteException.php',
'FastRoute\\DataGenerator' => __DIR__ . '/..' . '/nikic/fast-route/src/DataGenerator.php', 'FastRoute\\DataGenerator' => __DIR__ . '/..' . '/nikic/fast-route/src/DataGenerator.php',
'FastRoute\\DataGenerator\\CharCountBased' => __DIR__ . '/..' . '/nikic/fast-route/src/DataGenerator/CharCountBased.php', 'FastRoute\\DataGenerator\\CharCountBased' => __DIR__ . '/..' . '/nikic/fast-route/src/DataGenerator/CharCountBased.php',
@ -101,6 +119,8 @@ class ComposerStaticInitAdvancedContentFilterAddon
'FastRoute\\RouteCollector' => __DIR__ . '/..' . '/nikic/fast-route/src/RouteCollector.php', 'FastRoute\\RouteCollector' => __DIR__ . '/..' . '/nikic/fast-route/src/RouteCollector.php',
'FastRoute\\RouteParser' => __DIR__ . '/..' . '/nikic/fast-route/src/RouteParser.php', 'FastRoute\\RouteParser' => __DIR__ . '/..' . '/nikic/fast-route/src/RouteParser.php',
'FastRoute\\RouteParser\\Std' => __DIR__ . '/..' . '/nikic/fast-route/src/RouteParser/Std.php', 'FastRoute\\RouteParser\\Std' => __DIR__ . '/..' . '/nikic/fast-route/src/RouteParser/Std.php',
'JsonException' => __DIR__ . '/..' . '/symfony/polyfill-php73/Resources/stubs/JsonException.php',
'PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
'Psr\\Cache\\CacheException' => __DIR__ . '/..' . '/psr/cache/src/CacheException.php', 'Psr\\Cache\\CacheException' => __DIR__ . '/..' . '/psr/cache/src/CacheException.php',
'Psr\\Cache\\CacheItemInterface' => __DIR__ . '/..' . '/psr/cache/src/CacheItemInterface.php', 'Psr\\Cache\\CacheItemInterface' => __DIR__ . '/..' . '/psr/cache/src/CacheItemInterface.php',
'Psr\\Cache\\CacheItemPoolInterface' => __DIR__ . '/..' . '/psr/cache/src/CacheItemPoolInterface.php', 'Psr\\Cache\\CacheItemPoolInterface' => __DIR__ . '/..' . '/psr/cache/src/CacheItemPoolInterface.php',
@ -134,9 +154,6 @@ class ComposerStaticInitAdvancedContentFilterAddon
'Psr\\Log\\Test\\DummyTest' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/DummyTest.php', 'Psr\\Log\\Test\\DummyTest' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/DummyTest.php',
'Psr\\Log\\Test\\LoggerInterfaceTest' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php', 'Psr\\Log\\Test\\LoggerInterfaceTest' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php',
'Psr\\Log\\Test\\TestLogger' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/TestLogger.php', 'Psr\\Log\\Test\\TestLogger' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/TestLogger.php',
'Psr\\SimpleCache\\CacheException' => __DIR__ . '/..' . '/psr/simple-cache/src/CacheException.php',
'Psr\\SimpleCache\\CacheInterface' => __DIR__ . '/..' . '/psr/simple-cache/src/CacheInterface.php',
'Psr\\SimpleCache\\InvalidArgumentException' => __DIR__ . '/..' . '/psr/simple-cache/src/InvalidArgumentException.php',
'Slim\\App' => __DIR__ . '/..' . '/slim/slim/Slim/App.php', 'Slim\\App' => __DIR__ . '/..' . '/slim/slim/Slim/App.php',
'Slim\\CallableResolver' => __DIR__ . '/..' . '/slim/slim/Slim/CallableResolver.php', 'Slim\\CallableResolver' => __DIR__ . '/..' . '/slim/slim/Slim/CallableResolver.php',
'Slim\\Error\\AbstractErrorRenderer' => __DIR__ . '/..' . '/slim/slim/Slim/Error/AbstractErrorRenderer.php', 'Slim\\Error\\AbstractErrorRenderer' => __DIR__ . '/..' . '/slim/slim/Slim/Error/AbstractErrorRenderer.php',
@ -153,6 +170,7 @@ class ComposerStaticInitAdvancedContentFilterAddon
'Slim\\Exception\\HttpNotFoundException' => __DIR__ . '/..' . '/slim/slim/Slim/Exception/HttpNotFoundException.php', 'Slim\\Exception\\HttpNotFoundException' => __DIR__ . '/..' . '/slim/slim/Slim/Exception/HttpNotFoundException.php',
'Slim\\Exception\\HttpNotImplementedException' => __DIR__ . '/..' . '/slim/slim/Slim/Exception/HttpNotImplementedException.php', 'Slim\\Exception\\HttpNotImplementedException' => __DIR__ . '/..' . '/slim/slim/Slim/Exception/HttpNotImplementedException.php',
'Slim\\Exception\\HttpSpecializedException' => __DIR__ . '/..' . '/slim/slim/Slim/Exception/HttpSpecializedException.php', 'Slim\\Exception\\HttpSpecializedException' => __DIR__ . '/..' . '/slim/slim/Slim/Exception/HttpSpecializedException.php',
'Slim\\Exception\\HttpTooManyRequestsException' => __DIR__ . '/..' . '/slim/slim/Slim/Exception/HttpTooManyRequestsException.php',
'Slim\\Exception\\HttpUnauthorizedException' => __DIR__ . '/..' . '/slim/slim/Slim/Exception/HttpUnauthorizedException.php', 'Slim\\Exception\\HttpUnauthorizedException' => __DIR__ . '/..' . '/slim/slim/Slim/Exception/HttpUnauthorizedException.php',
'Slim\\Factory\\AppFactory' => __DIR__ . '/..' . '/slim/slim/Slim/Factory/AppFactory.php', 'Slim\\Factory\\AppFactory' => __DIR__ . '/..' . '/slim/slim/Slim/Factory/AppFactory.php',
'Slim\\Factory\\Psr17\\GuzzlePsr17Factory' => __DIR__ . '/..' . '/slim/slim/Slim/Factory/Psr17/GuzzlePsr17Factory.php', 'Slim\\Factory\\Psr17\\GuzzlePsr17Factory' => __DIR__ . '/..' . '/slim/slim/Slim/Factory/Psr17/GuzzlePsr17Factory.php',
@ -208,20 +226,25 @@ class ComposerStaticInitAdvancedContentFilterAddon
'Slim\\Routing\\RouteResolver' => __DIR__ . '/..' . '/slim/slim/Slim/Routing/RouteResolver.php', 'Slim\\Routing\\RouteResolver' => __DIR__ . '/..' . '/slim/slim/Slim/Routing/RouteResolver.php',
'Slim\\Routing\\RouteRunner' => __DIR__ . '/..' . '/slim/slim/Slim/Routing/RouteRunner.php', 'Slim\\Routing\\RouteRunner' => __DIR__ . '/..' . '/slim/slim/Slim/Routing/RouteRunner.php',
'Slim\\Routing\\RoutingResults' => __DIR__ . '/..' . '/slim/slim/Slim/Routing/RoutingResults.php', 'Slim\\Routing\\RoutingResults' => __DIR__ . '/..' . '/slim/slim/Slim/Routing/RoutingResults.php',
'Stringable' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
'Symfony\\Component\\Cache\\Adapter\\AbstractAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/AbstractAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\AbstractAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/AbstractAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\AbstractTagAwareAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/AbstractTagAwareAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\AdapterInterface' => __DIR__ . '/..' . '/symfony/cache/Adapter/AdapterInterface.php', 'Symfony\\Component\\Cache\\Adapter\\AdapterInterface' => __DIR__ . '/..' . '/symfony/cache/Adapter/AdapterInterface.php',
'Symfony\\Component\\Cache\\Adapter\\ApcuAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/ApcuAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\ApcuAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/ApcuAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\ArrayAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/ArrayAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\ArrayAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/ArrayAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\ChainAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/ChainAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\ChainAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/ChainAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\DoctrineAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/DoctrineAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\DoctrineAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/DoctrineAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\FilesystemAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/FilesystemAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\FilesystemAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/FilesystemAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\FilesystemTagAwareAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/FilesystemTagAwareAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\MemcachedAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/MemcachedAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\MemcachedAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/MemcachedAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\NullAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/NullAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\NullAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/NullAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\PdoAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/PdoAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\PdoAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/PdoAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\PhpArrayAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/PhpArrayAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\PhpArrayAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/PhpArrayAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\PhpFilesAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/PhpFilesAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\PhpFilesAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/PhpFilesAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\ProxyAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/ProxyAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\ProxyAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/ProxyAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\Psr16Adapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/Psr16Adapter.php',
'Symfony\\Component\\Cache\\Adapter\\RedisAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/RedisAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\RedisAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/RedisAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\RedisTagAwareAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/RedisTagAwareAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\SimpleCacheAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/SimpleCacheAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\SimpleCacheAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/SimpleCacheAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\TagAwareAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/TagAwareAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\TagAwareAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/TagAwareAdapter.php',
'Symfony\\Component\\Cache\\Adapter\\TagAwareAdapterInterface' => __DIR__ . '/..' . '/symfony/cache/Adapter/TagAwareAdapterInterface.php', 'Symfony\\Component\\Cache\\Adapter\\TagAwareAdapterInterface' => __DIR__ . '/..' . '/symfony/cache/Adapter/TagAwareAdapterInterface.php',
@ -230,10 +253,21 @@ class ComposerStaticInitAdvancedContentFilterAddon
'Symfony\\Component\\Cache\\Adapter\\TraceableTagAwareAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/TraceableTagAwareAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\TraceableTagAwareAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/TraceableTagAwareAdapter.php',
'Symfony\\Component\\Cache\\CacheItem' => __DIR__ . '/..' . '/symfony/cache/CacheItem.php', 'Symfony\\Component\\Cache\\CacheItem' => __DIR__ . '/..' . '/symfony/cache/CacheItem.php',
'Symfony\\Component\\Cache\\DataCollector\\CacheDataCollector' => __DIR__ . '/..' . '/symfony/cache/DataCollector/CacheDataCollector.php', 'Symfony\\Component\\Cache\\DataCollector\\CacheDataCollector' => __DIR__ . '/..' . '/symfony/cache/DataCollector/CacheDataCollector.php',
'Symfony\\Component\\Cache\\DependencyInjection\\CacheCollectorPass' => __DIR__ . '/..' . '/symfony/cache/DependencyInjection/CacheCollectorPass.php',
'Symfony\\Component\\Cache\\DependencyInjection\\CachePoolClearerPass' => __DIR__ . '/..' . '/symfony/cache/DependencyInjection/CachePoolClearerPass.php',
'Symfony\\Component\\Cache\\DependencyInjection\\CachePoolPass' => __DIR__ . '/..' . '/symfony/cache/DependencyInjection/CachePoolPass.php',
'Symfony\\Component\\Cache\\DependencyInjection\\CachePoolPrunerPass' => __DIR__ . '/..' . '/symfony/cache/DependencyInjection/CachePoolPrunerPass.php',
'Symfony\\Component\\Cache\\DoctrineProvider' => __DIR__ . '/..' . '/symfony/cache/DoctrineProvider.php', 'Symfony\\Component\\Cache\\DoctrineProvider' => __DIR__ . '/..' . '/symfony/cache/DoctrineProvider.php',
'Symfony\\Component\\Cache\\Exception\\CacheException' => __DIR__ . '/..' . '/symfony/cache/Exception/CacheException.php', 'Symfony\\Component\\Cache\\Exception\\CacheException' => __DIR__ . '/..' . '/symfony/cache/Exception/CacheException.php',
'Symfony\\Component\\Cache\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/symfony/cache/Exception/InvalidArgumentException.php', 'Symfony\\Component\\Cache\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/symfony/cache/Exception/InvalidArgumentException.php',
'Symfony\\Component\\Cache\\Exception\\LogicException' => __DIR__ . '/..' . '/symfony/cache/Exception/LogicException.php',
'Symfony\\Component\\Cache\\LockRegistry' => __DIR__ . '/..' . '/symfony/cache/LockRegistry.php',
'Symfony\\Component\\Cache\\Marshaller\\DefaultMarshaller' => __DIR__ . '/..' . '/symfony/cache/Marshaller/DefaultMarshaller.php',
'Symfony\\Component\\Cache\\Marshaller\\DeflateMarshaller' => __DIR__ . '/..' . '/symfony/cache/Marshaller/DeflateMarshaller.php',
'Symfony\\Component\\Cache\\Marshaller\\MarshallerInterface' => __DIR__ . '/..' . '/symfony/cache/Marshaller/MarshallerInterface.php',
'Symfony\\Component\\Cache\\Marshaller\\TagAwareMarshaller' => __DIR__ . '/..' . '/symfony/cache/Marshaller/TagAwareMarshaller.php',
'Symfony\\Component\\Cache\\PruneableInterface' => __DIR__ . '/..' . '/symfony/cache/PruneableInterface.php', 'Symfony\\Component\\Cache\\PruneableInterface' => __DIR__ . '/..' . '/symfony/cache/PruneableInterface.php',
'Symfony\\Component\\Cache\\Psr16Cache' => __DIR__ . '/..' . '/symfony/cache/Psr16Cache.php',
'Symfony\\Component\\Cache\\ResettableInterface' => __DIR__ . '/..' . '/symfony/cache/ResettableInterface.php', 'Symfony\\Component\\Cache\\ResettableInterface' => __DIR__ . '/..' . '/symfony/cache/ResettableInterface.php',
'Symfony\\Component\\Cache\\Simple\\AbstractCache' => __DIR__ . '/..' . '/symfony/cache/Simple/AbstractCache.php', 'Symfony\\Component\\Cache\\Simple\\AbstractCache' => __DIR__ . '/..' . '/symfony/cache/Simple/AbstractCache.php',
'Symfony\\Component\\Cache\\Simple\\ApcuCache' => __DIR__ . '/..' . '/symfony/cache/Simple/ApcuCache.php', 'Symfony\\Component\\Cache\\Simple\\ApcuCache' => __DIR__ . '/..' . '/symfony/cache/Simple/ApcuCache.php',
@ -250,17 +284,22 @@ class ComposerStaticInitAdvancedContentFilterAddon
'Symfony\\Component\\Cache\\Simple\\RedisCache' => __DIR__ . '/..' . '/symfony/cache/Simple/RedisCache.php', 'Symfony\\Component\\Cache\\Simple\\RedisCache' => __DIR__ . '/..' . '/symfony/cache/Simple/RedisCache.php',
'Symfony\\Component\\Cache\\Simple\\TraceableCache' => __DIR__ . '/..' . '/symfony/cache/Simple/TraceableCache.php', 'Symfony\\Component\\Cache\\Simple\\TraceableCache' => __DIR__ . '/..' . '/symfony/cache/Simple/TraceableCache.php',
'Symfony\\Component\\Cache\\Simple\\TraceableCacheEvent' => __DIR__ . '/..' . '/symfony/cache/Simple/TraceableCache.php', 'Symfony\\Component\\Cache\\Simple\\TraceableCacheEvent' => __DIR__ . '/..' . '/symfony/cache/Simple/TraceableCache.php',
'Symfony\\Component\\Cache\\Traits\\AbstractAdapterTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/AbstractAdapterTrait.php',
'Symfony\\Component\\Cache\\Traits\\AbstractTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/AbstractTrait.php', 'Symfony\\Component\\Cache\\Traits\\AbstractTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/AbstractTrait.php',
'Symfony\\Component\\Cache\\Traits\\ApcuTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/ApcuTrait.php', 'Symfony\\Component\\Cache\\Traits\\ApcuTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/ApcuTrait.php',
'Symfony\\Component\\Cache\\Traits\\ArrayTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/ArrayTrait.php', 'Symfony\\Component\\Cache\\Traits\\ArrayTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/ArrayTrait.php',
'Symfony\\Component\\Cache\\Traits\\ContractsTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/ContractsTrait.php',
'Symfony\\Component\\Cache\\Traits\\DoctrineTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/DoctrineTrait.php', 'Symfony\\Component\\Cache\\Traits\\DoctrineTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/DoctrineTrait.php',
'Symfony\\Component\\Cache\\Traits\\FilesystemCommonTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/FilesystemCommonTrait.php', 'Symfony\\Component\\Cache\\Traits\\FilesystemCommonTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/FilesystemCommonTrait.php',
'Symfony\\Component\\Cache\\Traits\\FilesystemTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/FilesystemTrait.php', 'Symfony\\Component\\Cache\\Traits\\FilesystemTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/FilesystemTrait.php',
'Symfony\\Component\\Cache\\Traits\\LazyValue' => __DIR__ . '/..' . '/symfony/cache/Traits/PhpFilesTrait.php',
'Symfony\\Component\\Cache\\Traits\\MemcachedTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/MemcachedTrait.php', 'Symfony\\Component\\Cache\\Traits\\MemcachedTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/MemcachedTrait.php',
'Symfony\\Component\\Cache\\Traits\\PdoTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/PdoTrait.php', 'Symfony\\Component\\Cache\\Traits\\PdoTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/PdoTrait.php',
'Symfony\\Component\\Cache\\Traits\\PhpArrayTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/PhpArrayTrait.php', 'Symfony\\Component\\Cache\\Traits\\PhpArrayTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/PhpArrayTrait.php',
'Symfony\\Component\\Cache\\Traits\\PhpFilesTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/PhpFilesTrait.php', 'Symfony\\Component\\Cache\\Traits\\PhpFilesTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/PhpFilesTrait.php',
'Symfony\\Component\\Cache\\Traits\\ProxyTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/ProxyTrait.php', 'Symfony\\Component\\Cache\\Traits\\ProxyTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/ProxyTrait.php',
'Symfony\\Component\\Cache\\Traits\\RedisClusterNodeProxy' => __DIR__ . '/..' . '/symfony/cache/Traits/RedisClusterNodeProxy.php',
'Symfony\\Component\\Cache\\Traits\\RedisClusterProxy' => __DIR__ . '/..' . '/symfony/cache/Traits/RedisClusterProxy.php',
'Symfony\\Component\\Cache\\Traits\\RedisProxy' => __DIR__ . '/..' . '/symfony/cache/Traits/RedisProxy.php', 'Symfony\\Component\\Cache\\Traits\\RedisProxy' => __DIR__ . '/..' . '/symfony/cache/Traits/RedisProxy.php',
'Symfony\\Component\\Cache\\Traits\\RedisTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/RedisTrait.php', 'Symfony\\Component\\Cache\\Traits\\RedisTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/RedisTrait.php',
'Symfony\\Component\\ExpressionLanguage\\Compiler' => __DIR__ . '/..' . '/symfony/expression-language/Compiler.php', 'Symfony\\Component\\ExpressionLanguage\\Compiler' => __DIR__ . '/..' . '/symfony/expression-language/Compiler.php',
@ -288,7 +327,34 @@ class ComposerStaticInitAdvancedContentFilterAddon
'Symfony\\Component\\ExpressionLanguage\\SyntaxError' => __DIR__ . '/..' . '/symfony/expression-language/SyntaxError.php', 'Symfony\\Component\\ExpressionLanguage\\SyntaxError' => __DIR__ . '/..' . '/symfony/expression-language/SyntaxError.php',
'Symfony\\Component\\ExpressionLanguage\\Token' => __DIR__ . '/..' . '/symfony/expression-language/Token.php', 'Symfony\\Component\\ExpressionLanguage\\Token' => __DIR__ . '/..' . '/symfony/expression-language/Token.php',
'Symfony\\Component\\ExpressionLanguage\\TokenStream' => __DIR__ . '/..' . '/symfony/expression-language/TokenStream.php', 'Symfony\\Component\\ExpressionLanguage\\TokenStream' => __DIR__ . '/..' . '/symfony/expression-language/TokenStream.php',
'Symfony\\Polyfill\\Apcu\\Apcu' => __DIR__ . '/..' . '/symfony/polyfill-apcu/Apcu.php', 'Symfony\\Component\\VarExporter\\Exception\\ClassNotFoundException' => __DIR__ . '/..' . '/symfony/var-exporter/Exception/ClassNotFoundException.php',
'Symfony\\Component\\VarExporter\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/symfony/var-exporter/Exception/ExceptionInterface.php',
'Symfony\\Component\\VarExporter\\Exception\\NotInstantiableTypeException' => __DIR__ . '/..' . '/symfony/var-exporter/Exception/NotInstantiableTypeException.php',
'Symfony\\Component\\VarExporter\\Instantiator' => __DIR__ . '/..' . '/symfony/var-exporter/Instantiator.php',
'Symfony\\Component\\VarExporter\\Internal\\Exporter' => __DIR__ . '/..' . '/symfony/var-exporter/Internal/Exporter.php',
'Symfony\\Component\\VarExporter\\Internal\\Hydrator' => __DIR__ . '/..' . '/symfony/var-exporter/Internal/Hydrator.php',
'Symfony\\Component\\VarExporter\\Internal\\Reference' => __DIR__ . '/..' . '/symfony/var-exporter/Internal/Reference.php',
'Symfony\\Component\\VarExporter\\Internal\\Registry' => __DIR__ . '/..' . '/symfony/var-exporter/Internal/Registry.php',
'Symfony\\Component\\VarExporter\\Internal\\Values' => __DIR__ . '/..' . '/symfony/var-exporter/Internal/Values.php',
'Symfony\\Component\\VarExporter\\VarExporter' => __DIR__ . '/..' . '/symfony/var-exporter/VarExporter.php',
'Symfony\\Contracts\\Cache\\CacheInterface' => __DIR__ . '/..' . '/symfony/cache-contracts/CacheInterface.php',
'Symfony\\Contracts\\Cache\\CacheTrait' => __DIR__ . '/..' . '/symfony/cache-contracts/CacheTrait.php',
'Symfony\\Contracts\\Cache\\CallbackInterface' => __DIR__ . '/..' . '/symfony/cache-contracts/CallbackInterface.php',
'Symfony\\Contracts\\Cache\\ItemInterface' => __DIR__ . '/..' . '/symfony/cache-contracts/ItemInterface.php',
'Symfony\\Contracts\\Cache\\TagAwareCacheInterface' => __DIR__ . '/..' . '/symfony/cache-contracts/TagAwareCacheInterface.php',
'Symfony\\Contracts\\Service\\Attribute\\Required' => __DIR__ . '/..' . '/symfony/service-contracts/Attribute/Required.php',
'Symfony\\Contracts\\Service\\Attribute\\SubscribedService' => __DIR__ . '/..' . '/symfony/service-contracts/Attribute/SubscribedService.php',
'Symfony\\Contracts\\Service\\ResetInterface' => __DIR__ . '/..' . '/symfony/service-contracts/ResetInterface.php',
'Symfony\\Contracts\\Service\\ServiceLocatorTrait' => __DIR__ . '/..' . '/symfony/service-contracts/ServiceLocatorTrait.php',
'Symfony\\Contracts\\Service\\ServiceProviderInterface' => __DIR__ . '/..' . '/symfony/service-contracts/ServiceProviderInterface.php',
'Symfony\\Contracts\\Service\\ServiceSubscriberInterface' => __DIR__ . '/..' . '/symfony/service-contracts/ServiceSubscriberInterface.php',
'Symfony\\Contracts\\Service\\ServiceSubscriberTrait' => __DIR__ . '/..' . '/symfony/service-contracts/ServiceSubscriberTrait.php',
'Symfony\\Contracts\\Service\\Test\\ServiceLocatorTest' => __DIR__ . '/..' . '/symfony/service-contracts/Test/ServiceLocatorTest.php',
'Symfony\\Polyfill\\Php73\\Php73' => __DIR__ . '/..' . '/symfony/polyfill-php73/Php73.php',
'Symfony\\Polyfill\\Php80\\Php80' => __DIR__ . '/..' . '/symfony/polyfill-php80/Php80.php',
'Symfony\\Polyfill\\Php80\\PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/PhpToken.php',
'UnhandledMatchError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
'ValueError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
); );
public static function getInitializer(ClassLoader $loader) public static function getInitializer(ClassLoader $loader)

View file

@ -97,29 +97,24 @@
}, },
{ {
"name": "psr/container", "name": "psr/container",
"version": "2.0.2", "version": "1.1.2",
"version_normalized": "2.0.2.0", "version_normalized": "1.1.2.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/php-fig/container.git", "url": "https://github.com/php-fig/container.git",
"reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" "reference": "513e0666f7216c7459170d56df27dfcefe1689ea"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea",
"reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", "reference": "513e0666f7216c7459170d56df27dfcefe1689ea",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=7.4.0" "php": ">=7.4.0"
}, },
"time": "2021-11-05T16:47:00+00:00", "time": "2021-11-05T16:50:12+00:00",
"type": "library", "type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0.x-dev"
}
},
"installation-source": "dist", "installation-source": "dist",
"autoload": { "autoload": {
"psr-4": { "psr-4": {
@ -202,27 +197,27 @@
}, },
{ {
"name": "psr/http-message", "name": "psr/http-message",
"version": "1.1", "version": "2.0",
"version_normalized": "1.1.0.0", "version_normalized": "2.0.0.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/php-fig/http-message.git", "url": "https://github.com/php-fig/http-message.git",
"reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba" "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/php-fig/http-message/zipball/cb6ce4845ce34a8ad9e68117c10ee90a29919eba", "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71",
"reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba", "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^7.2 || ^8.0" "php": "^7.2 || ^8.0"
}, },
"time": "2023-04-04T09:50:52+00:00", "time": "2023-04-04T09:54:51+00:00",
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "1.1.x-dev" "dev-master": "2.0.x-dev"
} }
}, },
"installation-source": "dist", "installation-source": "dist",
@ -238,7 +233,7 @@
"authors": [ "authors": [
{ {
"name": "PHP-FIG", "name": "PHP-FIG",
"homepage": "http://www.php-fig.org/" "homepage": "https://www.php-fig.org/"
} }
], ],
"description": "Common interface for HTTP messages", "description": "Common interface for HTTP messages",
@ -411,69 +406,19 @@
"psr-3" "psr-3"
] ]
}, },
{
"name": "psr/simple-cache",
"version": "1.0.1",
"version_normalized": "1.0.1.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/simple-cache.git",
"reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
"reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"time": "2017-10-23T01:57:42+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Psr\\SimpleCache\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interfaces for simple caching",
"keywords": [
"cache",
"caching",
"psr",
"psr-16",
"simple-cache"
]
},
{ {
"name": "slim/slim", "name": "slim/slim",
"version": "4.12.0", "version": "4.13.0",
"version_normalized": "4.12.0.0", "version_normalized": "4.13.0.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/slimphp/Slim.git", "url": "https://github.com/slimphp/Slim.git",
"reference": "e9e99c2b24398b967841c6c4c3048622cc7e2b18" "reference": "038fd5713d5a41636fdff0e8dcceedecdd17fc17"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/slimphp/Slim/zipball/e9e99c2b24398b967841c6c4c3048622cc7e2b18", "url": "https://api.github.com/repos/slimphp/Slim/zipball/038fd5713d5a41636fdff0e8dcceedecdd17fc17",
"reference": "e9e99c2b24398b967841c6c4c3048622cc7e2b18", "reference": "038fd5713d5a41636fdff0e8dcceedecdd17fc17",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -482,7 +427,7 @@
"php": "^7.4 || ^8.0", "php": "^7.4 || ^8.0",
"psr/container": "^1.0 || ^2.0", "psr/container": "^1.0 || ^2.0",
"psr/http-factory": "^1.0", "psr/http-factory": "^1.0",
"psr/http-message": "^1.1", "psr/http-message": "^1.1 || ^2.0",
"psr/http-server-handler": "^1.0", "psr/http-server-handler": "^1.0",
"psr/http-server-middleware": "^1.0", "psr/http-server-middleware": "^1.0",
"psr/log": "^1.1 || ^2.0 || ^3.0" "psr/log": "^1.1 || ^2.0 || ^3.0"
@ -490,19 +435,19 @@
"require-dev": { "require-dev": {
"adriansuter/php-autoload-override": "^1.4", "adriansuter/php-autoload-override": "^1.4",
"ext-simplexml": "*", "ext-simplexml": "*",
"guzzlehttp/psr7": "^2.5", "guzzlehttp/psr7": "^2.6",
"httpsoft/http-message": "^1.1", "httpsoft/http-message": "^1.1",
"httpsoft/http-server-request": "^1.1", "httpsoft/http-server-request": "^1.1",
"laminas/laminas-diactoros": "^2.17", "laminas/laminas-diactoros": "^2.17 || ^3",
"nyholm/psr7": "^1.8", "nyholm/psr7": "^1.8",
"nyholm/psr7-server": "^1.0", "nyholm/psr7-server": "^1.1",
"phpspec/prophecy": "^1.17", "phpspec/prophecy": "^1.19",
"phpspec/prophecy-phpunit": "^2.0", "phpspec/prophecy-phpunit": "^2.1",
"phpstan/phpstan": "^1.10", "phpstan/phpstan": "^1.10",
"phpunit/phpunit": "^9.6", "phpunit/phpunit": "^9.6",
"slim/http": "^1.3", "slim/http": "^1.3",
"slim/psr7": "^1.6", "slim/psr7": "^1.6",
"squizlabs/php_codesniffer": "^3.7" "squizlabs/php_codesniffer": "^3.9"
}, },
"suggest": { "suggest": {
"ext-simplexml": "Needed to support XML format in BodyParsingMiddleware", "ext-simplexml": "Needed to support XML format in BodyParsingMiddleware",
@ -510,7 +455,7 @@
"php-di/php-di": "PHP-DI is the recommended container library to be used with Slim", "php-di/php-di": "PHP-DI is the recommended container library to be used with Slim",
"slim/psr7": "Slim PSR-7 implementation. See https://www.slimframework.com/docs/v4/start/installation.html for more information." "slim/psr7": "Slim PSR-7 implementation. See https://www.slimframework.com/docs/v4/start/installation.html for more information."
}, },
"time": "2023-07-23T04:54:29+00:00", "time": "2024-03-03T21:25:30+00:00",
"type": "library", "type": "library",
"installation-source": "dist", "installation-source": "dist",
"autoload": { "autoload": {
@ -570,40 +515,53 @@
}, },
{ {
"name": "symfony/cache", "name": "symfony/cache",
"version": "v3.4.47", "version": "v4.4.48",
"version_normalized": "3.4.47.0", "version_normalized": "4.4.48.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/cache.git", "url": "https://github.com/symfony/cache.git",
"reference": "a7a14c4832760bd1fbd31be2859ffedc9b6ff813" "reference": "3b98ed664887ad197b8ede3da2432787212eb915"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/cache/zipball/a7a14c4832760bd1fbd31be2859ffedc9b6ff813", "url": "https://api.github.com/repos/symfony/cache/zipball/3b98ed664887ad197b8ede3da2432787212eb915",
"reference": "a7a14c4832760bd1fbd31be2859ffedc9b6ff813", "reference": "3b98ed664887ad197b8ede3da2432787212eb915",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^5.5.9|>=7.0.8", "php": ">=7.1.3",
"psr/cache": "~1.0", "psr/cache": "^1.0|^2.0",
"psr/log": "~1.0", "psr/log": "^1|^2|^3",
"psr/simple-cache": "^1.0", "symfony/cache-contracts": "^1.1.7|^2",
"symfony/polyfill-apcu": "~1.1" "symfony/polyfill-php73": "^1.9",
"symfony/polyfill-php80": "^1.16",
"symfony/service-contracts": "^1.1|^2",
"symfony/var-exporter": "^4.2|^5.0"
}, },
"conflict": { "conflict": {
"symfony/var-dumper": "<3.3" "doctrine/dbal": "<2.7",
"symfony/dependency-injection": "<3.4",
"symfony/http-kernel": "<4.4|>=5.0",
"symfony/var-dumper": "<4.4"
}, },
"provide": { "provide": {
"psr/cache-implementation": "1.0", "psr/cache-implementation": "1.0|2.0",
"psr/simple-cache-implementation": "1.0" "psr/simple-cache-implementation": "1.0|2.0",
"symfony/cache-implementation": "1.0|2.0"
}, },
"require-dev": { "require-dev": {
"cache/integration-tests": "dev-master", "cache/integration-tests": "dev-master",
"doctrine/cache": "^1.6", "doctrine/cache": "^1.6|^2.0",
"doctrine/dbal": "^2.4|^3.0", "doctrine/dbal": "^2.7|^3.0",
"predis/predis": "^1.0" "predis/predis": "^1.1",
"psr/simple-cache": "^1.0|^2.0",
"symfony/config": "^4.2|^5.0",
"symfony/dependency-injection": "^3.4|^4.1|^5.0",
"symfony/filesystem": "^4.4|^5.0",
"symfony/http-kernel": "^4.4",
"symfony/var-dumper": "^4.4|^5.0"
}, },
"time": "2020-10-24T10:57:07+00:00", "time": "2022-10-17T20:21:54+00:00",
"type": "library", "type": "library",
"installation-source": "dist", "installation-source": "dist",
"autoload": { "autoload": {
@ -628,7 +586,7 @@
"homepage": "https://symfony.com/contributors" "homepage": "https://symfony.com/contributors"
} }
], ],
"description": "Symfony Cache component with PSR-6, PSR-16, and tags", "description": "Provides extended PSR-6, PSR-16 (and tags) implementations",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"keywords": [ "keywords": [
"caching", "caching",
@ -649,6 +607,150 @@
} }
] ]
}, },
{
"name": "symfony/cache-contracts",
"version": "v2.5.2",
"version_normalized": "2.5.2.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/cache-contracts.git",
"reference": "64be4a7acb83b6f2bf6de9a02cee6dad41277ebc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/cache-contracts/zipball/64be4a7acb83b6f2bf6de9a02cee6dad41277ebc",
"reference": "64be4a7acb83b6f2bf6de9a02cee6dad41277ebc",
"shasum": ""
},
"require": {
"php": ">=7.2.5",
"psr/cache": "^1.0|^2.0|^3.0"
},
"suggest": {
"symfony/cache-implementation": ""
},
"time": "2022-01-02T09:53:40+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "2.5-dev"
},
"thanks": {
"name": "symfony/contracts",
"url": "https://github.com/symfony/contracts"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Symfony\\Contracts\\Cache\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Generic abstractions related to caching",
"homepage": "https://symfony.com",
"keywords": [
"abstractions",
"contracts",
"decoupling",
"interfaces",
"interoperability",
"standards"
],
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
]
},
{
"name": "symfony/deprecation-contracts",
"version": "v2.5.2",
"version_normalized": "2.5.2.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
"reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e8b495ea28c1d97b5e0c121748d6f9b53d075c66",
"reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"time": "2022-01-02T09:53:40+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "2.5-dev"
},
"thanks": {
"name": "symfony/contracts",
"url": "https://github.com/symfony/contracts"
}
},
"installation-source": "dist",
"autoload": {
"files": [
"function.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
]
},
{ {
"name": "symfony/expression-language", "name": "symfony/expression-language",
"version": "v3.4.47", "version": "v3.4.47",
@ -711,82 +813,6 @@
} }
] ]
}, },
{
"name": "symfony/polyfill-apcu",
"version": "v1.28.0",
"version_normalized": "1.28.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-apcu.git",
"reference": "c6c2c0f5f4cb0b100c5dfea807ef5cd27bbe9899"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-apcu/zipball/c6c2c0f5f4cb0b100c5dfea807ef5cd27bbe9899",
"reference": "c6c2c0f5f4cb0b100c5dfea807ef5cd27bbe9899",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"time": "2023-01-26T09:26:14+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"installation-source": "dist",
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Apcu\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill backporting apcu_* functions to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"apcu",
"compatibility",
"polyfill",
"portable",
"shim"
],
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
]
},
{ {
"name": "symfony/polyfill-php70", "name": "symfony/polyfill-php70",
"version": "v1.20.0", "version": "v1.20.0",
@ -852,5 +878,313 @@
"type": "tidelift" "type": "tidelift"
} }
] ]
},
{
"name": "symfony/polyfill-php73",
"version": "v1.29.0",
"version_normalized": "1.29.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php73.git",
"reference": "21bd091060673a1177ae842c0ef8fe30893114d2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/21bd091060673a1177ae842c0ef8fe30893114d2",
"reference": "21bd091060673a1177ae842c0ef8fe30893114d2",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"time": "2024-01-29T20:11:03+00:00",
"type": "library",
"extra": {
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"installation-source": "dist",
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Php73\\": ""
},
"classmap": [
"Resources/stubs"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"polyfill",
"portable",
"shim"
],
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
]
},
{
"name": "symfony/polyfill-php80",
"version": "v1.29.0",
"version_normalized": "1.29.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
"reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/87b68208d5c1188808dd7839ee1e6c8ec3b02f1b",
"reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"time": "2024-01-29T20:11:03+00:00",
"type": "library",
"extra": {
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"installation-source": "dist",
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Php80\\": ""
},
"classmap": [
"Resources/stubs"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Ion Bazan",
"email": "ion.bazan@gmail.com"
},
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"polyfill",
"portable",
"shim"
],
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
]
},
{
"name": "symfony/service-contracts",
"version": "v2.5.2",
"version_normalized": "2.5.2.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/service-contracts.git",
"reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/4b426aac47d6427cc1a1d0f7e2ac724627f5966c",
"reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c",
"shasum": ""
},
"require": {
"php": ">=7.2.5",
"psr/container": "^1.1",
"symfony/deprecation-contracts": "^2.1|^3"
},
"conflict": {
"ext-psr": "<1.1|>=2"
},
"suggest": {
"symfony/service-implementation": ""
},
"time": "2022-05-30T19:17:29+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "2.5-dev"
},
"thanks": {
"name": "symfony/contracts",
"url": "https://github.com/symfony/contracts"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Symfony\\Contracts\\Service\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Generic abstractions related to writing services",
"homepage": "https://symfony.com",
"keywords": [
"abstractions",
"contracts",
"decoupling",
"interfaces",
"interoperability",
"standards"
],
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
]
},
{
"name": "symfony/var-exporter",
"version": "v5.4.35",
"version_normalized": "5.4.35.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-exporter.git",
"reference": "abb0a151b62d6b07e816487e20040464af96cae7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/var-exporter/zipball/abb0a151b62d6b07e816487e20040464af96cae7",
"reference": "abb0a151b62d6b07e816487e20040464af96cae7",
"shasum": ""
},
"require": {
"php": ">=7.2.5",
"symfony/polyfill-php80": "^1.16"
},
"require-dev": {
"symfony/var-dumper": "^4.4.9|^5.0.9|^6.0"
},
"time": "2024-01-23T13:51:25+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"Symfony\\Component\\VarExporter\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Allows exporting any serializable PHP data structure to plain PHP code",
"homepage": "https://symfony.com",
"keywords": [
"clone",
"construct",
"export",
"hydrate",
"instantiate",
"serialize"
],
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
]
} }
] ]

View file

@ -18,10 +18,5 @@
"psr-4": { "psr-4": {
"Psr\\Container\\": "src/" "Psr\\Container\\": "src/"
} }
},
"extra": {
"branch-alias": {
"dev-master": "2.0.x-dev"
}
} }
} }

View file

@ -32,5 +32,5 @@ interface ContainerInterface
* *
* @return bool * @return bool
*/ */
public function has(string $id): bool; public function has(string $id);
} }

View file

@ -7,7 +7,7 @@
"authors": [ "authors": [
{ {
"name": "PHP-FIG", "name": "PHP-FIG",
"homepage": "http://www.php-fig.org/" "homepage": "https://www.php-fig.org/"
} }
], ],
"require": { "require": {
@ -20,7 +20,7 @@
}, },
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "1.1.x-dev" "dev-master": "2.0.x-dev"
} }
} }
} }

View file

@ -1,7 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace Psr\Http\Message; namespace Psr\Http\Message;
/** /**
@ -25,7 +23,7 @@ interface MessageInterface
* *
* @return string HTTP protocol version. * @return string HTTP protocol version.
*/ */
public function getProtocolVersion(); public function getProtocolVersion(): string;
/** /**
* Return an instance with the specified HTTP protocol version. * Return an instance with the specified HTTP protocol version.
@ -40,7 +38,7 @@ interface MessageInterface
* @param string $version HTTP protocol version * @param string $version HTTP protocol version
* @return static * @return static
*/ */
public function withProtocolVersion(string $version); public function withProtocolVersion(string $version): MessageInterface;
/** /**
* Retrieves all message header values. * Retrieves all message header values.
@ -67,7 +65,7 @@ interface MessageInterface
* key MUST be a header name, and each value MUST be an array of strings * key MUST be a header name, and each value MUST be an array of strings
* for that header. * for that header.
*/ */
public function getHeaders(); public function getHeaders(): array;
/** /**
* Checks if a header exists by the given case-insensitive name. * Checks if a header exists by the given case-insensitive name.
@ -77,7 +75,7 @@ interface MessageInterface
* name using a case-insensitive string comparison. Returns false if * name using a case-insensitive string comparison. Returns false if
* no matching header name is found in the message. * no matching header name is found in the message.
*/ */
public function hasHeader(string $name); public function hasHeader(string $name): bool;
/** /**
* Retrieves a message header value by the given case-insensitive name. * Retrieves a message header value by the given case-insensitive name.
@ -93,7 +91,7 @@ interface MessageInterface
* header. If the header does not appear in the message, this method MUST * header. If the header does not appear in the message, this method MUST
* return an empty array. * return an empty array.
*/ */
public function getHeader(string $name); public function getHeader(string $name): array;
/** /**
* Retrieves a comma-separated string of the values for a single header. * Retrieves a comma-separated string of the values for a single header.
@ -114,7 +112,7 @@ interface MessageInterface
* concatenated together using a comma. If the header does not appear in * concatenated together using a comma. If the header does not appear in
* the message, this method MUST return an empty string. * the message, this method MUST return an empty string.
*/ */
public function getHeaderLine(string $name); public function getHeaderLine(string $name): string;
/** /**
* Return an instance with the provided value replacing the specified header. * Return an instance with the provided value replacing the specified header.
@ -131,7 +129,7 @@ interface MessageInterface
* @return static * @return static
* @throws \InvalidArgumentException for invalid header names or values. * @throws \InvalidArgumentException for invalid header names or values.
*/ */
public function withHeader(string $name, $value); public function withHeader(string $name, $value): MessageInterface;
/** /**
* Return an instance with the specified header appended with the given value. * Return an instance with the specified header appended with the given value.
@ -149,7 +147,7 @@ interface MessageInterface
* @return static * @return static
* @throws \InvalidArgumentException for invalid header names or values. * @throws \InvalidArgumentException for invalid header names or values.
*/ */
public function withAddedHeader(string $name, $value); public function withAddedHeader(string $name, $value): MessageInterface;
/** /**
* Return an instance without the specified header. * Return an instance without the specified header.
@ -163,14 +161,14 @@ interface MessageInterface
* @param string $name Case-insensitive header field name to remove. * @param string $name Case-insensitive header field name to remove.
* @return static * @return static
*/ */
public function withoutHeader(string $name); public function withoutHeader(string $name): MessageInterface;
/** /**
* Gets the body of the message. * Gets the body of the message.
* *
* @return StreamInterface Returns the body as a stream. * @return StreamInterface Returns the body as a stream.
*/ */
public function getBody(); public function getBody(): StreamInterface;
/** /**
* Return an instance with the specified message body. * Return an instance with the specified message body.
@ -185,5 +183,5 @@ interface MessageInterface
* @return static * @return static
* @throws \InvalidArgumentException When the body is not valid. * @throws \InvalidArgumentException When the body is not valid.
*/ */
public function withBody(StreamInterface $body); public function withBody(StreamInterface $body): MessageInterface;
} }

View file

@ -1,7 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace Psr\Http\Message; namespace Psr\Http\Message;
/** /**
@ -41,7 +39,7 @@ interface RequestInterface extends MessageInterface
* *
* @return string * @return string
*/ */
public function getRequestTarget(); public function getRequestTarget(): string;
/** /**
* Return an instance with the specific request-target. * Return an instance with the specific request-target.
@ -60,14 +58,15 @@ interface RequestInterface extends MessageInterface
* @param string $requestTarget * @param string $requestTarget
* @return static * @return static
*/ */
public function withRequestTarget(string $requestTarget); public function withRequestTarget(string $requestTarget): RequestInterface;
/** /**
* Retrieves the HTTP method of the request. * Retrieves the HTTP method of the request.
* *
* @return string Returns the request method. * @return string Returns the request method.
*/ */
public function getMethod(); public function getMethod(): string;
/** /**
* Return an instance with the provided HTTP method. * Return an instance with the provided HTTP method.
@ -84,7 +83,7 @@ interface RequestInterface extends MessageInterface
* @return static * @return static
* @throws \InvalidArgumentException for invalid HTTP methods. * @throws \InvalidArgumentException for invalid HTTP methods.
*/ */
public function withMethod(string $method); public function withMethod(string $method): RequestInterface;
/** /**
* Retrieves the URI instance. * Retrieves the URI instance.
@ -95,7 +94,7 @@ interface RequestInterface extends MessageInterface
* @return UriInterface Returns a UriInterface instance * @return UriInterface Returns a UriInterface instance
* representing the URI of the request. * representing the URI of the request.
*/ */
public function getUri(); public function getUri(): UriInterface;
/** /**
* Returns an instance with the provided URI. * Returns an instance with the provided URI.
@ -127,5 +126,5 @@ interface RequestInterface extends MessageInterface
* @param bool $preserveHost Preserve the original state of the Host header. * @param bool $preserveHost Preserve the original state of the Host header.
* @return static * @return static
*/ */
public function withUri(UriInterface $uri, bool $preserveHost = false); public function withUri(UriInterface $uri, bool $preserveHost = false): RequestInterface;
} }

View file

@ -1,7 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace Psr\Http\Message; namespace Psr\Http\Message;
/** /**
@ -29,7 +27,7 @@ interface ResponseInterface extends MessageInterface
* *
* @return int Status code. * @return int Status code.
*/ */
public function getStatusCode(); public function getStatusCode(): int;
/** /**
* Return an instance with the specified status code and, optionally, reason phrase. * Return an instance with the specified status code and, optionally, reason phrase.
@ -51,7 +49,7 @@ interface ResponseInterface extends MessageInterface
* @return static * @return static
* @throws \InvalidArgumentException For invalid status code arguments. * @throws \InvalidArgumentException For invalid status code arguments.
*/ */
public function withStatus(int $code, string $reasonPhrase = ''); public function withStatus(int $code, string $reasonPhrase = ''): ResponseInterface;
/** /**
* Gets the response reason phrase associated with the status code. * Gets the response reason phrase associated with the status code.
@ -66,5 +64,5 @@ interface ResponseInterface extends MessageInterface
* @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml * @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
* @return string Reason phrase; must return an empty string if none present. * @return string Reason phrase; must return an empty string if none present.
*/ */
public function getReasonPhrase(); public function getReasonPhrase(): string;
} }

View file

@ -1,7 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace Psr\Http\Message; namespace Psr\Http\Message;
/** /**
@ -53,7 +51,7 @@ interface ServerRequestInterface extends RequestInterface
* *
* @return array * @return array
*/ */
public function getServerParams(); public function getServerParams(): array;
/** /**
* Retrieve cookies. * Retrieve cookies.
@ -65,7 +63,7 @@ interface ServerRequestInterface extends RequestInterface
* *
* @return array * @return array
*/ */
public function getCookieParams(); public function getCookieParams(): array;
/** /**
* Return an instance with the specified cookies. * Return an instance with the specified cookies.
@ -84,7 +82,7 @@ interface ServerRequestInterface extends RequestInterface
* @param array $cookies Array of key/value pairs representing cookies. * @param array $cookies Array of key/value pairs representing cookies.
* @return static * @return static
*/ */
public function withCookieParams(array $cookies); public function withCookieParams(array $cookies): ServerRequestInterface;
/** /**
* Retrieve query string arguments. * Retrieve query string arguments.
@ -98,7 +96,7 @@ interface ServerRequestInterface extends RequestInterface
* *
* @return array * @return array
*/ */
public function getQueryParams(); public function getQueryParams(): array;
/** /**
* Return an instance with the specified query string arguments. * Return an instance with the specified query string arguments.
@ -122,7 +120,7 @@ interface ServerRequestInterface extends RequestInterface
* $_GET. * $_GET.
* @return static * @return static
*/ */
public function withQueryParams(array $query); public function withQueryParams(array $query): ServerRequestInterface;
/** /**
* Retrieve normalized file upload data. * Retrieve normalized file upload data.
@ -136,7 +134,7 @@ interface ServerRequestInterface extends RequestInterface
* @return array An array tree of UploadedFileInterface instances; an empty * @return array An array tree of UploadedFileInterface instances; an empty
* array MUST be returned if no data is present. * array MUST be returned if no data is present.
*/ */
public function getUploadedFiles(); public function getUploadedFiles(): array;
/** /**
* Create a new instance with the specified uploaded files. * Create a new instance with the specified uploaded files.
@ -149,7 +147,7 @@ interface ServerRequestInterface extends RequestInterface
* @return static * @return static
* @throws \InvalidArgumentException if an invalid structure is provided. * @throws \InvalidArgumentException if an invalid structure is provided.
*/ */
public function withUploadedFiles(array $uploadedFiles); public function withUploadedFiles(array $uploadedFiles): ServerRequestInterface;
/** /**
* Retrieve any parameters provided in the request body. * Retrieve any parameters provided in the request body.
@ -196,7 +194,7 @@ interface ServerRequestInterface extends RequestInterface
* @throws \InvalidArgumentException if an unsupported argument type is * @throws \InvalidArgumentException if an unsupported argument type is
* provided. * provided.
*/ */
public function withParsedBody($data); public function withParsedBody($data): ServerRequestInterface;
/** /**
* Retrieve attributes derived from the request. * Retrieve attributes derived from the request.
@ -209,7 +207,7 @@ interface ServerRequestInterface extends RequestInterface
* *
* @return array Attributes derived from the request. * @return array Attributes derived from the request.
*/ */
public function getAttributes(); public function getAttributes(): array;
/** /**
* Retrieve a single derived request attribute. * Retrieve a single derived request attribute.
@ -243,7 +241,7 @@ interface ServerRequestInterface extends RequestInterface
* @param mixed $value The value of the attribute. * @param mixed $value The value of the attribute.
* @return static * @return static
*/ */
public function withAttribute(string $name, $value); public function withAttribute(string $name, $value): ServerRequestInterface;
/** /**
* Return an instance that removes the specified derived request attribute. * Return an instance that removes the specified derived request attribute.
@ -259,5 +257,5 @@ interface ServerRequestInterface extends RequestInterface
* @param string $name The attribute name. * @param string $name The attribute name.
* @return static * @return static
*/ */
public function withoutAttribute(string $name); public function withoutAttribute(string $name): ServerRequestInterface;
} }

View file

@ -1,7 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace Psr\Http\Message; namespace Psr\Http\Message;
/** /**
@ -27,14 +25,14 @@ interface StreamInterface
* @see http://php.net/manual/en/language.oop5.magic.php#object.tostring * @see http://php.net/manual/en/language.oop5.magic.php#object.tostring
* @return string * @return string
*/ */
public function __toString(); public function __toString(): string;
/** /**
* Closes the stream and any underlying resources. * Closes the stream and any underlying resources.
* *
* @return void * @return void
*/ */
public function close(); public function close(): void;
/** /**
* Separates any underlying resources from the stream. * Separates any underlying resources from the stream.
@ -50,7 +48,7 @@ interface StreamInterface
* *
* @return int|null Returns the size in bytes if known, or null if unknown. * @return int|null Returns the size in bytes if known, or null if unknown.
*/ */
public function getSize(); public function getSize(): ?int;
/** /**
* Returns the current position of the file read/write pointer * Returns the current position of the file read/write pointer
@ -58,21 +56,21 @@ interface StreamInterface
* @return int Position of the file pointer * @return int Position of the file pointer
* @throws \RuntimeException on error. * @throws \RuntimeException on error.
*/ */
public function tell(); public function tell(): int;
/** /**
* Returns true if the stream is at the end of the stream. * Returns true if the stream is at the end of the stream.
* *
* @return bool * @return bool
*/ */
public function eof(); public function eof(): bool;
/** /**
* Returns whether or not the stream is seekable. * Returns whether or not the stream is seekable.
* *
* @return bool * @return bool
*/ */
public function isSeekable(); public function isSeekable(): bool;
/** /**
* Seek to a position in the stream. * Seek to a position in the stream.
@ -86,7 +84,7 @@ interface StreamInterface
* SEEK_END: Set position to end-of-stream plus offset. * SEEK_END: Set position to end-of-stream plus offset.
* @throws \RuntimeException on failure. * @throws \RuntimeException on failure.
*/ */
public function seek(int $offset, int $whence = SEEK_SET); public function seek(int $offset, int $whence = SEEK_SET): void;
/** /**
* Seek to the beginning of the stream. * Seek to the beginning of the stream.
@ -98,14 +96,14 @@ interface StreamInterface
* @link http://www.php.net/manual/en/function.fseek.php * @link http://www.php.net/manual/en/function.fseek.php
* @throws \RuntimeException on failure. * @throws \RuntimeException on failure.
*/ */
public function rewind(); public function rewind(): void;
/** /**
* Returns whether or not the stream is writable. * Returns whether or not the stream is writable.
* *
* @return bool * @return bool
*/ */
public function isWritable(); public function isWritable(): bool;
/** /**
* Write data to the stream. * Write data to the stream.
@ -114,14 +112,14 @@ interface StreamInterface
* @return int Returns the number of bytes written to the stream. * @return int Returns the number of bytes written to the stream.
* @throws \RuntimeException on failure. * @throws \RuntimeException on failure.
*/ */
public function write(string $string); public function write(string $string): int;
/** /**
* Returns whether or not the stream is readable. * Returns whether or not the stream is readable.
* *
* @return bool * @return bool
*/ */
public function isReadable(); public function isReadable(): bool;
/** /**
* Read data from the stream. * Read data from the stream.
@ -133,7 +131,7 @@ interface StreamInterface
* if no bytes are available. * if no bytes are available.
* @throws \RuntimeException if an error occurs. * @throws \RuntimeException if an error occurs.
*/ */
public function read(int $length); public function read(int $length): string;
/** /**
* Returns the remaining contents in a string * Returns the remaining contents in a string
@ -142,7 +140,7 @@ interface StreamInterface
* @throws \RuntimeException if unable to read or an error occurs while * @throws \RuntimeException if unable to read or an error occurs while
* reading. * reading.
*/ */
public function getContents(); public function getContents(): string;
/** /**
* Get stream metadata as an associative array or retrieve a specific key. * Get stream metadata as an associative array or retrieve a specific key.

View file

@ -1,7 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace Psr\Http\Message; namespace Psr\Http\Message;
/** /**
@ -30,7 +28,7 @@ interface UploadedFileInterface
* @throws \RuntimeException in cases when no stream is available or can be * @throws \RuntimeException in cases when no stream is available or can be
* created. * created.
*/ */
public function getStream(); public function getStream(): StreamInterface;
/** /**
* Move the uploaded file to a new location. * Move the uploaded file to a new location.
@ -64,7 +62,7 @@ interface UploadedFileInterface
* @throws \RuntimeException on any error during the move operation, or on * @throws \RuntimeException on any error during the move operation, or on
* the second or subsequent call to the method. * the second or subsequent call to the method.
*/ */
public function moveTo(string $targetPath); public function moveTo(string $targetPath): void;
/** /**
* Retrieve the file size. * Retrieve the file size.
@ -75,7 +73,7 @@ interface UploadedFileInterface
* *
* @return int|null The file size in bytes or null if unknown. * @return int|null The file size in bytes or null if unknown.
*/ */
public function getSize(); public function getSize(): ?int;
/** /**
* Retrieve the error associated with the uploaded file. * Retrieve the error associated with the uploaded file.
@ -91,7 +89,7 @@ interface UploadedFileInterface
* @see http://php.net/manual/en/features.file-upload.errors.php * @see http://php.net/manual/en/features.file-upload.errors.php
* @return int One of PHP's UPLOAD_ERR_XXX constants. * @return int One of PHP's UPLOAD_ERR_XXX constants.
*/ */
public function getError(); public function getError(): int;
/** /**
* Retrieve the filename sent by the client. * Retrieve the filename sent by the client.
@ -106,7 +104,7 @@ interface UploadedFileInterface
* @return string|null The filename sent by the client or null if none * @return string|null The filename sent by the client or null if none
* was provided. * was provided.
*/ */
public function getClientFilename(); public function getClientFilename(): ?string;
/** /**
* Retrieve the media type sent by the client. * Retrieve the media type sent by the client.
@ -121,5 +119,5 @@ interface UploadedFileInterface
* @return string|null The media type sent by the client or null if none * @return string|null The media type sent by the client or null if none
* was provided. * was provided.
*/ */
public function getClientMediaType(); public function getClientMediaType(): ?string;
} }

View file

@ -1,7 +1,5 @@
<?php <?php
declare(strict_types=1);
namespace Psr\Http\Message; namespace Psr\Http\Message;
/** /**
@ -40,7 +38,7 @@ interface UriInterface
* @see https://tools.ietf.org/html/rfc3986#section-3.1 * @see https://tools.ietf.org/html/rfc3986#section-3.1
* @return string The URI scheme. * @return string The URI scheme.
*/ */
public function getScheme(); public function getScheme(): string;
/** /**
* Retrieve the authority component of the URI. * Retrieve the authority component of the URI.
@ -60,7 +58,7 @@ interface UriInterface
* @see https://tools.ietf.org/html/rfc3986#section-3.2 * @see https://tools.ietf.org/html/rfc3986#section-3.2
* @return string The URI authority, in "[user-info@]host[:port]" format. * @return string The URI authority, in "[user-info@]host[:port]" format.
*/ */
public function getAuthority(); public function getAuthority(): string;
/** /**
* Retrieve the user information component of the URI. * Retrieve the user information component of the URI.
@ -77,7 +75,7 @@ interface UriInterface
* *
* @return string The URI user information, in "username[:password]" format. * @return string The URI user information, in "username[:password]" format.
*/ */
public function getUserInfo(); public function getUserInfo(): string;
/** /**
* Retrieve the host component of the URI. * Retrieve the host component of the URI.
@ -90,7 +88,7 @@ interface UriInterface
* @see http://tools.ietf.org/html/rfc3986#section-3.2.2 * @see http://tools.ietf.org/html/rfc3986#section-3.2.2
* @return string The URI host. * @return string The URI host.
*/ */
public function getHost(); public function getHost(): string;
/** /**
* Retrieve the port component of the URI. * Retrieve the port component of the URI.
@ -107,7 +105,7 @@ interface UriInterface
* *
* @return null|int The URI port. * @return null|int The URI port.
*/ */
public function getPort(); public function getPort(): ?int;
/** /**
* Retrieve the path component of the URI. * Retrieve the path component of the URI.
@ -134,7 +132,7 @@ interface UriInterface
* @see https://tools.ietf.org/html/rfc3986#section-3.3 * @see https://tools.ietf.org/html/rfc3986#section-3.3
* @return string The URI path. * @return string The URI path.
*/ */
public function getPath(); public function getPath(): string;
/** /**
* Retrieve the query string of the URI. * Retrieve the query string of the URI.
@ -156,7 +154,7 @@ interface UriInterface
* @see https://tools.ietf.org/html/rfc3986#section-3.4 * @see https://tools.ietf.org/html/rfc3986#section-3.4
* @return string The URI query string. * @return string The URI query string.
*/ */
public function getQuery(); public function getQuery(): string;
/** /**
* Retrieve the fragment component of the URI. * Retrieve the fragment component of the URI.
@ -174,7 +172,7 @@ interface UriInterface
* @see https://tools.ietf.org/html/rfc3986#section-3.5 * @see https://tools.ietf.org/html/rfc3986#section-3.5
* @return string The URI fragment. * @return string The URI fragment.
*/ */
public function getFragment(); public function getFragment(): string;
/** /**
* Return an instance with the specified scheme. * Return an instance with the specified scheme.
@ -191,7 +189,7 @@ interface UriInterface
* @return static A new instance with the specified scheme. * @return static A new instance with the specified scheme.
* @throws \InvalidArgumentException for invalid or unsupported schemes. * @throws \InvalidArgumentException for invalid or unsupported schemes.
*/ */
public function withScheme(string $scheme); public function withScheme(string $scheme): UriInterface;
/** /**
* Return an instance with the specified user information. * Return an instance with the specified user information.
@ -207,7 +205,7 @@ interface UriInterface
* @param null|string $password The password associated with $user. * @param null|string $password The password associated with $user.
* @return static A new instance with the specified user information. * @return static A new instance with the specified user information.
*/ */
public function withUserInfo(string $user, ?string $password = null); public function withUserInfo(string $user, ?string $password = null): UriInterface;
/** /**
* Return an instance with the specified host. * Return an instance with the specified host.
@ -221,7 +219,7 @@ interface UriInterface
* @return static A new instance with the specified host. * @return static A new instance with the specified host.
* @throws \InvalidArgumentException for invalid hostnames. * @throws \InvalidArgumentException for invalid hostnames.
*/ */
public function withHost(string $host); public function withHost(string $host): UriInterface;
/** /**
* Return an instance with the specified port. * Return an instance with the specified port.
@ -240,7 +238,7 @@ interface UriInterface
* @return static A new instance with the specified port. * @return static A new instance with the specified port.
* @throws \InvalidArgumentException for invalid ports. * @throws \InvalidArgumentException for invalid ports.
*/ */
public function withPort(?int $port); public function withPort(?int $port): UriInterface;
/** /**
* Return an instance with the specified path. * Return an instance with the specified path.
@ -264,7 +262,7 @@ interface UriInterface
* @return static A new instance with the specified path. * @return static A new instance with the specified path.
* @throws \InvalidArgumentException for invalid paths. * @throws \InvalidArgumentException for invalid paths.
*/ */
public function withPath(string $path); public function withPath(string $path): UriInterface;
/** /**
* Return an instance with the specified query string. * Return an instance with the specified query string.
@ -281,7 +279,7 @@ interface UriInterface
* @return static A new instance with the specified query string. * @return static A new instance with the specified query string.
* @throws \InvalidArgumentException for invalid query strings. * @throws \InvalidArgumentException for invalid query strings.
*/ */
public function withQuery(string $query); public function withQuery(string $query): UriInterface;
/** /**
* Return an instance with the specified URI fragment. * Return an instance with the specified URI fragment.
@ -297,7 +295,7 @@ interface UriInterface
* @param string $fragment The fragment to use with the new instance. * @param string $fragment The fragment to use with the new instance.
* @return static A new instance with the specified fragment. * @return static A new instance with the specified fragment.
*/ */
public function withFragment(string $fragment); public function withFragment(string $fragment): UriInterface;
/** /**
* Return the string representation as a URI reference. * Return the string representation as a URI reference.
@ -322,5 +320,5 @@ interface UriInterface
* @see http://tools.ietf.org/html/rfc3986#section-4.1 * @see http://tools.ietf.org/html/rfc3986#section-4.1
* @return string * @return string
*/ */
public function __toString(); public function __toString(): string;
} }

View file

@ -0,0 +1,28 @@
<?php
/**
* Slim Framework (https://slimframework.com)
*
* @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
*/
declare(strict_types=1);
namespace Slim\Exception;
class HttpTooManyRequestsException extends HttpSpecializedException
{
/**
* @var int
*/
protected $code = 429;
/**
* @var string
*/
protected $message = 'Too many requests.';
protected string $title = '429 Too Many Requests';
protected string $description = 'The client application has surpassed its rate limit, ' .
'or number of requests they can send in a given period of time.';
}

View file

@ -48,7 +48,7 @@
"nikic/fast-route": "^1.3", "nikic/fast-route": "^1.3",
"psr/container": "^1.0 || ^2.0", "psr/container": "^1.0 || ^2.0",
"psr/http-factory": "^1.0", "psr/http-factory": "^1.0",
"psr/http-message": "^1.1", "psr/http-message": "^1.1 || ^2.0",
"psr/http-server-handler": "^1.0", "psr/http-server-handler": "^1.0",
"psr/http-server-middleware": "^1.0", "psr/http-server-middleware": "^1.0",
"psr/log": "^1.1 || ^2.0 || ^3.0" "psr/log": "^1.1 || ^2.0 || ^3.0"
@ -56,19 +56,19 @@
"require-dev": { "require-dev": {
"ext-simplexml": "*", "ext-simplexml": "*",
"adriansuter/php-autoload-override": "^1.4", "adriansuter/php-autoload-override": "^1.4",
"guzzlehttp/psr7": "^2.5", "guzzlehttp/psr7": "^2.6",
"httpsoft/http-message": "^1.1", "httpsoft/http-message": "^1.1",
"httpsoft/http-server-request": "^1.1", "httpsoft/http-server-request": "^1.1",
"laminas/laminas-diactoros": "^2.17", "laminas/laminas-diactoros": "^2.17 || ^3",
"nyholm/psr7": "^1.8", "nyholm/psr7": "^1.8",
"nyholm/psr7-server": "^1.0", "nyholm/psr7-server": "^1.1",
"phpspec/prophecy": "^1.17", "phpspec/prophecy": "^1.19",
"phpspec/prophecy-phpunit": "^2.0", "phpspec/prophecy-phpunit": "^2.1",
"phpstan/phpstan": "^1.10", "phpstan/phpstan": "^1.10",
"phpunit/phpunit": "^9.6", "phpunit/phpunit": "^9.6",
"slim/http": "^1.3", "slim/http": "^1.3",
"slim/psr7": "^1.6", "slim/psr7": "^1.6",
"squizlabs/php_codesniffer": "^3.7" "squizlabs/php_codesniffer": "^3.9"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {

View file

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

View file

@ -0,0 +1,5 @@
CHANGELOG
=========
The changelog is maintained for all Symfony contracts at the following URL:
https://github.com/symfony/contracts/blob/main/CHANGELOG.md

View file

@ -0,0 +1,57 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Contracts\Cache;
use Psr\Cache\CacheItemInterface;
use Psr\Cache\InvalidArgumentException;
/**
* Covers most simple to advanced caching needs.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
interface CacheInterface
{
/**
* Fetches a value from the pool or computes it if not found.
*
* On cache misses, a callback is called that should return the missing value.
* This callback is given a PSR-6 CacheItemInterface instance corresponding to the
* requested key, that could be used e.g. for expiration control. It could also
* be an ItemInterface instance when its additional features are needed.
*
* @param string $key The key of the item to retrieve from the cache
* @param callable|CallbackInterface $callback Should return the computed value for the given key/item
* @param float|null $beta A float that, as it grows, controls the likeliness of triggering
* early expiration. 0 disables it, INF forces immediate expiration.
* The default (or providing null) is implementation dependent but should
* typically be 1.0, which should provide optimal stampede protection.
* See https://en.wikipedia.org/wiki/Cache_stampede#Probabilistic_early_expiration
* @param array &$metadata The metadata of the cached item {@see ItemInterface::getMetadata()}
*
* @return mixed
*
* @throws InvalidArgumentException When $key is not valid or when $beta is negative
*/
public function get(string $key, callable $callback, float $beta = null, array &$metadata = null);
/**
* Removes an item from the pool.
*
* @param string $key The key to delete
*
* @throws InvalidArgumentException When $key is not valid
*
* @return bool True if the item was successfully removed, false if there was any error
*/
public function delete(string $key): bool;
}

View file

@ -0,0 +1,80 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Contracts\Cache;
use Psr\Cache\CacheItemPoolInterface;
use Psr\Cache\InvalidArgumentException;
use Psr\Log\LoggerInterface;
// Help opcache.preload discover always-needed symbols
class_exists(InvalidArgumentException::class);
/**
* An implementation of CacheInterface for PSR-6 CacheItemPoolInterface classes.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
trait CacheTrait
{
/**
* {@inheritdoc}
*
* @return mixed
*/
public function get(string $key, callable $callback, float $beta = null, array &$metadata = null)
{
return $this->doGet($this, $key, $callback, $beta, $metadata);
}
/**
* {@inheritdoc}
*/
public function delete(string $key): bool
{
return $this->deleteItem($key);
}
private function doGet(CacheItemPoolInterface $pool, string $key, callable $callback, ?float $beta, array &$metadata = null, LoggerInterface $logger = null)
{
if (0 > $beta = $beta ?? 1.0) {
throw new class(sprintf('Argument "$beta" provided to "%s::get()" must be a positive number, %f given.', static::class, $beta)) extends \InvalidArgumentException implements InvalidArgumentException { };
}
$item = $pool->getItem($key);
$recompute = !$item->isHit() || \INF === $beta;
$metadata = $item instanceof ItemInterface ? $item->getMetadata() : [];
if (!$recompute && $metadata) {
$expiry = $metadata[ItemInterface::METADATA_EXPIRY] ?? false;
$ctime = $metadata[ItemInterface::METADATA_CTIME] ?? false;
if ($recompute = $ctime && $expiry && $expiry <= ($now = microtime(true)) - $ctime / 1000 * $beta * log(random_int(1, \PHP_INT_MAX) / \PHP_INT_MAX)) {
// force applying defaultLifetime to expiry
$item->expiresAt(null);
$logger && $logger->info('Item "{key}" elected for early recomputation {delta}s before its expiration', [
'key' => $key,
'delta' => sprintf('%.1f', $expiry - $now),
]);
}
}
if ($recompute) {
$save = true;
$item->set($callback($item, $save));
if ($save) {
$pool->save($item);
}
}
return $item->get();
}
}

View file

@ -0,0 +1,30 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Contracts\Cache;
use Psr\Cache\CacheItemInterface;
/**
* Computes and returns the cached value of an item.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
interface CallbackInterface
{
/**
* @param CacheItemInterface|ItemInterface $item The item to compute the value for
* @param bool &$save Should be set to false when the value should not be saved in the pool
*
* @return mixed The computed value for the passed item
*/
public function __invoke(CacheItemInterface $item, bool &$save);
}

View file

@ -0,0 +1,65 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Contracts\Cache;
use Psr\Cache\CacheException;
use Psr\Cache\CacheItemInterface;
use Psr\Cache\InvalidArgumentException;
/**
* Augments PSR-6's CacheItemInterface with support for tags and metadata.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
interface ItemInterface extends CacheItemInterface
{
/**
* References the Unix timestamp stating when the item will expire.
*/
public const METADATA_EXPIRY = 'expiry';
/**
* References the time the item took to be created, in milliseconds.
*/
public const METADATA_CTIME = 'ctime';
/**
* References the list of tags that were assigned to the item, as string[].
*/
public const METADATA_TAGS = 'tags';
/**
* Reserved characters that cannot be used in a key or tag.
*/
public const RESERVED_CHARACTERS = '{}()/\@:';
/**
* Adds a tag to a cache item.
*
* Tags are strings that follow the same validation rules as keys.
*
* @param string|string[] $tags A tag or array of tags
*
* @return $this
*
* @throws InvalidArgumentException When $tag is not valid
* @throws CacheException When the item comes from a pool that is not tag-aware
*/
public function tag($tags): self;
/**
* Returns a list of metadata info that were saved alongside with the cached value.
*
* See ItemInterface::METADATA_* consts for keys potentially found in the returned array.
*/
public function getMetadata(): array;
}

View file

@ -0,0 +1,19 @@
Copyright (c) 2018-2022 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
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View file

@ -0,0 +1,9 @@
Symfony Cache 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
that already have battle tested implementations.
See https://github.com/symfony/contracts/blob/main/README.md for more information.

View file

@ -0,0 +1,38 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Contracts\Cache;
use Psr\Cache\InvalidArgumentException;
/**
* Allows invalidating cached items using tags.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
interface TagAwareCacheInterface extends CacheInterface
{
/**
* Invalidates cached items using tags.
*
* When implemented on a PSR-6 pool, invalidation should not apply
* to deferred items. Instead, they should be committed as usual.
* This allows replacing old tagged values by new ones without
* race conditions.
*
* @param string[] $tags An array of tags to invalidate
*
* @return bool True on success
*
* @throws InvalidArgumentException When $tags is not valid
*/
public function invalidateTags(array $tags);
}

View file

@ -0,0 +1,38 @@
{
"name": "symfony/cache-contracts",
"type": "library",
"description": "Generic abstractions related to caching",
"keywords": ["abstractions", "contracts", "decoupling", "interfaces", "interoperability", "standards"],
"homepage": "https://symfony.com",
"license": "MIT",
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"require": {
"php": ">=7.2.5",
"psr/cache": "^1.0|^2.0|^3.0"
},
"suggest": {
"symfony/cache-implementation": ""
},
"autoload": {
"psr-4": { "Symfony\\Contracts\\Cache\\": "" }
},
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-main": "2.5-dev"
},
"thanks": {
"name": "symfony/contracts",
"url": "https://github.com/symfony/contracts"
}
}
}

View file

@ -11,37 +11,32 @@
namespace Symfony\Component\Cache\Adapter; namespace Symfony\Component\Cache\Adapter;
use Psr\Cache\CacheItemInterface;
use Psr\Log\LoggerAwareInterface; use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Symfony\Component\Cache\CacheItem; use Symfony\Component\Cache\CacheItem;
use Symfony\Component\Cache\Exception\InvalidArgumentException; use Symfony\Component\Cache\Exception\InvalidArgumentException;
use Symfony\Component\Cache\ResettableInterface; use Symfony\Component\Cache\ResettableInterface;
use Symfony\Component\Cache\Traits\AbstractTrait; use Symfony\Component\Cache\Traits\AbstractAdapterTrait;
use Symfony\Component\Cache\Traits\ContractsTrait;
use Symfony\Contracts\Cache\CacheInterface;
/** /**
* @author Nicolas Grekas <p@tchwork.com> * @author Nicolas Grekas <p@tchwork.com>
*/ */
abstract class AbstractAdapter implements AdapterInterface, LoggerAwareInterface, ResettableInterface abstract class AbstractAdapter implements AdapterInterface, CacheInterface, LoggerAwareInterface, ResettableInterface
{ {
use AbstractAdapterTrait;
use ContractsTrait;
/** /**
* @internal * @internal
*/ */
const NS_SEPARATOR = ':'; protected const NS_SEPARATOR = ':';
use AbstractTrait;
private static $apcuSupported; private static $apcuSupported;
private static $phpFilesSupported; private static $phpFilesSupported;
private $createCacheItem; protected function __construct(string $namespace = '', int $defaultLifetime = 0)
private $mergeByLifetime;
/**
* @param string $namespace
* @param int $defaultLifetime
*/
protected function __construct($namespace = '', $defaultLifetime = 0)
{ {
$this->namespace = '' === $namespace ? '' : CacheItem::validateKey($namespace).static::NS_SEPARATOR; $this->namespace = '' === $namespace ? '' : CacheItem::validateKey($namespace).static::NS_SEPARATOR;
if (null !== $this->maxIdLength && \strlen($namespace) > $this->maxIdLength - 24) { if (null !== $this->maxIdLength && \strlen($namespace) > $this->maxIdLength - 24) {
@ -51,31 +46,45 @@ abstract class AbstractAdapter implements AdapterInterface, LoggerAwareInterface
static function ($key, $value, $isHit) { static function ($key, $value, $isHit) {
$item = new CacheItem(); $item = new CacheItem();
$item->key = $key; $item->key = $key;
$item->value = $value; $item->value = $v = $value;
$item->isHit = $isHit; $item->isHit = $isHit;
// Detect wrapped values that encode for their expiry and creation duration
// For compactness, these values are packed in the key of an array using
// magic numbers in the form 9D-..-..-..-..-00-..-..-..-5F
if (\is_array($v) && 1 === \count($v) && 10 === \strlen($k = (string) array_key_first($v)) && "\x9D" === $k[0] && "\0" === $k[5] && "\x5F" === $k[9]) {
$item->value = $v[$k];
$v = unpack('Ve/Nc', substr($k, 1, -1));
$item->metadata[CacheItem::METADATA_EXPIRY] = $v['e'] + CacheItem::METADATA_EXPIRY_OFFSET;
$item->metadata[CacheItem::METADATA_CTIME] = $v['c'];
}
return $item; return $item;
}, },
null, null,
CacheItem::class CacheItem::class
); );
$getId = function ($key) { return $this->getId((string) $key); }; $getId = \Closure::fromCallable([$this, 'getId']);
$this->mergeByLifetime = \Closure::bind( $this->mergeByLifetime = \Closure::bind(
static function ($deferred, $namespace, &$expiredIds) use ($getId, $defaultLifetime) { static function ($deferred, $namespace, &$expiredIds) use ($getId, $defaultLifetime) {
$byLifetime = []; $byLifetime = [];
$now = time(); $now = microtime(true);
$expiredIds = []; $expiredIds = [];
foreach ($deferred as $key => $item) { foreach ($deferred as $key => $item) {
$key = (string) $key;
if (null === $item->expiry) { if (null === $item->expiry) {
$byLifetime[0 < $defaultLifetime ? $defaultLifetime : 0][$getId($key)] = $item->value; $ttl = 0 < $defaultLifetime ? $defaultLifetime : 0;
} elseif (0 === $item->expiry) { } elseif (!$item->expiry) {
$byLifetime[0][$getId($key)] = $item->value; $ttl = 0;
} elseif ($item->expiry > $now) { } elseif (0 >= $ttl = (int) (0.1 + $item->expiry - $now)) {
$byLifetime[$item->expiry - $now][$getId($key)] = $item->value;
} else {
$expiredIds[] = $getId($key); $expiredIds[] = $getId($key);
continue;
} }
if (isset(($metadata = $item->newMetadata)[CacheItem::METADATA_TAGS])) {
unset($metadata[CacheItem::METADATA_TAGS]);
}
// For compactness, expiry and creation duration are packed in the key of an array, using magic numbers as separators
$byLifetime[$ttl][$getId($key)] = $metadata ? ["\x9D".pack('VN', (int) (0.1 + $metadata[self::METADATA_EXPIRY] - self::METADATA_EXPIRY_OFFSET), $metadata[self::METADATA_CTIME])."\x5F" => $item->value] : $item->value;
} }
return $byLifetime; return $byLifetime;
@ -86,6 +95,10 @@ abstract class AbstractAdapter implements AdapterInterface, LoggerAwareInterface
} }
/** /**
* Returns the best possible adapter that your runtime supports.
*
* Using ApcuAdapter makes system caches compatible with read-only filesystems.
*
* @param string $namespace * @param string $namespace
* @param int $defaultLifetime * @param int $defaultLifetime
* @param string $version * @param string $version
@ -95,37 +108,25 @@ abstract class AbstractAdapter implements AdapterInterface, LoggerAwareInterface
*/ */
public static function createSystemCache($namespace, $defaultLifetime, $version, $directory, LoggerInterface $logger = null) public static function createSystemCache($namespace, $defaultLifetime, $version, $directory, LoggerInterface $logger = null)
{ {
if (null === self::$apcuSupported) { $opcache = new PhpFilesAdapter($namespace, $defaultLifetime, $directory, true);
self::$apcuSupported = ApcuAdapter::isSupported();
}
if (!self::$apcuSupported && null === self::$phpFilesSupported) {
self::$phpFilesSupported = PhpFilesAdapter::isSupported();
}
if (self::$phpFilesSupported) {
$opcache = new PhpFilesAdapter($namespace, $defaultLifetime, $directory);
if (null !== $logger) { if (null !== $logger) {
$opcache->setLogger($logger); $opcache->setLogger($logger);
} }
if (!self::$apcuSupported = self::$apcuSupported ?? ApcuAdapter::isSupported()) {
return $opcache; return $opcache;
} }
$fs = new FilesystemAdapter($namespace, $defaultLifetime, $directory); if (\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) && !filter_var(\ini_get('apc.enable_cli'), \FILTER_VALIDATE_BOOLEAN)) {
if (null !== $logger) { return $opcache;
$fs->setLogger($logger);
}
if (!self::$apcuSupported || (\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) && !filter_var(ini_get('apc.enable_cli'), \FILTER_VALIDATE_BOOLEAN))) {
return $fs;
} }
$apcu = new ApcuAdapter($namespace, (int) $defaultLifetime / 5, $version); $apcu = new ApcuAdapter($namespace, intdiv($defaultLifetime, 5), $version);
if (null !== $logger) { if (null !== $logger) {
$apcu->setLogger($logger); $apcu->setLogger($logger);
} }
return new ChainAdapter([$apcu, $fs]); return new ChainAdapter([$apcu, $opcache]);
} }
public static function createConnection($dsn, array $options = []) public static function createConnection($dsn, array $options = [])
@ -133,10 +134,10 @@ abstract class AbstractAdapter implements AdapterInterface, LoggerAwareInterface
if (!\is_string($dsn)) { if (!\is_string($dsn)) {
throw new InvalidArgumentException(sprintf('The "%s()" method expect argument #1 to be string, "%s" given.', __METHOD__, \gettype($dsn))); throw new InvalidArgumentException(sprintf('The "%s()" method expect argument #1 to be string, "%s" given.', __METHOD__, \gettype($dsn)));
} }
if (0 === strpos($dsn, 'redis://')) { if (str_starts_with($dsn, 'redis:') || str_starts_with($dsn, 'rediss:')) {
return RedisAdapter::createConnection($dsn, $options); return RedisAdapter::createConnection($dsn, $options);
} }
if (0 === strpos($dsn, 'memcached://')) { if (str_starts_with($dsn, 'memcached:')) {
return MemcachedAdapter::createConnection($dsn, $options); return MemcachedAdapter::createConnection($dsn, $options);
} }
@ -145,81 +146,8 @@ abstract class AbstractAdapter implements AdapterInterface, LoggerAwareInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ *
public function getItem($key) * @return bool
{
if ($this->deferred) {
$this->commit();
}
$id = $this->getId($key);
$f = $this->createCacheItem;
$isHit = false;
$value = null;
try {
foreach ($this->doFetch([$id]) as $value) {
$isHit = true;
}
} catch (\Exception $e) {
CacheItem::log($this->logger, 'Failed to fetch key "{key}"', ['key' => $key, 'exception' => $e]);
}
return $f($key, $value, $isHit);
}
/**
* {@inheritdoc}
*/
public function getItems(array $keys = [])
{
if ($this->deferred) {
$this->commit();
}
$ids = [];
foreach ($keys as $key) {
$ids[] = $this->getId($key);
}
try {
$items = $this->doFetch($ids);
} catch (\Exception $e) {
CacheItem::log($this->logger, 'Failed to fetch requested items', ['keys' => $keys, 'exception' => $e]);
$items = [];
}
$ids = array_combine($ids, $keys);
return $this->generateItems($items, $ids);
}
/**
* {@inheritdoc}
*/
public function save(CacheItemInterface $item)
{
if (!$item instanceof CacheItem) {
return false;
}
$this->deferred[$item->getKey()] = $item;
return $this->commit();
}
/**
* {@inheritdoc}
*/
public function saveDeferred(CacheItemInterface $item)
{
if (!$item instanceof CacheItem) {
return false;
}
$this->deferred[$item->getKey()] = $item;
return true;
}
/**
* {@inheritdoc}
*/ */
public function commit() public function commit()
{ {
@ -229,7 +157,12 @@ abstract class AbstractAdapter implements AdapterInterface, LoggerAwareInterface
$retry = $this->deferred = []; $retry = $this->deferred = [];
if ($expiredIds) { if ($expiredIds) {
try {
$this->doDelete($expiredIds); $this->doDelete($expiredIds);
} catch (\Exception $e) {
$ok = false;
CacheItem::log($this->logger, 'Failed to delete expired items: '.$e->getMessage(), ['exception' => $e, 'cache-adapter' => get_debug_type($this)]);
}
} }
foreach ($byLifetime as $lifetime => $values) { foreach ($byLifetime as $lifetime => $values) {
try { try {
@ -244,7 +177,8 @@ abstract class AbstractAdapter implements AdapterInterface, LoggerAwareInterface
$ok = false; $ok = false;
$v = $values[$id]; $v = $values[$id];
$type = \is_object($v) ? \get_class($v) : \gettype($v); $type = \is_object($v) ? \get_class($v) : \gettype($v);
CacheItem::log($this->logger, 'Failed to save key "{key}" ({type})', ['key' => substr($id, \strlen($this->namespace)), 'type' => $type, 'exception' => $e instanceof \Exception ? $e : null]); $message = sprintf('Failed to save key "{key}" of type %s%s', $type, $e instanceof \Exception ? ': '.$e->getMessage() : '.');
CacheItem::log($this->logger, $message, ['key' => substr($id, \strlen($this->namespace)), 'exception' => $e instanceof \Exception ? $e : null]);
} }
} else { } else {
foreach ($values as $id => $v) { foreach ($values as $id => $v) {
@ -266,49 +200,11 @@ abstract class AbstractAdapter implements AdapterInterface, LoggerAwareInterface
} }
$ok = false; $ok = false;
$type = \is_object($v) ? \get_class($v) : \gettype($v); $type = \is_object($v) ? \get_class($v) : \gettype($v);
CacheItem::log($this->logger, 'Failed to save key "{key}" ({type})', ['key' => substr($id, \strlen($this->namespace)), 'type' => $type, 'exception' => $e instanceof \Exception ? $e : null]); $message = sprintf('Failed to save key "{key}" of type %s%s', $type, $e instanceof \Exception ? ': '.$e->getMessage() : '.');
CacheItem::log($this->logger, $message, ['key' => substr($id, \strlen($this->namespace)), 'exception' => $e instanceof \Exception ? $e : null]);
} }
} }
return $ok; return $ok;
} }
public function __sleep()
{
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
}
public function __wakeup()
{
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}
public function __destruct()
{
if ($this->deferred) {
$this->commit();
}
}
private function generateItems($items, &$keys)
{
$f = $this->createCacheItem;
try {
foreach ($items as $id => $value) {
if (!isset($keys[$id])) {
$id = key($keys);
}
$key = $keys[$id];
unset($keys[$id]);
yield $key => $f($key, $value, true);
}
} catch (\Exception $e) {
CacheItem::log($this->logger, 'Failed to fetch requested items', ['keys' => array_values($keys), 'exception' => $e]);
}
foreach ($keys as $key) {
yield $key => $f($key, null, false);
}
}
} }

View file

@ -0,0 +1,334 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Cache\Adapter;
use Psr\Log\LoggerAwareInterface;
use Symfony\Component\Cache\CacheItem;
use Symfony\Component\Cache\Exception\InvalidArgumentException;
use Symfony\Component\Cache\ResettableInterface;
use Symfony\Component\Cache\Traits\AbstractAdapterTrait;
use Symfony\Component\Cache\Traits\ContractsTrait;
use Symfony\Contracts\Cache\TagAwareCacheInterface;
/**
* Abstract for native TagAware adapters.
*
* To keep info on tags, the tags are both serialized as part of cache value and provided as tag ids
* to Adapters on operations when needed for storage to doSave(), doDelete() & doInvalidate().
*
* @author Nicolas Grekas <p@tchwork.com>
* @author André Rømcke <andre.romcke+symfony@gmail.com>
*
* @internal
*/
abstract class AbstractTagAwareAdapter implements TagAwareAdapterInterface, TagAwareCacheInterface, LoggerAwareInterface, ResettableInterface
{
use AbstractAdapterTrait;
use ContractsTrait;
private const TAGS_PREFIX = "\0tags\0";
protected function __construct(string $namespace = '', int $defaultLifetime = 0)
{
$this->namespace = '' === $namespace ? '' : CacheItem::validateKey($namespace).':';
if (null !== $this->maxIdLength && \strlen($namespace) > $this->maxIdLength - 24) {
throw new InvalidArgumentException(sprintf('Namespace must be %d chars max, %d given ("%s").', $this->maxIdLength - 24, \strlen($namespace), $namespace));
}
$this->createCacheItem = \Closure::bind(
static function ($key, $value, $isHit) {
$item = new CacheItem();
$item->key = $key;
$item->isTaggable = true;
// If structure does not match what we expect return item as is (no value and not a hit)
if (!\is_array($value) || !\array_key_exists('value', $value)) {
return $item;
}
$item->isHit = $isHit;
// Extract value, tags and meta data from the cache value
$item->value = $value['value'];
$item->metadata[CacheItem::METADATA_TAGS] = $value['tags'] ?? [];
if (isset($value['meta'])) {
// For compactness these values are packed, & expiry is offset to reduce size
$v = unpack('Ve/Nc', $value['meta']);
$item->metadata[CacheItem::METADATA_EXPIRY] = $v['e'] + CacheItem::METADATA_EXPIRY_OFFSET;
$item->metadata[CacheItem::METADATA_CTIME] = $v['c'];
}
return $item;
},
null,
CacheItem::class
);
$getId = \Closure::fromCallable([$this, 'getId']);
$tagPrefix = self::TAGS_PREFIX;
$this->mergeByLifetime = \Closure::bind(
static function ($deferred, &$expiredIds) use ($getId, $tagPrefix, $defaultLifetime) {
$byLifetime = [];
$now = microtime(true);
$expiredIds = [];
foreach ($deferred as $key => $item) {
$key = (string) $key;
if (null === $item->expiry) {
$ttl = 0 < $defaultLifetime ? $defaultLifetime : 0;
} elseif (!$item->expiry) {
$ttl = 0;
} elseif (0 >= $ttl = (int) (0.1 + $item->expiry - $now)) {
$expiredIds[] = $getId($key);
continue;
}
// Store Value and Tags on the cache value
if (isset(($metadata = $item->newMetadata)[CacheItem::METADATA_TAGS])) {
$value = ['value' => $item->value, 'tags' => $metadata[CacheItem::METADATA_TAGS]];
unset($metadata[CacheItem::METADATA_TAGS]);
} else {
$value = ['value' => $item->value, 'tags' => []];
}
if ($metadata) {
// For compactness, expiry and creation duration are packed, using magic numbers as separators
$value['meta'] = pack('VN', (int) (0.1 + $metadata[self::METADATA_EXPIRY] - self::METADATA_EXPIRY_OFFSET), $metadata[self::METADATA_CTIME]);
}
// Extract tag changes, these should be removed from values in doSave()
$value['tag-operations'] = ['add' => [], 'remove' => []];
$oldTags = $item->metadata[CacheItem::METADATA_TAGS] ?? [];
foreach (array_diff($value['tags'], $oldTags) as $addedTag) {
$value['tag-operations']['add'][] = $getId($tagPrefix.$addedTag);
}
foreach (array_diff($oldTags, $value['tags']) as $removedTag) {
$value['tag-operations']['remove'][] = $getId($tagPrefix.$removedTag);
}
$byLifetime[$ttl][$getId($key)] = $value;
$item->metadata = $item->newMetadata;
}
return $byLifetime;
},
null,
CacheItem::class
);
}
/**
* Persists several cache items immediately.
*
* @param array $values The values to cache, indexed by their cache identifier
* @param int $lifetime The lifetime of the cached values, 0 for persisting until manual cleaning
* @param array[] $addTagData Hash where key is tag id, and array value is list of cache id's to add to tag
* @param array[] $removeTagData Hash where key is tag id, and array value is list of cache id's to remove to tag
*
* @return array The identifiers that failed to be cached or a boolean stating if caching succeeded or not
*/
abstract protected function doSave(array $values, int $lifetime, array $addTagData = [], array $removeTagData = []): array;
/**
* Removes multiple items from the pool and their corresponding tags.
*
* @param array $ids An array of identifiers that should be removed from the pool
*
* @return bool True if the items were successfully removed, false otherwise
*/
abstract protected function doDelete(array $ids);
/**
* Removes relations between tags and deleted items.
*
* @param array $tagData Array of tag => key identifiers that should be removed from the pool
*/
abstract protected function doDeleteTagRelations(array $tagData): bool;
/**
* Invalidates cached items using tags.
*
* @param string[] $tagIds An array of tags to invalidate, key is tag and value is tag id
*
* @return bool True on success
*/
abstract protected function doInvalidate(array $tagIds): bool;
/**
* Delete items and yields the tags they were bound to.
*/
protected function doDeleteYieldTags(array $ids): iterable
{
foreach ($this->doFetch($ids) as $id => $value) {
yield $id => \is_array($value) && \is_array($value['tags'] ?? null) ? $value['tags'] : [];
}
$this->doDelete($ids);
}
/**
* {@inheritdoc}
*/
public function commit(): bool
{
$ok = true;
$byLifetime = $this->mergeByLifetime;
$byLifetime = $byLifetime($this->deferred, $expiredIds);
$retry = $this->deferred = [];
if ($expiredIds) {
// Tags are not cleaned up in this case, however that is done on invalidateTags().
try {
$this->doDelete($expiredIds);
} catch (\Exception $e) {
$ok = false;
CacheItem::log($this->logger, 'Failed to delete expired items: '.$e->getMessage(), ['exception' => $e, 'cache-adapter' => get_debug_type($this)]);
}
}
foreach ($byLifetime as $lifetime => $values) {
try {
$values = $this->extractTagData($values, $addTagData, $removeTagData);
$e = $this->doSave($values, $lifetime, $addTagData, $removeTagData);
} catch (\Exception $e) {
}
if (true === $e || [] === $e) {
continue;
}
if (\is_array($e) || 1 === \count($values)) {
foreach (\is_array($e) ? $e : array_keys($values) as $id) {
$ok = false;
$v = $values[$id];
$type = \is_object($v) ? \get_class($v) : \gettype($v);
$message = sprintf('Failed to save key "{key}" of type %s%s', $type, $e instanceof \Exception ? ': '.$e->getMessage() : '.');
CacheItem::log($this->logger, $message, ['key' => substr($id, \strlen($this->namespace)), 'exception' => $e instanceof \Exception ? $e : null]);
}
} else {
foreach ($values as $id => $v) {
$retry[$lifetime][] = $id;
}
}
}
// When bulk-save failed, retry each item individually
foreach ($retry as $lifetime => $ids) {
foreach ($ids as $id) {
try {
$v = $byLifetime[$lifetime][$id];
$values = $this->extractTagData([$id => $v], $addTagData, $removeTagData);
$e = $this->doSave($values, $lifetime, $addTagData, $removeTagData);
} catch (\Exception $e) {
}
if (true === $e || [] === $e) {
continue;
}
$ok = false;
$type = \is_object($v) ? \get_class($v) : \gettype($v);
$message = sprintf('Failed to save key "{key}" of type %s%s', $type, $e instanceof \Exception ? ': '.$e->getMessage() : '.');
CacheItem::log($this->logger, $message, ['key' => substr($id, \strlen($this->namespace)), 'exception' => $e instanceof \Exception ? $e : null]);
}
}
return $ok;
}
/**
* {@inheritdoc}
*/
public function deleteItems(array $keys): bool
{
if (!$keys) {
return true;
}
$ok = true;
$ids = [];
$tagData = [];
foreach ($keys as $key) {
$ids[$key] = $this->getId($key);
unset($this->deferred[$key]);
}
try {
foreach ($this->doDeleteYieldTags(array_values($ids)) as $id => $tags) {
foreach ($tags as $tag) {
$tagData[$this->getId(self::TAGS_PREFIX.$tag)][] = $id;
}
}
} catch (\Exception $e) {
$ok = false;
}
try {
if ((!$tagData || $this->doDeleteTagRelations($tagData)) && $ok) {
return true;
}
} catch (\Exception $e) {
}
// When bulk-delete failed, retry each item individually
foreach ($ids as $key => $id) {
try {
$e = null;
if ($this->doDelete([$id])) {
continue;
}
} catch (\Exception $e) {
}
$message = 'Failed to delete key "{key}"'.($e instanceof \Exception ? ': '.$e->getMessage() : '.');
CacheItem::log($this->logger, $message, ['key' => $key, 'exception' => $e]);
$ok = false;
}
return $ok;
}
/**
* {@inheritdoc}
*/
public function invalidateTags(array $tags)
{
if (empty($tags)) {
return false;
}
$tagIds = [];
foreach (array_unique($tags) as $tag) {
$tagIds[] = $this->getId(self::TAGS_PREFIX.$tag);
}
try {
if ($this->doInvalidate($tagIds)) {
return true;
}
} catch (\Exception $e) {
CacheItem::log($this->logger, 'Failed to invalidate tags: '.$e->getMessage(), ['exception' => $e, 'cache-adapter' => get_debug_type($this)]);
}
return false;
}
/**
* Extracts tags operation data from $values set in mergeByLifetime, and returns values without it.
*/
private function extractTagData(array $values, ?array &$addTagData, ?array &$removeTagData): array
{
$addTagData = $removeTagData = [];
foreach ($values as $id => $value) {
foreach ($value['tag-operations']['add'] as $tag => $tagId) {
$addTagData[$tagId][] = $id;
}
foreach ($value['tag-operations']['remove'] as $tag => $tagId) {
$removeTagData[$tagId][] = $id;
}
unset($values[$id]['tag-operations']);
}
return $values;
}
}

View file

@ -14,6 +14,9 @@ namespace Symfony\Component\Cache\Adapter;
use Psr\Cache\CacheItemPoolInterface; use Psr\Cache\CacheItemPoolInterface;
use Symfony\Component\Cache\CacheItem; use Symfony\Component\Cache\CacheItem;
// Help opcache.preload discover always-needed symbols
class_exists(CacheItem::class);
/** /**
* Interface for adapters managing instances of Symfony's CacheItem. * Interface for adapters managing instances of Symfony's CacheItem.
* *
@ -34,4 +37,13 @@ interface AdapterInterface extends CacheItemPoolInterface
* @return \Traversable|CacheItem[] * @return \Traversable|CacheItem[]
*/ */
public function getItems(array $keys = []); public function getItems(array $keys = []);
/**
* {@inheritdoc}
*
* @param string $prefix
*
* @return bool
*/
public function clear(/* string $prefix = '' */);
} }

View file

@ -18,13 +18,9 @@ class ApcuAdapter extends AbstractAdapter
use ApcuTrait; use ApcuTrait;
/** /**
* @param string $namespace
* @param int $defaultLifetime
* @param string|null $version
*
* @throws CacheException if APCu is not enabled * @throws CacheException if APCu is not enabled
*/ */
public function __construct($namespace = '', $defaultLifetime = 0, $version = null) public function __construct(string $namespace = '', int $defaultLifetime = 0, string $version = null)
{ {
$this->init($namespace, $defaultLifetime, $version); $this->init($namespace, $defaultLifetime, $version);
} }

View file

@ -16,11 +16,12 @@ use Psr\Log\LoggerAwareInterface;
use Symfony\Component\Cache\CacheItem; use Symfony\Component\Cache\CacheItem;
use Symfony\Component\Cache\ResettableInterface; use Symfony\Component\Cache\ResettableInterface;
use Symfony\Component\Cache\Traits\ArrayTrait; use Symfony\Component\Cache\Traits\ArrayTrait;
use Symfony\Contracts\Cache\CacheInterface;
/** /**
* @author Nicolas Grekas <p@tchwork.com> * @author Nicolas Grekas <p@tchwork.com>
*/ */
class ArrayAdapter implements AdapterInterface, LoggerAwareInterface, ResettableInterface class ArrayAdapter implements AdapterInterface, CacheInterface, LoggerAwareInterface, ResettableInterface
{ {
use ArrayTrait; use ArrayTrait;
@ -28,10 +29,9 @@ class ArrayAdapter implements AdapterInterface, LoggerAwareInterface, Resettable
private $defaultLifetime; private $defaultLifetime;
/** /**
* @param int $defaultLifetime
* @param bool $storeSerialized Disabling serialization can lead to cache corruptions when storing mutable values but increases performance otherwise * @param bool $storeSerialized Disabling serialization can lead to cache corruptions when storing mutable values but increases performance otherwise
*/ */
public function __construct($defaultLifetime = 0, $storeSerialized = true) public function __construct(int $defaultLifetime = 0, bool $storeSerialized = true)
{ {
$this->defaultLifetime = $defaultLifetime; $this->defaultLifetime = $defaultLifetime;
$this->storeSerialized = $storeSerialized; $this->storeSerialized = $storeSerialized;
@ -49,27 +49,35 @@ class ArrayAdapter implements AdapterInterface, LoggerAwareInterface, Resettable
); );
} }
/**
* {@inheritdoc}
*/
public function get(string $key, callable $callback, float $beta = null, array &$metadata = null)
{
$item = $this->getItem($key);
$metadata = $item->getMetadata();
// ArrayAdapter works in memory, we don't care about stampede protection
if (\INF === $beta || !$item->isHit()) {
$save = true;
$item->set($callback($item, $save));
if ($save) {
$this->save($item);
}
}
return $item->get();
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getItem($key) public function getItem($key)
{ {
$isHit = $this->hasItem($key); if (!$isHit = $this->hasItem($key)) {
try {
if (!$isHit) {
$this->values[$key] = $value = null; $this->values[$key] = $value = null;
} elseif (!$this->storeSerialized) { } else {
$value = $this->values[$key]; $value = $this->storeSerialized ? $this->unfreeze($key, $isHit) : $this->values[$key];
} elseif ('b:0;' === $value = $this->values[$key]) {
$value = false;
} elseif (false === $value = unserialize($value)) {
$this->values[$key] = $value = null;
$isHit = false;
}
} catch (\Exception $e) {
CacheItem::log($this->logger, 'Failed to unserialize key "{key}"', ['key' => $key, 'exception' => $e]);
$this->values[$key] = $value = null;
$isHit = false;
} }
$f = $this->createCacheItem; $f = $this->createCacheItem;
@ -82,14 +90,18 @@ class ArrayAdapter implements AdapterInterface, LoggerAwareInterface, Resettable
public function getItems(array $keys = []) public function getItems(array $keys = [])
{ {
foreach ($keys as $key) { foreach ($keys as $key) {
if (!\is_string($key) || !isset($this->expiries[$key])) {
CacheItem::validateKey($key); CacheItem::validateKey($key);
} }
}
return $this->generateItems($keys, time(), $this->createCacheItem); return $this->generateItems($keys, microtime(true), $this->createCacheItem);
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function deleteItems(array $keys) public function deleteItems(array $keys)
{ {
@ -102,6 +114,8 @@ class ArrayAdapter implements AdapterInterface, LoggerAwareInterface, Resettable
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function save(CacheItemInterface $item) public function save(CacheItemInterface $item)
{ {
@ -113,37 +127,32 @@ class ArrayAdapter implements AdapterInterface, LoggerAwareInterface, Resettable
$value = $item["\0*\0value"]; $value = $item["\0*\0value"];
$expiry = $item["\0*\0expiry"]; $expiry = $item["\0*\0expiry"];
if (0 === $expiry) { if (null !== $expiry) {
if (!$expiry) {
$expiry = \PHP_INT_MAX; $expiry = \PHP_INT_MAX;
} } elseif ($expiry <= microtime(true)) {
if (null !== $expiry && $expiry <= time()) {
$this->deleteItem($key); $this->deleteItem($key);
return true; return true;
} }
if ($this->storeSerialized) { }
try { if ($this->storeSerialized && null === $value = $this->freeze($value, $key)) {
$value = serialize($value);
} catch (\Exception $e) {
$type = \is_object($value) ? \get_class($value) : \gettype($value);
CacheItem::log($this->logger, 'Failed to save key "{key}" ({type})', ['key' => $key, 'type' => $type, 'exception' => $e]);
return false; return false;
} }
}
if (null === $expiry && 0 < $this->defaultLifetime) { if (null === $expiry && 0 < $this->defaultLifetime) {
$expiry = time() + $this->defaultLifetime; $expiry = microtime(true) + $this->defaultLifetime;
} }
$this->values[$key] = $value; $this->values[$key] = $value;
$this->expiries[$key] = null !== $expiry ? $expiry : \PHP_INT_MAX; $this->expiries[$key] = $expiry ?? \PHP_INT_MAX;
return true; return true;
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function saveDeferred(CacheItemInterface $item) public function saveDeferred(CacheItemInterface $item)
{ {
@ -152,9 +161,19 @@ class ArrayAdapter implements AdapterInterface, LoggerAwareInterface, Resettable
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function commit() public function commit()
{ {
return true; return true;
} }
/**
* {@inheritdoc}
*/
public function delete(string $key): bool
{
return $this->deleteItem($key);
}
} }

View file

@ -17,6 +17,9 @@ use Symfony\Component\Cache\CacheItem;
use Symfony\Component\Cache\Exception\InvalidArgumentException; use Symfony\Component\Cache\Exception\InvalidArgumentException;
use Symfony\Component\Cache\PruneableInterface; use Symfony\Component\Cache\PruneableInterface;
use Symfony\Component\Cache\ResettableInterface; use Symfony\Component\Cache\ResettableInterface;
use Symfony\Component\Cache\Traits\ContractsTrait;
use Symfony\Contracts\Cache\CacheInterface;
use Symfony\Contracts\Service\ResetInterface;
/** /**
* Chains several adapters together. * Chains several adapters together.
@ -26,8 +29,10 @@ use Symfony\Component\Cache\ResettableInterface;
* *
* @author Kévin Dunglas <dunglas@gmail.com> * @author Kévin Dunglas <dunglas@gmail.com>
*/ */
class ChainAdapter implements AdapterInterface, PruneableInterface, ResettableInterface class ChainAdapter implements AdapterInterface, CacheInterface, PruneableInterface, ResettableInterface
{ {
use ContractsTrait;
private $adapters = []; private $adapters = [];
private $adapterCount; private $adapterCount;
private $syncItem; private $syncItem;
@ -36,7 +41,7 @@ class ChainAdapter implements AdapterInterface, PruneableInterface, ResettableIn
* @param CacheItemPoolInterface[] $adapters The ordered list of adapters used to fetch cached items * @param CacheItemPoolInterface[] $adapters The ordered list of adapters used to fetch cached items
* @param int $defaultLifetime The default lifetime of items propagated from lower adapters to upper ones * @param int $defaultLifetime The default lifetime of items propagated from lower adapters to upper ones
*/ */
public function __construct(array $adapters, $defaultLifetime = 0) public function __construct(array $adapters, int $defaultLifetime = 0)
{ {
if (!$adapters) { if (!$adapters) {
throw new InvalidArgumentException('At least one adapter must be specified.'); throw new InvalidArgumentException('At least one adapter must be specified.');
@ -46,7 +51,7 @@ class ChainAdapter implements AdapterInterface, PruneableInterface, ResettableIn
if (!$adapter instanceof CacheItemPoolInterface) { if (!$adapter instanceof CacheItemPoolInterface) {
throw new InvalidArgumentException(sprintf('The class "%s" does not implement the "%s" interface.', \get_class($adapter), CacheItemPoolInterface::class)); throw new InvalidArgumentException(sprintf('The class "%s" does not implement the "%s" interface.', \get_class($adapter), CacheItemPoolInterface::class));
} }
if (\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) && $adapter instanceof ApcuAdapter && !filter_var(ini_get('apc.enable_cli'), \FILTER_VALIDATE_BOOLEAN)) { if (\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) && $adapter instanceof ApcuAdapter && !filter_var(\ini_get('apc.enable_cli'), \FILTER_VALIDATE_BOOLEAN)) {
continue; // skip putting APCu in the chain when the backend is disabled continue; // skip putting APCu in the chain when the backend is disabled
} }
@ -59,11 +64,18 @@ class ChainAdapter implements AdapterInterface, PruneableInterface, ResettableIn
$this->adapterCount = \count($this->adapters); $this->adapterCount = \count($this->adapters);
$this->syncItem = \Closure::bind( $this->syncItem = \Closure::bind(
static function ($sourceItem, $item) use ($defaultLifetime) { static function ($sourceItem, $item, $sourceMetadata = null) use ($defaultLifetime) {
$sourceItem->isTaggable = false;
$sourceMetadata = $sourceMetadata ?? $sourceItem->metadata;
unset($sourceMetadata[CacheItem::METADATA_TAGS]);
$item->value = $sourceItem->value; $item->value = $sourceItem->value;
$item->isHit = $sourceItem->isHit; $item->isHit = $sourceItem->isHit;
$item->metadata = $item->newMetadata = $sourceItem->metadata = $sourceMetadata;
if (0 < $defaultLifetime) { if (isset($item->metadata[CacheItem::METADATA_EXPIRY])) {
$item->expiresAt(\DateTime::createFromFormat('U.u', sprintf('%.6F', $item->metadata[CacheItem::METADATA_EXPIRY])));
} elseif (0 < $defaultLifetime) {
$item->expiresAfter($defaultLifetime); $item->expiresAfter($defaultLifetime);
} }
@ -74,6 +86,43 @@ class ChainAdapter implements AdapterInterface, PruneableInterface, ResettableIn
); );
} }
/**
* {@inheritdoc}
*/
public function get(string $key, callable $callback, float $beta = null, array &$metadata = null)
{
$doSave = true;
$callback = static function (CacheItem $item, bool &$save) use ($callback, &$doSave) {
$value = $callback($item, $save);
$doSave = $save;
return $value;
};
$lastItem = null;
$i = 0;
$wrap = function (CacheItem $item = null, bool &$save = true) use ($key, $callback, $beta, &$wrap, &$i, &$doSave, &$lastItem, &$metadata) {
$adapter = $this->adapters[$i];
if (isset($this->adapters[++$i])) {
$callback = $wrap;
$beta = \INF === $beta ? \INF : 0;
}
if ($adapter instanceof CacheInterface) {
$value = $adapter->get($key, $callback, $beta, $metadata);
} else {
$value = $this->doGet($adapter, $key, $callback, $beta, $metadata);
}
if (null !== $item) {
($this->syncItem)($lastItem = $lastItem ?? $item, $item, $metadata);
}
$save = $doSave;
return $value;
};
return $wrap();
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
@ -107,12 +156,12 @@ class ChainAdapter implements AdapterInterface, PruneableInterface, ResettableIn
return $this->generateItems($this->adapters[0]->getItems($keys), 0); return $this->generateItems($this->adapters[0]->getItems($keys), 0);
} }
private function generateItems($items, $adapterIndex) private function generateItems(iterable $items, int $adapterIndex)
{ {
$missing = []; $missing = [];
$misses = []; $misses = [];
$nextAdapterIndex = $adapterIndex + 1; $nextAdapterIndex = $adapterIndex + 1;
$nextAdapter = isset($this->adapters[$nextAdapterIndex]) ? $this->adapters[$nextAdapterIndex] : null; $nextAdapter = $this->adapters[$nextAdapterIndex] ?? null;
foreach ($items as $k => $item) { foreach ($items as $k => $item) {
if (!$nextAdapter || $item->isHit()) { if (!$nextAdapter || $item->isHit()) {
@ -140,6 +189,8 @@ class ChainAdapter implements AdapterInterface, PruneableInterface, ResettableIn
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function hasItem($key) public function hasItem($key)
{ {
@ -154,21 +205,32 @@ class ChainAdapter implements AdapterInterface, PruneableInterface, ResettableIn
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @param string $prefix
*
* @return bool
*/ */
public function clear() public function clear(/* string $prefix = '' */)
{ {
$prefix = 0 < \func_num_args() ? (string) func_get_arg(0) : '';
$cleared = true; $cleared = true;
$i = $this->adapterCount; $i = $this->adapterCount;
while ($i--) { while ($i--) {
if ($this->adapters[$i] instanceof AdapterInterface) {
$cleared = $this->adapters[$i]->clear($prefix) && $cleared;
} else {
$cleared = $this->adapters[$i]->clear() && $cleared; $cleared = $this->adapters[$i]->clear() && $cleared;
} }
}
return $cleared; return $cleared;
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function deleteItem($key) public function deleteItem($key)
{ {
@ -184,6 +246,8 @@ class ChainAdapter implements AdapterInterface, PruneableInterface, ResettableIn
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function deleteItems(array $keys) public function deleteItems(array $keys)
{ {
@ -199,6 +263,8 @@ class ChainAdapter implements AdapterInterface, PruneableInterface, ResettableIn
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function save(CacheItemInterface $item) public function save(CacheItemInterface $item)
{ {
@ -214,6 +280,8 @@ class ChainAdapter implements AdapterInterface, PruneableInterface, ResettableIn
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function saveDeferred(CacheItemInterface $item) public function saveDeferred(CacheItemInterface $item)
{ {
@ -229,6 +297,8 @@ class ChainAdapter implements AdapterInterface, PruneableInterface, ResettableIn
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function commit() public function commit()
{ {
@ -264,7 +334,7 @@ class ChainAdapter implements AdapterInterface, PruneableInterface, ResettableIn
public function reset() public function reset()
{ {
foreach ($this->adapters as $adapter) { foreach ($this->adapters as $adapter) {
if ($adapter instanceof ResettableInterface) { if ($adapter instanceof ResetInterface) {
$adapter->reset(); $adapter->reset();
} }
} }

View file

@ -18,11 +18,7 @@ class DoctrineAdapter extends AbstractAdapter
{ {
use DoctrineTrait; use DoctrineTrait;
/** public function __construct(CacheProvider $provider, string $namespace = '', int $defaultLifetime = 0)
* @param string $namespace
* @param int $defaultLifetime
*/
public function __construct(CacheProvider $provider, $namespace = '', $defaultLifetime = 0)
{ {
parent::__construct('', $defaultLifetime); parent::__construct('', $defaultLifetime);
$this->provider = $provider; $this->provider = $provider;

View file

@ -11,6 +11,8 @@
namespace Symfony\Component\Cache\Adapter; namespace Symfony\Component\Cache\Adapter;
use Symfony\Component\Cache\Marshaller\DefaultMarshaller;
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
use Symfony\Component\Cache\PruneableInterface; use Symfony\Component\Cache\PruneableInterface;
use Symfony\Component\Cache\Traits\FilesystemTrait; use Symfony\Component\Cache\Traits\FilesystemTrait;
@ -18,13 +20,9 @@ class FilesystemAdapter extends AbstractAdapter implements PruneableInterface
{ {
use FilesystemTrait; use FilesystemTrait;
/** public function __construct(string $namespace = '', int $defaultLifetime = 0, string $directory = null, MarshallerInterface $marshaller = null)
* @param string $namespace
* @param int $defaultLifetime
* @param string|null $directory
*/
public function __construct($namespace = '', $defaultLifetime = 0, $directory = null)
{ {
$this->marshaller = $marshaller ?? new DefaultMarshaller();
parent::__construct('', $defaultLifetime); parent::__construct('', $defaultLifetime);
$this->init($namespace, $directory); $this->init($namespace, $directory);
} }

View file

@ -0,0 +1,239 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Cache\Adapter;
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
use Symfony\Component\Cache\Marshaller\TagAwareMarshaller;
use Symfony\Component\Cache\PruneableInterface;
use Symfony\Component\Cache\Traits\FilesystemTrait;
/**
* Stores tag id <> cache id relationship as a symlink, and lookup on invalidation calls.
*
* @author Nicolas Grekas <p@tchwork.com>
* @author André Rømcke <andre.romcke+symfony@gmail.com>
*/
class FilesystemTagAwareAdapter extends AbstractTagAwareAdapter implements PruneableInterface
{
use FilesystemTrait {
doClear as private doClearCache;
doSave as private doSaveCache;
}
/**
* Folder used for tag symlinks.
*/
private const TAG_FOLDER = 'tags';
public function __construct(string $namespace = '', int $defaultLifetime = 0, string $directory = null, MarshallerInterface $marshaller = null)
{
$this->marshaller = new TagAwareMarshaller($marshaller);
parent::__construct('', $defaultLifetime);
$this->init($namespace, $directory);
}
/**
* {@inheritdoc}
*/
protected function doClear($namespace)
{
$ok = $this->doClearCache($namespace);
if ('' !== $namespace) {
return $ok;
}
set_error_handler(static function () {});
$chars = '+-ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
try {
foreach ($this->scanHashDir($this->directory.self::TAG_FOLDER.\DIRECTORY_SEPARATOR) as $dir) {
if (rename($dir, $renamed = substr_replace($dir, bin2hex(random_bytes(4)), -8))) {
$dir = $renamed.\DIRECTORY_SEPARATOR;
} else {
$dir .= \DIRECTORY_SEPARATOR;
$renamed = null;
}
for ($i = 0; $i < 38; ++$i) {
if (!file_exists($dir.$chars[$i])) {
continue;
}
for ($j = 0; $j < 38; ++$j) {
if (!file_exists($d = $dir.$chars[$i].\DIRECTORY_SEPARATOR.$chars[$j])) {
continue;
}
foreach (scandir($d, \SCANDIR_SORT_NONE) ?: [] as $link) {
if ('.' !== $link && '..' !== $link && (null !== $renamed || !realpath($d.\DIRECTORY_SEPARATOR.$link))) {
unlink($d.\DIRECTORY_SEPARATOR.$link);
}
}
null === $renamed ?: rmdir($d);
}
null === $renamed ?: rmdir($dir.$chars[$i]);
}
null === $renamed ?: rmdir($renamed);
}
} finally {
restore_error_handler();
}
return $ok;
}
/**
* {@inheritdoc}
*/
protected function doSave(array $values, int $lifetime, array $addTagData = [], array $removeTagData = []): array
{
$failed = $this->doSaveCache($values, $lifetime);
// Add Tags as symlinks
foreach ($addTagData as $tagId => $ids) {
$tagFolder = $this->getTagFolder($tagId);
foreach ($ids as $id) {
if ($failed && \in_array($id, $failed, true)) {
continue;
}
$file = $this->getFile($id);
if (!@symlink($file, $tagLink = $this->getFile($id, true, $tagFolder)) && !is_link($tagLink)) {
@unlink($file);
$failed[] = $id;
}
}
}
// Unlink removed Tags
foreach ($removeTagData as $tagId => $ids) {
$tagFolder = $this->getTagFolder($tagId);
foreach ($ids as $id) {
if ($failed && \in_array($id, $failed, true)) {
continue;
}
@unlink($this->getFile($id, false, $tagFolder));
}
}
return $failed;
}
/**
* {@inheritdoc}
*/
protected function doDeleteYieldTags(array $ids): iterable
{
foreach ($ids as $id) {
$file = $this->getFile($id);
if (!file_exists($file) || !$h = @fopen($file, 'r')) {
continue;
}
if ((\PHP_VERSION_ID >= 70300 || '\\' !== \DIRECTORY_SEPARATOR) && !@unlink($file)) {
fclose($h);
continue;
}
$meta = explode("\n", fread($h, 4096), 3)[2] ?? '';
// detect the compact format used in marshall() using magic numbers in the form 9D-..-..-..-..-00-..-..-..-5F
if (13 < \strlen($meta) && "\x9D" === $meta[0] && "\0" === $meta[5] && "\x5F" === $meta[9]) {
$meta[9] = "\0";
$tagLen = unpack('Nlen', $meta, 9)['len'];
$meta = substr($meta, 13, $tagLen);
if (0 < $tagLen -= \strlen($meta)) {
$meta .= fread($h, $tagLen);
}
try {
yield $id => '' === $meta ? [] : $this->marshaller->unmarshall($meta);
} catch (\Exception $e) {
yield $id => [];
}
}
fclose($h);
if (\PHP_VERSION_ID < 70300 && '\\' === \DIRECTORY_SEPARATOR) {
@unlink($file);
}
}
}
/**
* {@inheritdoc}
*/
protected function doDeleteTagRelations(array $tagData): bool
{
foreach ($tagData as $tagId => $idList) {
$tagFolder = $this->getTagFolder($tagId);
foreach ($idList as $id) {
@unlink($this->getFile($id, false, $tagFolder));
}
}
return true;
}
/**
* {@inheritdoc}
*/
protected function doInvalidate(array $tagIds): bool
{
foreach ($tagIds as $tagId) {
if (!file_exists($tagFolder = $this->getTagFolder($tagId))) {
continue;
}
set_error_handler(static function () {});
try {
if (rename($tagFolder, $renamed = substr_replace($tagFolder, bin2hex(random_bytes(4)), -9))) {
$tagFolder = $renamed.\DIRECTORY_SEPARATOR;
} else {
$renamed = null;
}
foreach ($this->scanHashDir($tagFolder) as $itemLink) {
unlink(realpath($itemLink) ?: $itemLink);
unlink($itemLink);
}
if (null === $renamed) {
continue;
}
$chars = '+-ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
for ($i = 0; $i < 38; ++$i) {
for ($j = 0; $j < 38; ++$j) {
rmdir($tagFolder.$chars[$i].\DIRECTORY_SEPARATOR.$chars[$j]);
}
rmdir($tagFolder.$chars[$i]);
}
rmdir($renamed);
} finally {
restore_error_handler();
}
}
return true;
}
private function getTagFolder(string $tagId): string
{
return $this->getFile($tagId, false, $this->directory.self::TAG_FOLDER.\DIRECTORY_SEPARATOR).\DIRECTORY_SEPARATOR;
}
}

View file

@ -11,6 +11,7 @@
namespace Symfony\Component\Cache\Adapter; namespace Symfony\Component\Cache\Adapter;
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
use Symfony\Component\Cache\Traits\MemcachedTrait; use Symfony\Component\Cache\Traits\MemcachedTrait;
class MemcachedAdapter extends AbstractAdapter class MemcachedAdapter extends AbstractAdapter
@ -29,8 +30,8 @@ class MemcachedAdapter extends AbstractAdapter
* *
* Using a MemcachedAdapter as a pure items store is fine. * Using a MemcachedAdapter as a pure items store is fine.
*/ */
public function __construct(\Memcached $client, $namespace = '', $defaultLifetime = 0) public function __construct(\Memcached $client, string $namespace = '', int $defaultLifetime = 0, MarshallerInterface $marshaller = null)
{ {
$this->init($client, $namespace, $defaultLifetime); $this->init($client, $namespace, $defaultLifetime, $marshaller);
} }
} }

View file

@ -13,11 +13,12 @@ namespace Symfony\Component\Cache\Adapter;
use Psr\Cache\CacheItemInterface; use Psr\Cache\CacheItemInterface;
use Symfony\Component\Cache\CacheItem; use Symfony\Component\Cache\CacheItem;
use Symfony\Contracts\Cache\CacheInterface;
/** /**
* @author Titouan Galopin <galopintitouan@gmail.com> * @author Titouan Galopin <galopintitouan@gmail.com>
*/ */
class NullAdapter implements AdapterInterface class NullAdapter implements AdapterInterface, CacheInterface
{ {
private $createCacheItem; private $createCacheItem;
@ -36,6 +37,16 @@ class NullAdapter implements AdapterInterface
); );
} }
/**
* {@inheritdoc}
*/
public function get(string $key, callable $callback, float $beta = null, array &$metadata = null)
{
$save = true;
return $callback(($this->createCacheItem)($key), $save);
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
@ -56,6 +67,8 @@ class NullAdapter implements AdapterInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function hasItem($key) public function hasItem($key)
{ {
@ -64,14 +77,20 @@ class NullAdapter implements AdapterInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @param string $prefix
*
* @return bool
*/ */
public function clear() public function clear(/* string $prefix = '' */)
{ {
return true; return true;
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function deleteItem($key) public function deleteItem($key)
{ {
@ -80,6 +99,8 @@ class NullAdapter implements AdapterInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function deleteItems(array $keys) public function deleteItems(array $keys)
{ {
@ -88,26 +109,40 @@ class NullAdapter implements AdapterInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function save(CacheItemInterface $item) public function save(CacheItemInterface $item)
{ {
return false; return true;
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function saveDeferred(CacheItemInterface $item) public function saveDeferred(CacheItemInterface $item)
{ {
return false; return true;
}
/**
* {@inheritdoc}
*
* @return bool
*/
public function commit()
{
return true;
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function commit() public function delete(string $key): bool
{ {
return false; return $this->deleteItem($key);
} }
private function generateItems(array $keys) private function generateItems(array $keys)

View file

@ -13,6 +13,7 @@ namespace Symfony\Component\Cache\Adapter;
use Doctrine\DBAL\Connection; use Doctrine\DBAL\Connection;
use Symfony\Component\Cache\Exception\InvalidArgumentException; use Symfony\Component\Cache\Exception\InvalidArgumentException;
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
use Symfony\Component\Cache\PruneableInterface; use Symfony\Component\Cache\PruneableInterface;
use Symfony\Component\Cache\Traits\PdoTrait; use Symfony\Component\Cache\Traits\PdoTrait;
@ -27,6 +28,9 @@ class PdoAdapter extends AbstractAdapter implements PruneableInterface
* a Doctrine DBAL Connection or a DSN string that will be used to * a Doctrine DBAL Connection or a DSN string that will be used to
* lazy-connect to the database when the cache is actually used. * lazy-connect to the database when the cache is actually used.
* *
* When a Doctrine DBAL Connection is passed, the cache table is created
* automatically when possible. Otherwise, use the createTable() method.
*
* List of available options: * List of available options:
* * db_table: The name of the table [default: cache_items] * * db_table: The name of the table [default: cache_items]
* * db_id_col: The column where to store the cache id [default: item_id] * * db_id_col: The column where to store the cache id [default: item_id]
@ -37,17 +41,14 @@ class PdoAdapter extends AbstractAdapter implements PruneableInterface
* * db_password: The password when lazy-connect [default: ''] * * db_password: The password when lazy-connect [default: '']
* * db_connection_options: An array of driver-specific connection options [default: []] * * db_connection_options: An array of driver-specific connection options [default: []]
* *
* @param \PDO|Connection|string $connOrDsn A \PDO or Connection instance or DSN string or null * @param \PDO|Connection|string $connOrDsn a \PDO or Connection instance or DSN string or null
* @param string $namespace
* @param int $defaultLifetime
* @param array $options An associative array of options
* *
* @throws InvalidArgumentException When first argument is not PDO nor Connection nor string * @throws InvalidArgumentException When first argument is not PDO nor Connection nor string
* @throws InvalidArgumentException When PDO error mode is not PDO::ERRMODE_EXCEPTION * @throws InvalidArgumentException When PDO error mode is not PDO::ERRMODE_EXCEPTION
* @throws InvalidArgumentException When namespace contains invalid characters * @throws InvalidArgumentException When namespace contains invalid characters
*/ */
public function __construct($connOrDsn, $namespace = '', $defaultLifetime = 0, array $options = []) public function __construct($connOrDsn, string $namespace = '', int $defaultLifetime = 0, array $options = [], MarshallerInterface $marshaller = null)
{ {
$this->init($connOrDsn, $namespace, $defaultLifetime, $options); $this->init($connOrDsn, $namespace, $defaultLifetime, $options, $marshaller);
} }
} }

View file

@ -17,7 +17,9 @@ use Symfony\Component\Cache\CacheItem;
use Symfony\Component\Cache\Exception\InvalidArgumentException; use Symfony\Component\Cache\Exception\InvalidArgumentException;
use Symfony\Component\Cache\PruneableInterface; use Symfony\Component\Cache\PruneableInterface;
use Symfony\Component\Cache\ResettableInterface; use Symfony\Component\Cache\ResettableInterface;
use Symfony\Component\Cache\Traits\ContractsTrait;
use Symfony\Component\Cache\Traits\PhpArrayTrait; use Symfony\Component\Cache\Traits\PhpArrayTrait;
use Symfony\Contracts\Cache\CacheInterface;
/** /**
* Caches items at warm up time using a PHP array that is stored in shared memory by OPCache since PHP 7.0. * Caches items at warm up time using a PHP array that is stored in shared memory by OPCache since PHP 7.0.
@ -26,8 +28,9 @@ use Symfony\Component\Cache\Traits\PhpArrayTrait;
* @author Titouan Galopin <galopintitouan@gmail.com> * @author Titouan Galopin <galopintitouan@gmail.com>
* @author Nicolas Grekas <p@tchwork.com> * @author Nicolas Grekas <p@tchwork.com>
*/ */
class PhpArrayAdapter implements AdapterInterface, PruneableInterface, ResettableInterface class PhpArrayAdapter implements AdapterInterface, CacheInterface, PruneableInterface, ResettableInterface
{ {
use ContractsTrait;
use PhpArrayTrait; use PhpArrayTrait;
private $createCacheItem; private $createCacheItem;
@ -36,11 +39,10 @@ class PhpArrayAdapter implements AdapterInterface, PruneableInterface, Resettabl
* @param string $file The PHP file were values are cached * @param string $file The PHP file were values are cached
* @param AdapterInterface $fallbackPool A pool to fallback on when an item is not hit * @param AdapterInterface $fallbackPool A pool to fallback on when an item is not hit
*/ */
public function __construct($file, AdapterInterface $fallbackPool) public function __construct(string $file, AdapterInterface $fallbackPool)
{ {
$this->file = $file; $this->file = $file;
$this->pool = $fallbackPool; $this->pool = $fallbackPool;
$this->zendDetectUnicode = filter_var(ini_get('zend.detect_unicode'), \FILTER_VALIDATE_BOOLEAN);
$this->createCacheItem = \Closure::bind( $this->createCacheItem = \Closure::bind(
static function ($key, $value, $isHit) { static function ($key, $value, $isHit) {
$item = new CacheItem(); $item = new CacheItem();
@ -56,9 +58,7 @@ class PhpArrayAdapter implements AdapterInterface, PruneableInterface, Resettabl
} }
/** /**
* This adapter should only be used on PHP 7.0+ to take advantage of how PHP * This adapter takes advantage of how PHP stores arrays in its latest versions.
* stores arrays in its latest versions. This factory method decorates the given
* fallback pool with this adapter only if the current PHP version is supported.
* *
* @param string $file The PHP file were values are cached * @param string $file The PHP file were values are cached
* @param CacheItemPoolInterface $fallbackPool A pool to fallback on when an item is not hit * @param CacheItemPoolInterface $fallbackPool A pool to fallback on when an item is not hit
@ -67,7 +67,6 @@ class PhpArrayAdapter implements AdapterInterface, PruneableInterface, Resettabl
*/ */
public static function create($file, CacheItemPoolInterface $fallbackPool) public static function create($file, CacheItemPoolInterface $fallbackPool)
{ {
if (\PHP_VERSION_ID >= 70000) {
if (!$fallbackPool instanceof AdapterInterface) { if (!$fallbackPool instanceof AdapterInterface) {
$fallbackPool = new ProxyAdapter($fallbackPool); $fallbackPool = new ProxyAdapter($fallbackPool);
} }
@ -75,7 +74,37 @@ class PhpArrayAdapter implements AdapterInterface, PruneableInterface, Resettabl
return new static($file, $fallbackPool); return new static($file, $fallbackPool);
} }
return $fallbackPool; /**
* {@inheritdoc}
*/
public function get(string $key, callable $callback, float $beta = null, array &$metadata = null)
{
if (null === $this->values) {
$this->initialize();
}
if (!isset($this->keys[$key])) {
get_from_pool:
if ($this->pool instanceof CacheInterface) {
return $this->pool->get($key, $callback, $beta, $metadata);
}
return $this->doGet($this->pool, $key, $callback, $beta, $metadata);
}
$value = $this->values[$this->keys[$key]];
if ('N;' === $value) {
return null;
}
try {
if ($value instanceof \Closure) {
return $value();
}
} catch (\Throwable $e) {
unset($this->keys[$key]);
goto get_from_pool;
}
return $value;
} }
/** /**
@ -89,23 +118,19 @@ class PhpArrayAdapter implements AdapterInterface, PruneableInterface, Resettabl
if (null === $this->values) { if (null === $this->values) {
$this->initialize(); $this->initialize();
} }
if (!isset($this->values[$key])) { if (!isset($this->keys[$key])) {
return $this->pool->getItem($key); return $this->pool->getItem($key);
} }
$value = $this->values[$key]; $value = $this->values[$this->keys[$key]];
$isHit = true; $isHit = true;
if ('N;' === $value) { if ('N;' === $value) {
$value = null; $value = null;
} elseif (\is_string($value) && isset($value[2]) && ':' === $value[1]) { } elseif ($value instanceof \Closure) {
try { try {
$e = null; $value = $value();
$value = unserialize($value); } catch (\Throwable $e) {
} catch (\Error $e) {
} catch (\Exception $e) {
}
if (null !== $e) {
$value = null; $value = null;
$isHit = false; $isHit = false;
} }
@ -135,6 +160,8 @@ class PhpArrayAdapter implements AdapterInterface, PruneableInterface, Resettabl
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function hasItem($key) public function hasItem($key)
{ {
@ -145,11 +172,13 @@ class PhpArrayAdapter implements AdapterInterface, PruneableInterface, Resettabl
$this->initialize(); $this->initialize();
} }
return isset($this->values[$key]) || $this->pool->hasItem($key); return isset($this->keys[$key]) || $this->pool->hasItem($key);
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function deleteItem($key) public function deleteItem($key)
{ {
@ -160,11 +189,13 @@ class PhpArrayAdapter implements AdapterInterface, PruneableInterface, Resettabl
$this->initialize(); $this->initialize();
} }
return !isset($this->values[$key]) && $this->pool->deleteItem($key); return !isset($this->keys[$key]) && $this->pool->deleteItem($key);
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function deleteItems(array $keys) public function deleteItems(array $keys)
{ {
@ -176,7 +207,7 @@ class PhpArrayAdapter implements AdapterInterface, PruneableInterface, Resettabl
throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key))); throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key)));
} }
if (isset($this->values[$key])) { if (isset($this->keys[$key])) {
$deleted = false; $deleted = false;
} else { } else {
$fallbackKeys[] = $key; $fallbackKeys[] = $key;
@ -195,6 +226,8 @@ class PhpArrayAdapter implements AdapterInterface, PruneableInterface, Resettabl
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function save(CacheItemInterface $item) public function save(CacheItemInterface $item)
{ {
@ -202,11 +235,13 @@ class PhpArrayAdapter implements AdapterInterface, PruneableInterface, Resettabl
$this->initialize(); $this->initialize();
} }
return !isset($this->values[$item->getKey()]) && $this->pool->save($item); return !isset($this->keys[$item->getKey()]) && $this->pool->save($item);
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function saveDeferred(CacheItemInterface $item) public function saveDeferred(CacheItemInterface $item)
{ {
@ -214,37 +249,34 @@ class PhpArrayAdapter implements AdapterInterface, PruneableInterface, Resettabl
$this->initialize(); $this->initialize();
} }
return !isset($this->values[$item->getKey()]) && $this->pool->saveDeferred($item); return !isset($this->keys[$item->getKey()]) && $this->pool->saveDeferred($item);
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function commit() public function commit()
{ {
return $this->pool->commit(); return $this->pool->commit();
} }
/** private function generateItems(array $keys): \Generator
* @return \Generator
*/
private function generateItems(array $keys)
{ {
$f = $this->createCacheItem; $f = $this->createCacheItem;
$fallbackKeys = []; $fallbackKeys = [];
foreach ($keys as $key) { foreach ($keys as $key) {
if (isset($this->values[$key])) { if (isset($this->keys[$key])) {
$value = $this->values[$key]; $value = $this->values[$this->keys[$key]];
if ('N;' === $value) { if ('N;' === $value) {
yield $key => $f($key, null, true); yield $key => $f($key, null, true);
} elseif (\is_string($value) && isset($value[2]) && ':' === $value[1]) { } elseif ($value instanceof \Closure) {
try { try {
yield $key => $f($key, unserialize($value), true); yield $key => $f($key, $value(), true);
} catch (\Error $e) { } catch (\Throwable $e) {
yield $key => $f($key, null, false);
} catch (\Exception $e) {
yield $key => $f($key, null, false); yield $key => $f($key, null, false);
} }
} else { } else {
@ -256,9 +288,7 @@ class PhpArrayAdapter implements AdapterInterface, PruneableInterface, Resettabl
} }
if ($fallbackKeys) { if ($fallbackKeys) {
foreach ($this->pool->getItems($fallbackKeys) as $key => $item) { yield from $this->pool->getItems($fallbackKeys);
yield $key => $item;
}
} }
} }

View file

@ -20,22 +20,19 @@ class PhpFilesAdapter extends AbstractAdapter implements PruneableInterface
use PhpFilesTrait; use PhpFilesTrait;
/** /**
* @param string $namespace * @param $appendOnly Set to `true` to gain extra performance when the items stored in this pool never expire.
* @param int $defaultLifetime * Doing so is encouraged because it fits perfectly OPcache's memory model.
* @param string|null $directory
* *
* @throws CacheException if OPcache is not enabled * @throws CacheException if OPcache is not enabled
*/ */
public function __construct($namespace = '', $defaultLifetime = 0, $directory = null) public function __construct(string $namespace = '', int $defaultLifetime = 0, string $directory = null, bool $appendOnly = false)
{ {
if (!static::isSupported()) { $this->appendOnly = $appendOnly;
throw new CacheException('OPcache is not enabled.'); self::$startTime = self::$startTime ?? $_SERVER['REQUEST_TIME'] ?? time();
}
parent::__construct('', $defaultLifetime); parent::__construct('', $defaultLifetime);
$this->init($namespace, $directory); $this->init($namespace, $directory);
$this->includeHandler = static function ($type, $msg, $file, $line) {
$e = new \Exception(); throw new \ErrorException($msg, 0, $type, $file, $line);
$this->includeHandler = function () use ($e) { throw $e; }; };
$this->zendDetectUnicode = filter_var(ini_get('zend.detect_unicode'), \FILTER_VALIDATE_BOOLEAN);
} }
} }

View file

@ -16,26 +16,26 @@ use Psr\Cache\CacheItemPoolInterface;
use Symfony\Component\Cache\CacheItem; use Symfony\Component\Cache\CacheItem;
use Symfony\Component\Cache\PruneableInterface; use Symfony\Component\Cache\PruneableInterface;
use Symfony\Component\Cache\ResettableInterface; use Symfony\Component\Cache\ResettableInterface;
use Symfony\Component\Cache\Traits\ContractsTrait;
use Symfony\Component\Cache\Traits\ProxyTrait; use Symfony\Component\Cache\Traits\ProxyTrait;
use Symfony\Contracts\Cache\CacheInterface;
/** /**
* @author Nicolas Grekas <p@tchwork.com> * @author Nicolas Grekas <p@tchwork.com>
*/ */
class ProxyAdapter implements AdapterInterface, PruneableInterface, ResettableInterface class ProxyAdapter implements AdapterInterface, CacheInterface, PruneableInterface, ResettableInterface
{ {
use ContractsTrait;
use ProxyTrait; use ProxyTrait;
private $namespace; private $namespace;
private $namespaceLen; private $namespaceLen;
private $createCacheItem; private $createCacheItem;
private $setInnerItem;
private $poolHash; private $poolHash;
private $defaultLifetime; private $defaultLifetime;
/** public function __construct(CacheItemPoolInterface $pool, string $namespace = '', int $defaultLifetime = 0)
* @param string $namespace
* @param int $defaultLifetime
*/
public function __construct(CacheItemPoolInterface $pool, $namespace = '', $defaultLifetime = 0)
{ {
$this->pool = $pool; $this->pool = $pool;
$this->poolHash = $poolHash = spl_object_hash($pool); $this->poolHash = $poolHash = spl_object_hash($pool);
@ -46,20 +46,71 @@ class ProxyAdapter implements AdapterInterface, PruneableInterface, ResettableIn
static function ($key, $innerItem) use ($poolHash) { static function ($key, $innerItem) use ($poolHash) {
$item = new CacheItem(); $item = new CacheItem();
$item->key = $key; $item->key = $key;
$item->poolHash = $poolHash;
if (null !== $innerItem) { if (null === $innerItem) {
$item->value = $innerItem->get(); return $item;
}
$item->value = $v = $innerItem->get();
$item->isHit = $innerItem->isHit(); $item->isHit = $innerItem->isHit();
$item->innerItem = $innerItem; $item->innerItem = $innerItem;
$innerItem->set(null); $item->poolHash = $poolHash;
// Detect wrapped values that encode for their expiry and creation duration
// For compactness, these values are packed in the key of an array using
// magic numbers in the form 9D-..-..-..-..-00-..-..-..-5F
if (\is_array($v) && 1 === \count($v) && 10 === \strlen($k = (string) array_key_first($v)) && "\x9D" === $k[0] && "\0" === $k[5] && "\x5F" === $k[9]) {
$item->value = $v[$k];
$v = unpack('Ve/Nc', substr($k, 1, -1));
$item->metadata[CacheItem::METADATA_EXPIRY] = $v['e'] + CacheItem::METADATA_EXPIRY_OFFSET;
$item->metadata[CacheItem::METADATA_CTIME] = $v['c'];
} elseif ($innerItem instanceof CacheItem) {
$item->metadata = $innerItem->metadata;
} }
$innerItem->set(null);
return $item; return $item;
}, },
null, null,
CacheItem::class CacheItem::class
); );
$this->setInnerItem = \Closure::bind(
/**
* @param array $item A CacheItem cast to (array); accessing protected properties requires adding the "\0*\0" PHP prefix
*/
static function (CacheItemInterface $innerItem, array $item) {
// Tags are stored separately, no need to account for them when considering this item's newly set metadata
if (isset(($metadata = $item["\0*\0newMetadata"])[CacheItem::METADATA_TAGS])) {
unset($metadata[CacheItem::METADATA_TAGS]);
}
if ($metadata) {
// For compactness, expiry and creation duration are packed in the key of an array, using magic numbers as separators
$item["\0*\0value"] = ["\x9D".pack('VN', (int) (0.1 + $metadata[self::METADATA_EXPIRY] - self::METADATA_EXPIRY_OFFSET), $metadata[self::METADATA_CTIME])."\x5F" => $item["\0*\0value"]];
}
$innerItem->set($item["\0*\0value"]);
$innerItem->expiresAt(null !== $item["\0*\0expiry"] ? \DateTime::createFromFormat('U.u', sprintf('%.6F', $item["\0*\0expiry"])) : null);
},
null,
CacheItem::class
);
}
/**
* {@inheritdoc}
*/
public function get(string $key, callable $callback, float $beta = null, array &$metadata = null)
{
if (!$this->pool instanceof CacheInterface) {
return $this->doGet($this, $key, $callback, $beta, $metadata);
}
return $this->pool->get($this->getId($key), function ($innerItem, bool &$save) use ($key, $callback) {
$item = ($this->createCacheItem)($key, $innerItem);
$item->set($value = $callback($item, $save));
($this->setInnerItem)($innerItem, (array) $item);
return $value;
}, $beta, $metadata);
} }
/** /**
@ -89,6 +140,8 @@ class ProxyAdapter implements AdapterInterface, PruneableInterface, ResettableIn
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function hasItem($key) public function hasItem($key)
{ {
@ -97,14 +150,26 @@ class ProxyAdapter implements AdapterInterface, PruneableInterface, ResettableIn
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @param string $prefix
*
* @return bool
*/ */
public function clear() public function clear(/* string $prefix = '' */)
{ {
$prefix = 0 < \func_num_args() ? (string) func_get_arg(0) : '';
if ($this->pool instanceof AdapterInterface) {
return $this->pool->clear($this->namespace.$prefix);
}
return $this->pool->clear(); return $this->pool->clear();
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function deleteItem($key) public function deleteItem($key)
{ {
@ -113,6 +178,8 @@ class ProxyAdapter implements AdapterInterface, PruneableInterface, ResettableIn
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function deleteItems(array $keys) public function deleteItems(array $keys)
{ {
@ -127,6 +194,8 @@ class ProxyAdapter implements AdapterInterface, PruneableInterface, ResettableIn
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function save(CacheItemInterface $item) public function save(CacheItemInterface $item)
{ {
@ -135,6 +204,8 @@ class ProxyAdapter implements AdapterInterface, PruneableInterface, ResettableIn
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function saveDeferred(CacheItemInterface $item) public function saveDeferred(CacheItemInterface $item)
{ {
@ -143,21 +214,22 @@ class ProxyAdapter implements AdapterInterface, PruneableInterface, ResettableIn
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function commit() public function commit()
{ {
return $this->pool->commit(); return $this->pool->commit();
} }
private function doSave(CacheItemInterface $item, $method) private function doSave(CacheItemInterface $item, string $method)
{ {
if (!$item instanceof CacheItem) { if (!$item instanceof CacheItem) {
return false; return false;
} }
$item = (array) $item; $item = (array) $item;
$expiry = $item["\0*\0expiry"]; if (null === $item["\0*\0expiry"] && 0 < $this->defaultLifetime) {
if (null === $expiry && 0 < $this->defaultLifetime) { $item["\0*\0expiry"] = microtime(true) + $this->defaultLifetime;
$expiry = time() + $this->defaultLifetime;
} }
if ($item["\0*\0poolHash"] === $this->poolHash && $item["\0*\0innerItem"]) { if ($item["\0*\0poolHash"] === $this->poolHash && $item["\0*\0innerItem"]) {
@ -171,13 +243,12 @@ class ProxyAdapter implements AdapterInterface, PruneableInterface, ResettableIn
$innerItem = $this->pool->getItem($this->namespace.$item["\0*\0key"]); $innerItem = $this->pool->getItem($this->namespace.$item["\0*\0key"]);
} }
$innerItem->set($item["\0*\0value"]); ($this->setInnerItem)($innerItem, $item);
$innerItem->expiresAt(null !== $expiry ? \DateTime::createFromFormat('U', $expiry) : null);
return $this->pool->$method($innerItem); return $this->pool->$method($innerItem);
} }
private function generateItems($items) private function generateItems(iterable $items)
{ {
$f = $this->createCacheItem; $f = $this->createCacheItem;
@ -190,7 +261,7 @@ class ProxyAdapter implements AdapterInterface, PruneableInterface, ResettableIn
} }
} }
private function getId($key) private function getId($key): string
{ {
CacheItem::validateKey($key); CacheItem::validateKey($key);

View file

@ -0,0 +1,86 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Cache\Adapter;
use Psr\SimpleCache\CacheInterface;
use Symfony\Component\Cache\PruneableInterface;
use Symfony\Component\Cache\ResettableInterface;
use Symfony\Component\Cache\Traits\ProxyTrait;
/**
* Turns a PSR-16 cache into a PSR-6 one.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
class Psr16Adapter extends AbstractAdapter implements PruneableInterface, ResettableInterface
{
use ProxyTrait;
/**
* @internal
*/
protected const NS_SEPARATOR = '_';
private $miss;
public function __construct(CacheInterface $pool, string $namespace = '', int $defaultLifetime = 0)
{
parent::__construct($namespace, $defaultLifetime);
$this->pool = $pool;
$this->miss = new \stdClass();
}
/**
* {@inheritdoc}
*/
protected function doFetch(array $ids)
{
foreach ($this->pool->getMultiple($ids, $this->miss) as $key => $value) {
if ($this->miss !== $value) {
yield $key => $value;
}
}
}
/**
* {@inheritdoc}
*/
protected function doHave($id)
{
return $this->pool->has($id);
}
/**
* {@inheritdoc}
*/
protected function doClear($namespace)
{
return $this->pool->clear();
}
/**
* {@inheritdoc}
*/
protected function doDelete(array $ids)
{
return $this->pool->deleteMultiple($ids);
}
/**
* {@inheritdoc}
*/
protected function doSave(array $values, int $lifetime)
{
return $this->pool->setMultiple($values, 0 === $lifetime ? null : $lifetime);
}
}

View file

@ -11,6 +11,9 @@
namespace Symfony\Component\Cache\Adapter; namespace Symfony\Component\Cache\Adapter;
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
use Symfony\Component\Cache\Traits\RedisClusterProxy;
use Symfony\Component\Cache\Traits\RedisProxy;
use Symfony\Component\Cache\Traits\RedisTrait; use Symfony\Component\Cache\Traits\RedisTrait;
class RedisAdapter extends AbstractAdapter class RedisAdapter extends AbstractAdapter
@ -18,12 +21,12 @@ class RedisAdapter extends AbstractAdapter
use RedisTrait; use RedisTrait;
/** /**
* @param \Redis|\RedisArray|\RedisCluster|\Predis\Client $redisClient The redis client * @param \Redis|\RedisArray|\RedisCluster|\Predis\ClientInterface|RedisProxy|RedisClusterProxy $redis The redis client
* @param string $namespace The default namespace * @param string $namespace The default namespace
* @param int $defaultLifetime The default lifetime * @param int $defaultLifetime The default lifetime
*/ */
public function __construct($redisClient, $namespace = '', $defaultLifetime = 0) public function __construct($redis, string $namespace = '', int $defaultLifetime = 0, MarshallerInterface $marshaller = null)
{ {
$this->init($redisClient, $namespace, $defaultLifetime); $this->init($redis, $namespace, $defaultLifetime, $marshaller);
} }
} }

View file

@ -0,0 +1,321 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Cache\Adapter;
use Predis\Connection\Aggregate\ClusterInterface;
use Predis\Connection\Aggregate\PredisCluster;
use Predis\Connection\Aggregate\ReplicationInterface;
use Predis\Response\ErrorInterface;
use Predis\Response\Status;
use Symfony\Component\Cache\CacheItem;
use Symfony\Component\Cache\Exception\InvalidArgumentException;
use Symfony\Component\Cache\Exception\LogicException;
use Symfony\Component\Cache\Marshaller\DeflateMarshaller;
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
use Symfony\Component\Cache\Marshaller\TagAwareMarshaller;
use Symfony\Component\Cache\Traits\RedisClusterProxy;
use Symfony\Component\Cache\Traits\RedisProxy;
use Symfony\Component\Cache\Traits\RedisTrait;
/**
* Stores tag id <> cache id relationship as a Redis Set.
*
* Set (tag relation info) is stored without expiry (non-volatile), while cache always gets an expiry (volatile) even
* if not set by caller. Thus if you configure redis with the right eviction policy you can be safe this tag <> cache
* relationship survives eviction (cache cleanup when Redis runs out of memory).
*
* Redis server 2.8+ with any `volatile-*` eviction policy, OR `noeviction` if you're sure memory will NEVER fill up
*
* Design limitations:
* - Max 4 billion cache keys per cache tag as limited by Redis Set datatype.
* E.g. If you use a "all" items tag for expiry instead of clear(), that limits you to 4 billion cache items also.
*
* @see https://redis.io/topics/lru-cache#eviction-policies Documentation for Redis eviction policies.
* @see https://redis.io/topics/data-types#sets Documentation for Redis Set datatype.
*
* @author Nicolas Grekas <p@tchwork.com>
* @author André Rømcke <andre.romcke+symfony@gmail.com>
*/
class RedisTagAwareAdapter extends AbstractTagAwareAdapter
{
use RedisTrait;
/**
* On cache items without a lifetime set, we set it to 100 days. This is to make sure cache items are
* preferred to be evicted over tag Sets, if eviction policy is configured according to requirements.
*/
private const DEFAULT_CACHE_TTL = 8640000;
/**
* @var string|null detected eviction policy used on Redis server
*/
private $redisEvictionPolicy;
private $namespace;
/**
* @param \Redis|\RedisArray|\RedisCluster|\Predis\ClientInterface|RedisProxy|RedisClusterProxy $redis The redis client
* @param string $namespace The default namespace
* @param int $defaultLifetime The default lifetime
*/
public function __construct($redis, string $namespace = '', int $defaultLifetime = 0, MarshallerInterface $marshaller = null)
{
if ($redis instanceof \Predis\ClientInterface && $redis->getConnection() instanceof ClusterInterface && !$redis->getConnection() instanceof PredisCluster) {
throw new InvalidArgumentException(sprintf('Unsupported Predis cluster connection: only "%s" is, "%s" given.', PredisCluster::class, \get_class($redis->getConnection())));
}
if (\defined('Redis::OPT_COMPRESSION') && ($redis instanceof \Redis || $redis instanceof \RedisArray || $redis instanceof \RedisCluster)) {
$compression = $redis->getOption(\Redis::OPT_COMPRESSION);
foreach (\is_array($compression) ? $compression : [$compression] as $c) {
if (\Redis::COMPRESSION_NONE !== $c) {
throw new InvalidArgumentException(sprintf('phpredis compression must be disabled when using "%s", use "%s" instead.', static::class, DeflateMarshaller::class));
}
}
}
$this->init($redis, $namespace, $defaultLifetime, new TagAwareMarshaller($marshaller));
$this->namespace = $namespace;
}
/**
* {@inheritdoc}
*/
protected function doSave(array $values, int $lifetime, array $addTagData = [], array $delTagData = []): array
{
$eviction = $this->getRedisEvictionPolicy();
if ('noeviction' !== $eviction && !str_starts_with($eviction, 'volatile-')) {
throw new LogicException(sprintf('Redis maxmemory-policy setting "%s" is *not* supported by RedisTagAwareAdapter, use "noeviction" or "volatile-*" eviction policies.', $eviction));
}
// serialize values
if (!$serialized = $this->marshaller->marshall($values, $failed)) {
return $failed;
}
// While pipeline isn't supported on RedisCluster, other setups will at least benefit from doing this in one op
$results = $this->pipeline(static function () use ($serialized, $lifetime, $addTagData, $delTagData, $failed) {
// Store cache items, force a ttl if none is set, as there is no MSETEX we need to set each one
foreach ($serialized as $id => $value) {
yield 'setEx' => [
$id,
0 >= $lifetime ? self::DEFAULT_CACHE_TTL : $lifetime,
$value,
];
}
// Add and Remove Tags
foreach ($addTagData as $tagId => $ids) {
if (!$failed || $ids = array_diff($ids, $failed)) {
yield 'sAdd' => array_merge([$tagId], $ids);
}
}
foreach ($delTagData as $tagId => $ids) {
if (!$failed || $ids = array_diff($ids, $failed)) {
yield 'sRem' => array_merge([$tagId], $ids);
}
}
});
foreach ($results as $id => $result) {
// Skip results of SADD/SREM operations, they'll be 1 or 0 depending on if set value already existed or not
if (is_numeric($result)) {
continue;
}
// setEx results
if (true !== $result && (!$result instanceof Status || Status::get('OK') !== $result)) {
$failed[] = $id;
}
}
return $failed;
}
/**
* {@inheritdoc}
*/
protected function doDeleteYieldTags(array $ids): iterable
{
$lua = <<<'EOLUA'
local v = redis.call('GET', KEYS[1])
redis.call('DEL', KEYS[1])
if not v or v:len() <= 13 or v:byte(1) ~= 0x9D or v:byte(6) ~= 0 or v:byte(10) ~= 0x5F then
return ''
end
return v:sub(14, 13 + v:byte(13) + v:byte(12) * 256 + v:byte(11) * 65536)
EOLUA;
$results = $this->pipeline(function () use ($ids, $lua) {
foreach ($ids as $id) {
yield 'eval' => $this->redis instanceof \Predis\ClientInterface ? [$lua, 1, $id] : [$lua, [$id], 1];
}
});
foreach ($results as $id => $result) {
if ($result instanceof \RedisException || $result instanceof ErrorInterface) {
CacheItem::log($this->logger, 'Failed to delete key "{key}": '.$result->getMessage(), ['key' => substr($id, \strlen($this->namespace)), 'exception' => $result]);
continue;
}
try {
yield $id => !\is_string($result) || '' === $result ? [] : $this->marshaller->unmarshall($result);
} catch (\Exception $e) {
yield $id => [];
}
}
}
/**
* {@inheritdoc}
*/
protected function doDeleteTagRelations(array $tagData): bool
{
$results = $this->pipeline(static function () use ($tagData) {
foreach ($tagData as $tagId => $idList) {
array_unshift($idList, $tagId);
yield 'sRem' => $idList;
}
});
foreach ($results as $result) {
// no-op
}
return true;
}
/**
* {@inheritdoc}
*/
protected function doInvalidate(array $tagIds): bool
{
// This script scans the set of items linked to tag: it empties the set
// and removes the linked items. When the set is still not empty after
// the scan, it means we're in cluster mode and that the linked items
// are on other nodes: we move the links to a temporary set and we
// garbage collect that set from the client side.
$lua = <<<'EOLUA'
redis.replicate_commands()
local cursor = '0'
local id = KEYS[1]
repeat
local result = redis.call('SSCAN', id, cursor, 'COUNT', 5000);
cursor = result[1];
local rems = {}
for _, v in ipairs(result[2]) do
local ok, _ = pcall(redis.call, 'DEL', ARGV[1]..v)
if ok then
table.insert(rems, v)
end
end
if 0 < #rems then
redis.call('SREM', id, unpack(rems))
end
until '0' == cursor;
redis.call('SUNIONSTORE', '{'..id..'}'..id, id)
redis.call('DEL', id)
return redis.call('SSCAN', '{'..id..'}'..id, '0', 'COUNT', 5000)
EOLUA;
$results = $this->pipeline(function () use ($tagIds, $lua) {
if ($this->redis instanceof \Predis\ClientInterface) {
$prefix = $this->redis->getOptions()->prefix ? $this->redis->getOptions()->prefix->getPrefix() : '';
} elseif (\is_array($prefix = $this->redis->getOption(\Redis::OPT_PREFIX) ?? '')) {
$prefix = current($prefix);
}
foreach ($tagIds as $id) {
yield 'eval' => $this->redis instanceof \Predis\ClientInterface ? [$lua, 1, $id, $prefix] : [$lua, [$id, $prefix], 1];
}
});
$lua = <<<'EOLUA'
redis.replicate_commands()
local id = KEYS[1]
local cursor = table.remove(ARGV)
redis.call('SREM', '{'..id..'}'..id, unpack(ARGV))
return redis.call('SSCAN', '{'..id..'}'..id, cursor, 'COUNT', 5000)
EOLUA;
$success = true;
foreach ($results as $id => $values) {
if ($values instanceof \RedisException || $values instanceof ErrorInterface) {
CacheItem::log($this->logger, 'Failed to invalidate key "{key}": '.$values->getMessage(), ['key' => substr($id, \strlen($this->namespace)), 'exception' => $values]);
$success = false;
continue;
}
[$cursor, $ids] = $values;
while ($ids || '0' !== $cursor) {
$this->doDelete($ids);
$evalArgs = [$id, $cursor];
array_splice($evalArgs, 1, 0, $ids);
if ($this->redis instanceof \Predis\ClientInterface) {
array_unshift($evalArgs, $lua, 1);
} else {
$evalArgs = [$lua, $evalArgs, 1];
}
$results = $this->pipeline(function () use ($evalArgs) {
yield 'eval' => $evalArgs;
});
foreach ($results as [$cursor, $ids]) {
// no-op
}
}
}
return $success;
}
private function getRedisEvictionPolicy(): string
{
if (null !== $this->redisEvictionPolicy) {
return $this->redisEvictionPolicy;
}
$hosts = $this->getHosts();
$host = reset($hosts);
if ($host instanceof \Predis\Client && $host->getConnection() instanceof ReplicationInterface) {
// Predis supports info command only on the master in replication environments
$hosts = [$host->getClientFor('master')];
}
foreach ($hosts as $host) {
$info = $host->info('Memory');
if ($info instanceof ErrorInterface) {
continue;
}
$info = $info['Memory'] ?? $info;
return $this->redisEvictionPolicy = $info['maxmemory_policy'];
}
return $this->redisEvictionPolicy = '';
}
}

View file

@ -11,73 +11,11 @@
namespace Symfony\Component\Cache\Adapter; namespace Symfony\Component\Cache\Adapter;
use Psr\SimpleCache\CacheInterface; @trigger_error(sprintf('The "%s" class is @deprecated since Symfony 4.3, use "Psr16Adapter" instead.', SimpleCacheAdapter::class), \E_USER_DEPRECATED);
use Symfony\Component\Cache\PruneableInterface;
use Symfony\Component\Cache\Traits\ProxyTrait;
/** /**
* @author Nicolas Grekas <p@tchwork.com> * @deprecated since Symfony 4.3, use Psr16Adapter instead.
*/ */
class SimpleCacheAdapter extends AbstractAdapter implements PruneableInterface class SimpleCacheAdapter extends Psr16Adapter
{ {
/**
* @internal
*/
const NS_SEPARATOR = '_';
use ProxyTrait;
private $miss;
public function __construct(CacheInterface $pool, $namespace = '', $defaultLifetime = 0)
{
parent::__construct($namespace, $defaultLifetime);
$this->pool = $pool;
$this->miss = new \stdClass();
}
/**
* {@inheritdoc}
*/
protected function doFetch(array $ids)
{
foreach ($this->pool->getMultiple($ids, $this->miss) as $key => $value) {
if ($this->miss !== $value) {
yield $key => $value;
}
}
}
/**
* {@inheritdoc}
*/
protected function doHave($id)
{
return $this->pool->has($id);
}
/**
* {@inheritdoc}
*/
protected function doClear($namespace)
{
return $this->pool->clear();
}
/**
* {@inheritdoc}
*/
protected function doDelete(array $ids)
{
return $this->pool->deleteMultiple($ids);
}
/**
* {@inheritdoc}
*/
protected function doSave(array $values, $lifetime)
{
return $this->pool->setMultiple($values, 0 === $lifetime ? null : $lifetime);
}
} }

View file

@ -13,20 +13,26 @@ namespace Symfony\Component\Cache\Adapter;
use Psr\Cache\CacheItemInterface; use Psr\Cache\CacheItemInterface;
use Psr\Cache\InvalidArgumentException; use Psr\Cache\InvalidArgumentException;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Symfony\Component\Cache\CacheItem; use Symfony\Component\Cache\CacheItem;
use Symfony\Component\Cache\PruneableInterface; use Symfony\Component\Cache\PruneableInterface;
use Symfony\Component\Cache\ResettableInterface; use Symfony\Component\Cache\ResettableInterface;
use Symfony\Component\Cache\Traits\ContractsTrait;
use Symfony\Component\Cache\Traits\ProxyTrait; use Symfony\Component\Cache\Traits\ProxyTrait;
use Symfony\Contracts\Cache\TagAwareCacheInterface;
/** /**
* @author Nicolas Grekas <p@tchwork.com> * @author Nicolas Grekas <p@tchwork.com>
*/ */
class TagAwareAdapter implements TagAwareAdapterInterface, PruneableInterface, ResettableInterface class TagAwareAdapter implements TagAwareAdapterInterface, TagAwareCacheInterface, PruneableInterface, ResettableInterface, LoggerAwareInterface
{ {
const TAGS_PREFIX = "\0tags\0"; use ContractsTrait;
use LoggerAwareTrait;
use ProxyTrait; use ProxyTrait;
public const TAGS_PREFIX = "\0tags\0";
private $deferred = []; private $deferred = [];
private $createCacheItem; private $createCacheItem;
private $setCacheItemTags; private $setCacheItemTags;
@ -36,7 +42,7 @@ class TagAwareAdapter implements TagAwareAdapterInterface, PruneableInterface, R
private $knownTagVersions = []; private $knownTagVersions = [];
private $knownTagVersionsTtl; private $knownTagVersionsTtl;
public function __construct(AdapterInterface $itemsPool, AdapterInterface $tagsPool = null, $knownTagVersionsTtl = 0.15) public function __construct(AdapterInterface $itemsPool, AdapterInterface $tagsPool = null, float $knownTagVersionsTtl = 0.15)
{ {
$this->pool = $itemsPool; $this->pool = $itemsPool;
$this->tags = $tagsPool ?: $itemsPool; $this->tags = $tagsPool ?: $itemsPool;
@ -56,12 +62,13 @@ class TagAwareAdapter implements TagAwareAdapterInterface, PruneableInterface, R
); );
$this->setCacheItemTags = \Closure::bind( $this->setCacheItemTags = \Closure::bind(
static function (CacheItem $item, $key, array &$itemTags) { static function (CacheItem $item, $key, array &$itemTags) {
$item->isTaggable = true;
if (!$item->isHit) { if (!$item->isHit) {
return $item; return $item;
} }
if (isset($itemTags[$key])) { if (isset($itemTags[$key])) {
foreach ($itemTags[$key] as $tag => $version) { foreach ($itemTags[$key] as $tag => $version) {
$item->prevTags[$tag] = $tag; $item->metadata[CacheItem::METADATA_TAGS][$tag] = $tag;
} }
unset($itemTags[$key]); unset($itemTags[$key]);
} else { } else {
@ -78,7 +85,8 @@ class TagAwareAdapter implements TagAwareAdapterInterface, PruneableInterface, R
static function ($deferred) { static function ($deferred) {
$tagsByKey = []; $tagsByKey = [];
foreach ($deferred as $key => $item) { foreach ($deferred as $key => $item) {
$tagsByKey[$key] = $item->tags; $tagsByKey[$key] = $item->newMetadata[CacheItem::METADATA_TAGS] ?? [];
$item->metadata = $item->newMetadata;
} }
return $tagsByKey; return $tagsByKey;
@ -145,12 +153,15 @@ class TagAwareAdapter implements TagAwareAdapterInterface, PruneableInterface, R
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function hasItem($key) public function hasItem($key)
{ {
if ($this->deferred) { if (\is_string($key) && isset($this->deferred[$key])) {
$this->commit(); $this->commit();
} }
if (!$this->pool->hasItem($key)) { if (!$this->pool->hasItem($key)) {
return false; return false;
} }
@ -166,9 +177,11 @@ class TagAwareAdapter implements TagAwareAdapterInterface, PruneableInterface, R
} }
foreach ($this->getTagVersions([$itemTags]) as $tag => $version) { foreach ($this->getTagVersions([$itemTags]) as $tag => $version) {
if ($itemTags[$tag] !== $version && 1 !== $itemTags[$tag] - $version) { if ($itemTags[$tag] === $version || \is_int($itemTags[$tag]) && \is_int($version) && 1 === $itemTags[$tag] - $version) {
return false; continue;
} }
return false;
} }
return true; return true;
@ -191,18 +204,21 @@ class TagAwareAdapter implements TagAwareAdapterInterface, PruneableInterface, R
*/ */
public function getItems(array $keys = []) public function getItems(array $keys = [])
{ {
if ($this->deferred) {
$this->commit();
}
$tagKeys = []; $tagKeys = [];
$commit = false;
foreach ($keys as $key) { foreach ($keys as $key) {
if ('' !== $key && \is_string($key)) { if ('' !== $key && \is_string($key)) {
$commit = $commit || isset($this->deferred[$key]);
$key = static::TAGS_PREFIX.$key; $key = static::TAGS_PREFIX.$key;
$tagKeys[$key] = $key; $tagKeys[$key] = $key;
} }
} }
if ($commit) {
$this->commit();
}
try { try {
$items = $this->pool->getItems($tagKeys + $keys); $items = $this->pool->getItems($tagKeys + $keys);
} catch (InvalidArgumentException $e) { } catch (InvalidArgumentException $e) {
@ -216,16 +232,36 @@ class TagAwareAdapter implements TagAwareAdapterInterface, PruneableInterface, R
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @param string $prefix
*
* @return bool
*/ */
public function clear() public function clear(/* string $prefix = '' */)
{ {
$prefix = 0 < \func_num_args() ? (string) func_get_arg(0) : '';
if ('' !== $prefix) {
foreach ($this->deferred as $key => $item) {
if (str_starts_with($key, $prefix)) {
unset($this->deferred[$key]);
}
}
} else {
$this->deferred = []; $this->deferred = [];
}
if ($this->pool instanceof AdapterInterface) {
return $this->pool->clear($prefix);
}
return $this->pool->clear(); return $this->pool->clear();
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function deleteItem($key) public function deleteItem($key)
{ {
@ -234,6 +270,8 @@ class TagAwareAdapter implements TagAwareAdapterInterface, PruneableInterface, R
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function deleteItems(array $keys) public function deleteItems(array $keys)
{ {
@ -248,6 +286,8 @@ class TagAwareAdapter implements TagAwareAdapterInterface, PruneableInterface, R
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function save(CacheItemInterface $item) public function save(CacheItemInterface $item)
{ {
@ -261,6 +301,8 @@ class TagAwareAdapter implements TagAwareAdapterInterface, PruneableInterface, R
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function saveDeferred(CacheItemInterface $item) public function saveDeferred(CacheItemInterface $item)
{ {
@ -274,12 +316,17 @@ class TagAwareAdapter implements TagAwareAdapterInterface, PruneableInterface, R
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function commit() public function commit()
{ {
return $this->invalidateTags([]); return $this->invalidateTags([]);
} }
/**
* @return array
*/
public function __sleep() public function __sleep()
{ {
throw new \BadMethodCallException('Cannot serialize '.__CLASS__); throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
@ -295,7 +342,7 @@ class TagAwareAdapter implements TagAwareAdapterInterface, PruneableInterface, R
$this->commit(); $this->commit();
} }
private function generateItems($items, array $tagKeys) private function generateItems(iterable $items, array $tagKeys)
{ {
$bufferedItems = $itemTags = []; $bufferedItems = $itemTags = [];
$f = $this->setCacheItemTags; $f = $this->setCacheItemTags;
@ -321,12 +368,13 @@ class TagAwareAdapter implements TagAwareAdapterInterface, PruneableInterface, R
foreach ($itemTags as $key => $tags) { foreach ($itemTags as $key => $tags) {
foreach ($tags as $tag => $version) { foreach ($tags as $tag => $version) {
if ($tagVersions[$tag] !== $version && 1 !== $version - $tagVersions[$tag]) { if ($tagVersions[$tag] === $version || \is_int($version) && \is_int($tagVersions[$tag]) && 1 === $version - $tagVersions[$tag]) {
continue;
}
unset($itemTags[$key]); unset($itemTags[$key]);
continue 2; continue 2;
} }
} }
}
$tagVersions = $tagKeys = null; $tagVersions = $tagKeys = null;
foreach ($bufferedItems as $key => $item) { foreach ($bufferedItems as $key => $item) {
@ -363,7 +411,7 @@ class TagAwareAdapter implements TagAwareAdapterInterface, PruneableInterface, R
$tags = []; $tags = [];
foreach ($tagVersions as $tag => $version) { foreach ($tagVersions as $tag => $version) {
$tags[$tag.static::TAGS_PREFIX] = $tag; $tags[$tag.static::TAGS_PREFIX] = $tag;
if ($fetchTagVersions || !isset($this->knownTagVersions[$tag])) { if ($fetchTagVersions || !isset($this->knownTagVersions[$tag]) || !\is_int($version)) {
$fetchTagVersions = true; $fetchTagVersions = true;
continue; continue;
} }
@ -385,6 +433,10 @@ class TagAwareAdapter implements TagAwareAdapterInterface, PruneableInterface, R
if (isset($invalidatedTags[$tag])) { if (isset($invalidatedTags[$tag])) {
$invalidatedTags[$tag] = $version->set(++$tagVersions[$tag]); $invalidatedTags[$tag] = $version->set(++$tagVersions[$tag]);
} }
if (!\is_int($tagVersions[$tag])) {
unset($this->knownTagVersions[$tag]);
continue;
}
$this->knownTagVersions[$tag] = [$now, $tagVersions[$tag]]; $this->knownTagVersions[$tag] = [$now, $tagVersions[$tag]];
} }

View file

@ -12,8 +12,11 @@
namespace Symfony\Component\Cache\Adapter; namespace Symfony\Component\Cache\Adapter;
use Psr\Cache\CacheItemInterface; use Psr\Cache\CacheItemInterface;
use Symfony\Component\Cache\CacheItem;
use Symfony\Component\Cache\PruneableInterface; use Symfony\Component\Cache\PruneableInterface;
use Symfony\Component\Cache\ResettableInterface; use Symfony\Component\Cache\ResettableInterface;
use Symfony\Contracts\Cache\CacheInterface;
use Symfony\Contracts\Service\ResetInterface;
/** /**
* An adapter that collects data about all cache calls. * An adapter that collects data about all cache calls.
@ -22,7 +25,7 @@ use Symfony\Component\Cache\ResettableInterface;
* @author Tobias Nyholm <tobias.nyholm@gmail.com> * @author Tobias Nyholm <tobias.nyholm@gmail.com>
* @author Nicolas Grekas <p@tchwork.com> * @author Nicolas Grekas <p@tchwork.com>
*/ */
class TraceableAdapter implements AdapterInterface, PruneableInterface, ResettableInterface class TraceableAdapter implements AdapterInterface, CacheInterface, PruneableInterface, ResettableInterface
{ {
protected $pool; protected $pool;
private $calls = []; private $calls = [];
@ -32,6 +35,38 @@ class TraceableAdapter implements AdapterInterface, PruneableInterface, Resettab
$this->pool = $pool; $this->pool = $pool;
} }
/**
* {@inheritdoc}
*/
public function get(string $key, callable $callback, float $beta = null, array &$metadata = null)
{
if (!$this->pool instanceof CacheInterface) {
throw new \BadMethodCallException(sprintf('Cannot call "%s::get()": this class doesn\'t implement "%s".', \get_class($this->pool), CacheInterface::class));
}
$isHit = true;
$callback = function (CacheItem $item, bool &$save) use ($callback, &$isHit) {
$isHit = $item->isHit();
return $callback($item, $save);
};
$event = $this->start(__FUNCTION__);
try {
$value = $this->pool->get($key, $callback, $beta, $metadata);
$event->result[$key] = \is_object($value) ? \get_class($value) : \gettype($value);
} finally {
$event->end = microtime(true);
}
if ($isHit) {
++$event->hits;
} else {
++$event->misses;
}
return $value;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
@ -54,6 +89,8 @@ class TraceableAdapter implements AdapterInterface, PruneableInterface, Resettab
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function hasItem($key) public function hasItem($key)
{ {
@ -67,6 +104,8 @@ class TraceableAdapter implements AdapterInterface, PruneableInterface, Resettab
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function deleteItem($key) public function deleteItem($key)
{ {
@ -80,6 +119,8 @@ class TraceableAdapter implements AdapterInterface, PruneableInterface, Resettab
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function save(CacheItemInterface $item) public function save(CacheItemInterface $item)
{ {
@ -93,6 +134,8 @@ class TraceableAdapter implements AdapterInterface, PruneableInterface, Resettab
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function saveDeferred(CacheItemInterface $item) public function saveDeferred(CacheItemInterface $item)
{ {
@ -132,11 +175,20 @@ class TraceableAdapter implements AdapterInterface, PruneableInterface, Resettab
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @param string $prefix
*
* @return bool
*/ */
public function clear() public function clear(/* string $prefix = '' */)
{ {
$prefix = 0 < \func_num_args() ? (string) func_get_arg(0) : '';
$event = $this->start(__FUNCTION__); $event = $this->start(__FUNCTION__);
try { try {
if ($this->pool instanceof AdapterInterface) {
return $event->result = $this->pool->clear($prefix);
}
return $event->result = $this->pool->clear(); return $event->result = $this->pool->clear();
} finally { } finally {
$event->end = microtime(true); $event->end = microtime(true);
@ -145,6 +197,8 @@ class TraceableAdapter implements AdapterInterface, PruneableInterface, Resettab
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function deleteItems(array $keys) public function deleteItems(array $keys)
{ {
@ -159,6 +213,8 @@ class TraceableAdapter implements AdapterInterface, PruneableInterface, Resettab
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function commit() public function commit()
{ {
@ -191,13 +247,26 @@ class TraceableAdapter implements AdapterInterface, PruneableInterface, Resettab
*/ */
public function reset() public function reset()
{ {
if ($this->pool instanceof ResettableInterface) { if ($this->pool instanceof ResetInterface) {
$this->pool->reset(); $this->pool->reset();
} }
$this->clearCalls(); $this->clearCalls();
} }
/**
* {@inheritdoc}
*/
public function delete(string $key): bool
{
$event = $this->start(__FUNCTION__);
try {
return $event->result[$key] = $this->pool->deleteItem($key);
} finally {
$event->end = microtime(true);
}
}
public function getCalls() public function getCalls()
{ {
return $this->calls; return $this->calls;

View file

@ -11,10 +11,12 @@
namespace Symfony\Component\Cache\Adapter; namespace Symfony\Component\Cache\Adapter;
use Symfony\Contracts\Cache\TagAwareCacheInterface;
/** /**
* @author Robin Chalas <robin.chalas@gmail.com> * @author Robin Chalas <robin.chalas@gmail.com>
*/ */
class TraceableTagAwareAdapter extends TraceableAdapter implements TagAwareAdapterInterface class TraceableTagAwareAdapter extends TraceableAdapter implements TagAwareAdapterInterface, TagAwareCacheInterface
{ {
public function __construct(TagAwareAdapterInterface $pool) public function __construct(TagAwareAdapterInterface $pool)
{ {

View file

@ -1,6 +1,43 @@
CHANGELOG CHANGELOG
========= =========
4.4.0
-----
* added support for connecting to Redis Sentinel clusters
* added argument `$prefix` to `AdapterInterface::clear()`
* improved `RedisTagAwareAdapter` to support Redis server >= 2.8 and up to 4B items per tag
* added `TagAwareMarshaller` for optimized data storage when using `AbstractTagAwareAdapter`
* added `DeflateMarshaller` to compress serialized values
* removed support for phpredis 4 `compression`
* [BC BREAK] `RedisTagAwareAdapter` is not compatible with `RedisCluster` from `Predis` anymore, use `phpredis` instead
* Marked the `CacheDataCollector` class as `@final`.
4.3.0
-----
* removed `psr/simple-cache` dependency, run `composer require psr/simple-cache` if you need it
* deprecated all PSR-16 adapters, use `Psr16Cache` or `Symfony\Contracts\Cache\CacheInterface` implementations instead
* deprecated `SimpleCacheAdapter`, use `Psr16Adapter` instead
4.2.0
-----
* added support for connecting to Redis clusters via DSN
* added support for configuring multiple Memcached servers via DSN
* added `MarshallerInterface` and `DefaultMarshaller` to allow changing the serializer and provide one that automatically uses igbinary when available
* implemented `CacheInterface`, which provides stampede protection via probabilistic early expiration and should become the preferred way to use a cache
* added sub-second expiry accuracy for backends that support it
* added support for phpredis 4 `compression` and `tcp_keepalive` options
* added automatic table creation when using Doctrine DBAL with PDO-based backends
* throw `LogicException` when `CacheItem::tag()` is called on an item coming from a non tag-aware pool
* deprecated `CacheItem::getPreviousTags()`, use `CacheItem::getMetadata()` instead
* deprecated the `AbstractAdapter::unserialize()` and `AbstractCache::unserialize()` methods
* added `CacheCollectorPass` (originally in `FrameworkBundle`)
* added `CachePoolClearerPass` (originally in `FrameworkBundle`)
* added `CachePoolPass` (originally in `FrameworkBundle`)
* added `CachePoolPrunerPass` (originally in `FrameworkBundle`)
3.4.0 3.4.0
----- -----
@ -13,7 +50,7 @@ CHANGELOG
3.3.0 3.3.0
----- -----
* [EXPERIMENTAL] added CacheItem::getPreviousTags() to get bound tags coming from the pool storage if any * added CacheItem::getPreviousTags() to get bound tags coming from the pool storage if any
* added PSR-16 "Simple Cache" implementations for all existing PSR-6 adapters * added PSR-16 "Simple Cache" implementations for all existing PSR-6 adapters
* added Psr6Cache and SimpleCacheAdapter for bidirectional interoperability between PSR-6 and PSR-16 * added Psr6Cache and SimpleCacheAdapter for bidirectional interoperability between PSR-6 and PSR-16
* added MemcachedAdapter (PSR-6) and MemcachedCache (PSR-16) * added MemcachedAdapter (PSR-6) and MemcachedCache (PSR-16)

View file

@ -11,34 +11,40 @@
namespace Symfony\Component\Cache; namespace Symfony\Component\Cache;
use Psr\Cache\CacheItemInterface;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Symfony\Component\Cache\Exception\InvalidArgumentException; use Symfony\Component\Cache\Exception\InvalidArgumentException;
use Symfony\Component\Cache\Exception\LogicException;
use Symfony\Contracts\Cache\ItemInterface;
/** /**
* @author Nicolas Grekas <p@tchwork.com> * @author Nicolas Grekas <p@tchwork.com>
*/ */
final class CacheItem implements CacheItemInterface final class CacheItem implements ItemInterface
{ {
private const METADATA_EXPIRY_OFFSET = 1527506807;
protected $key; protected $key;
protected $value; protected $value;
protected $isHit = false; protected $isHit = false;
protected $expiry; protected $expiry;
protected $tags = []; protected $metadata = [];
protected $prevTags = []; protected $newMetadata = [];
protected $innerItem; protected $innerItem;
protected $poolHash; protected $poolHash;
protected $isTaggable = false;
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getKey() public function getKey(): string
{ {
return $this->key; return $this->key;
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return mixed
*/ */
public function get() public function get()
{ {
@ -48,7 +54,7 @@ final class CacheItem implements CacheItemInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function isHit() public function isHit(): bool
{ {
return $this->isHit; return $this->isHit;
} }
@ -58,7 +64,7 @@ final class CacheItem implements CacheItemInterface
* *
* @return $this * @return $this
*/ */
public function set($value) public function set($value): self
{ {
$this->value = $value; $this->value = $value;
@ -70,12 +76,12 @@ final class CacheItem implements CacheItemInterface
* *
* @return $this * @return $this
*/ */
public function expiresAt($expiration) public function expiresAt($expiration): self
{ {
if (null === $expiration) { if (null === $expiration) {
$this->expiry = null; $this->expiry = null;
} elseif ($expiration instanceof \DateTimeInterface) { } elseif ($expiration instanceof \DateTimeInterface) {
$this->expiry = (int) $expiration->format('U'); $this->expiry = (float) $expiration->format('U.u');
} else { } else {
throw new InvalidArgumentException(sprintf('Expiration date must implement DateTimeInterface or be null, "%s" given.', \is_object($expiration) ? \get_class($expiration) : \gettype($expiration))); throw new InvalidArgumentException(sprintf('Expiration date must implement DateTimeInterface or be null, "%s" given.', \is_object($expiration) ? \get_class($expiration) : \gettype($expiration)));
} }
@ -88,14 +94,14 @@ final class CacheItem implements CacheItemInterface
* *
* @return $this * @return $this
*/ */
public function expiresAfter($time) public function expiresAfter($time): self
{ {
if (null === $time) { if (null === $time) {
$this->expiry = null; $this->expiry = null;
} elseif ($time instanceof \DateInterval) { } elseif ($time instanceof \DateInterval) {
$this->expiry = (int) \DateTime::createFromFormat('U', time())->add($time)->format('U'); $this->expiry = microtime(true) + \DateTime::createFromFormat('U', 0)->add($time)->format('U.u');
} elseif (\is_int($time)) { } elseif (\is_int($time)) {
$this->expiry = $time + time(); $this->expiry = $time + microtime(true);
} else { } else {
throw new InvalidArgumentException(sprintf('Expiration date must be an integer, a DateInterval or null, "%s" given.', \is_object($time) ? \get_class($time) : \gettype($time))); throw new InvalidArgumentException(sprintf('Expiration date must be an integer, a DateInterval or null, "%s" given.', \is_object($time) ? \get_class($time) : \gettype($time)));
} }
@ -104,58 +110,64 @@ final class CacheItem implements CacheItemInterface
} }
/** /**
* Adds a tag to a cache item. * {@inheritdoc}
*
* @param string|string[] $tags A tag or array of tags
*
* @return $this
*
* @throws InvalidArgumentException When $tag is not valid
*/ */
public function tag($tags) public function tag($tags): ItemInterface
{ {
if (!\is_array($tags)) { if (!$this->isTaggable) {
throw new LogicException(sprintf('Cache item "%s" comes from a non tag-aware pool: you cannot tag it.', $this->key));
}
if (!is_iterable($tags)) {
$tags = [$tags]; $tags = [$tags];
} }
foreach ($tags as $tag) { foreach ($tags as $tag) {
if (!\is_string($tag)) { if (!\is_string($tag) && !(\is_object($tag) && method_exists($tag, '__toString'))) {
throw new InvalidArgumentException(sprintf('Cache tag must be string, "%s" given.', \is_object($tag) ? \get_class($tag) : \gettype($tag))); throw new InvalidArgumentException(sprintf('Cache tag must be string or object that implements __toString(), "%s" given.', \is_object($tag) ? \get_class($tag) : \gettype($tag)));
} }
if (isset($this->tags[$tag])) { $tag = (string) $tag;
if (isset($this->newMetadata[self::METADATA_TAGS][$tag])) {
continue; continue;
} }
if ('' === $tag) { if ('' === $tag) {
throw new InvalidArgumentException('Cache tag length must be greater than zero.'); throw new InvalidArgumentException('Cache tag length must be greater than zero.');
} }
if (false !== strpbrk($tag, '{}()/\@:')) { if (false !== strpbrk($tag, self::RESERVED_CHARACTERS)) {
throw new InvalidArgumentException(sprintf('Cache tag "%s" contains reserved characters {}()/\@:.', $tag)); throw new InvalidArgumentException(sprintf('Cache tag "%s" contains reserved characters "%s".', $tag, self::RESERVED_CHARACTERS));
} }
$this->tags[$tag] = $tag; $this->newMetadata[self::METADATA_TAGS][$tag] = $tag;
} }
return $this; return $this;
} }
/**
* {@inheritdoc}
*/
public function getMetadata(): array
{
return $this->metadata;
}
/** /**
* Returns the list of tags bound to the value coming from the pool storage if any. * Returns the list of tags bound to the value coming from the pool storage if any.
* *
* @return array * @deprecated since Symfony 4.2, use the "getMetadata()" method instead.
*/ */
public function getPreviousTags() public function getPreviousTags(): array
{ {
return $this->prevTags; @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.2, use the "getMetadata()" method instead.', __METHOD__), \E_USER_DEPRECATED);
return $this->metadata[self::METADATA_TAGS] ?? [];
} }
/** /**
* Validates a cache key according to PSR-6. * Validates a cache key according to PSR-6.
* *
* @param string $key The key to validate * @param mixed $key The key to validate
*
* @return string
* *
* @throws InvalidArgumentException When $key is not valid * @throws InvalidArgumentException When $key is not valid
*/ */
public static function validateKey($key) public static function validateKey($key): string
{ {
if (!\is_string($key)) { if (!\is_string($key)) {
throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key))); throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key)));
@ -163,8 +175,8 @@ final class CacheItem implements CacheItemInterface
if ('' === $key) { if ('' === $key) {
throw new InvalidArgumentException('Cache key length must be greater than zero.'); throw new InvalidArgumentException('Cache key length must be greater than zero.');
} }
if (false !== strpbrk($key, '{}()/\@:')) { if (false !== strpbrk($key, self::RESERVED_CHARACTERS)) {
throw new InvalidArgumentException(sprintf('Cache key "%s" contains reserved characters {}()/\@:.', $key)); throw new InvalidArgumentException(sprintf('Cache key "%s" contains reserved characters "%s".', $key, self::RESERVED_CHARACTERS));
} }
return $key; return $key;
@ -175,14 +187,14 @@ final class CacheItem implements CacheItemInterface
* *
* @internal * @internal
*/ */
public static function log(LoggerInterface $logger = null, $message, $context = []) public static function log(?LoggerInterface $logger, string $message, array $context = [])
{ {
if ($logger) { if ($logger) {
$logger->warning($message, $context); $logger->warning($message, $context);
} else { } else {
$replace = []; $replace = [];
foreach ($context as $k => $v) { foreach ($context as $k => $v) {
if (is_scalar($v)) { if (\is_scalar($v)) {
$replace['{'.$k.'}'] = $v; $replace['{'.$k.'}'] = $v;
} }
} }

View file

@ -21,6 +21,8 @@ use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface;
/** /**
* @author Aaron Scherer <aequasi@gmail.com> * @author Aaron Scherer <aequasi@gmail.com>
* @author Tobias Nyholm <tobias.nyholm@gmail.com> * @author Tobias Nyholm <tobias.nyholm@gmail.com>
*
* @final since Symfony 4.4
*/ */
class CacheDataCollector extends DataCollector implements LateDataCollectorInterface class CacheDataCollector extends DataCollector implements LateDataCollectorInterface
{ {
@ -39,8 +41,10 @@ class CacheDataCollector extends DataCollector implements LateDataCollectorInter
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @param \Throwable|null $exception
*/ */
public function collect(Request $request, Response $response, \Exception $exception = null) public function collect(Request $request, Response $response/* , \Throwable $exception = null */)
{ {
$empty = ['calls' => [], 'config' => [], 'options' => [], 'statistics' => []]; $empty = ['calls' => [], 'config' => [], 'options' => [], 'statistics' => []];
$this->data = ['instances' => $empty, 'total' => $empty]; $this->data = ['instances' => $empty, 'total' => $empty];
@ -62,7 +66,7 @@ class CacheDataCollector extends DataCollector implements LateDataCollectorInter
public function lateCollect() public function lateCollect()
{ {
$this->data = $this->cloneVar($this->data); $this->data['instances']['calls'] = $this->cloneVar($this->data['instances']['calls']);
} }
/** /**
@ -103,10 +107,7 @@ class CacheDataCollector extends DataCollector implements LateDataCollectorInter
return $this->data['instances']['calls']; return $this->data['instances']['calls'];
} }
/** private function calculateStatistics(): array
* @return array
*/
private function calculateStatistics()
{ {
$statistics = []; $statistics = [];
foreach ($this->data['instances']['calls'] as $name => $calls) { foreach ($this->data['instances']['calls'] as $name => $calls) {
@ -123,7 +124,15 @@ class CacheDataCollector extends DataCollector implements LateDataCollectorInter
foreach ($calls as $call) { foreach ($calls as $call) {
++$statistics[$name]['calls']; ++$statistics[$name]['calls'];
$statistics[$name]['time'] += $call->end - $call->start; $statistics[$name]['time'] += $call->end - $call->start;
if ('getItem' === $call->name) { if ('get' === $call->name) {
++$statistics[$name]['reads'];
if ($call->hits) {
++$statistics[$name]['hits'];
} else {
++$statistics[$name]['misses'];
++$statistics[$name]['writes'];
}
} elseif ('getItem' === $call->name) {
++$statistics[$name]['reads']; ++$statistics[$name]['reads'];
if ($call->hits) { if ($call->hits) {
++$statistics[$name]['hits']; ++$statistics[$name]['hits'];
@ -157,10 +166,7 @@ class CacheDataCollector extends DataCollector implements LateDataCollectorInter
return $statistics; return $statistics;
} }
/** private function calculateTotalStatistics(): array
* @return array
*/
private function calculateTotalStatistics()
{ {
$statistics = $this->getStatistics(); $statistics = $this->getStatistics();
$totals = [ $totals = [

View file

@ -0,0 +1,81 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Cache\DependencyInjection;
use Symfony\Component\Cache\Adapter\TagAwareAdapterInterface;
use Symfony\Component\Cache\Adapter\TraceableAdapter;
use Symfony\Component\Cache\Adapter\TraceableTagAwareAdapter;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
/**
* Inject a data collector to all the cache services to be able to get detailed statistics.
*
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
class CacheCollectorPass implements CompilerPassInterface
{
private $dataCollectorCacheId;
private $cachePoolTag;
private $cachePoolRecorderInnerSuffix;
public function __construct(string $dataCollectorCacheId = 'data_collector.cache', string $cachePoolTag = 'cache.pool', string $cachePoolRecorderInnerSuffix = '.recorder_inner')
{
$this->dataCollectorCacheId = $dataCollectorCacheId;
$this->cachePoolTag = $cachePoolTag;
$this->cachePoolRecorderInnerSuffix = $cachePoolRecorderInnerSuffix;
}
/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition($this->dataCollectorCacheId)) {
return;
}
foreach ($container->findTaggedServiceIds($this->cachePoolTag) as $id => $attributes) {
$poolName = $attributes[0]['name'] ?? $id;
$this->addToCollector($id, $poolName, $container);
}
}
private function addToCollector(string $id, string $name, ContainerBuilder $container)
{
$definition = $container->getDefinition($id);
if ($definition->isAbstract()) {
return;
}
$collectorDefinition = $container->getDefinition($this->dataCollectorCacheId);
$recorder = new Definition(is_subclass_of($definition->getClass(), TagAwareAdapterInterface::class) ? TraceableTagAwareAdapter::class : TraceableAdapter::class);
$recorder->setTags($definition->getTags());
if (!$definition->isPublic() || !$definition->isPrivate()) {
$recorder->setPublic($definition->isPublic());
}
$recorder->setArguments([new Reference($innerId = $id.$this->cachePoolRecorderInnerSuffix)]);
$definition->setTags([]);
$definition->setPublic(false);
$container->setDefinition($innerId, $definition);
$container->setDefinition($id, $recorder);
// Tell the collector to add the new instance
$collectorDefinition->addMethodCall('addInstance', [$name, new Reference($id)]);
$collectorDefinition->setPublic(false);
}
}

View file

@ -0,0 +1,48 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Cache\DependencyInjection;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
/**
* @author Nicolas Grekas <p@tchwork.com>
*/
class CachePoolClearerPass implements CompilerPassInterface
{
private $cachePoolClearerTag;
public function __construct(string $cachePoolClearerTag = 'cache.pool.clearer')
{
$this->cachePoolClearerTag = $cachePoolClearerTag;
}
/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container)
{
$container->getParameterBag()->remove('cache.prefix.seed');
foreach ($container->findTaggedServiceIds($this->cachePoolClearerTag) as $id => $attr) {
$clearer = $container->getDefinition($id);
$pools = [];
foreach ($clearer->getArgument(0) as $name => $ref) {
if ($container->hasDefinition($ref)) {
$pools[$name] = new Reference($ref);
}
}
$clearer->replaceArgument(0, $pools);
}
}
}

View file

@ -0,0 +1,228 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Cache\DependencyInjection;
use Symfony\Component\Cache\Adapter\AbstractAdapter;
use Symfony\Component\Cache\Adapter\ArrayAdapter;
use Symfony\Component\Cache\Adapter\ChainAdapter;
use Symfony\Component\Cache\Adapter\NullAdapter;
use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Reference;
/**
* @author Nicolas Grekas <p@tchwork.com>
*/
class CachePoolPass implements CompilerPassInterface
{
private $cachePoolTag;
private $kernelResetTag;
private $cacheClearerId;
private $cachePoolClearerTag;
private $cacheSystemClearerId;
private $cacheSystemClearerTag;
public function __construct(string $cachePoolTag = 'cache.pool', string $kernelResetTag = 'kernel.reset', string $cacheClearerId = 'cache.global_clearer', string $cachePoolClearerTag = 'cache.pool.clearer', string $cacheSystemClearerId = 'cache.system_clearer', string $cacheSystemClearerTag = 'kernel.cache_clearer')
{
$this->cachePoolTag = $cachePoolTag;
$this->kernelResetTag = $kernelResetTag;
$this->cacheClearerId = $cacheClearerId;
$this->cachePoolClearerTag = $cachePoolClearerTag;
$this->cacheSystemClearerId = $cacheSystemClearerId;
$this->cacheSystemClearerTag = $cacheSystemClearerTag;
}
/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container)
{
if ($container->hasParameter('cache.prefix.seed')) {
$seed = '.'.$container->getParameterBag()->resolveValue($container->getParameter('cache.prefix.seed'));
} else {
$seed = '_'.$container->getParameter('kernel.project_dir');
}
$seed .= '.'.$container->getParameter('kernel.container_class');
$allPools = [];
$clearers = [];
$attributes = [
'provider',
'name',
'namespace',
'default_lifetime',
'reset',
];
foreach ($container->findTaggedServiceIds($this->cachePoolTag) as $id => $tags) {
$adapter = $pool = $container->getDefinition($id);
if ($pool->isAbstract()) {
continue;
}
$class = $adapter->getClass();
while ($adapter instanceof ChildDefinition) {
$adapter = $container->findDefinition($adapter->getParent());
$class = $class ?: $adapter->getClass();
if ($t = $adapter->getTag($this->cachePoolTag)) {
$tags[0] += $t[0];
}
}
$name = $tags[0]['name'] ?? $id;
if (!isset($tags[0]['namespace'])) {
$namespaceSeed = $seed;
if (null !== $class) {
$namespaceSeed .= '.'.$class;
}
$tags[0]['namespace'] = $this->getNamespace($namespaceSeed, $name);
}
if (isset($tags[0]['clearer'])) {
$clearer = $tags[0]['clearer'];
while ($container->hasAlias($clearer)) {
$clearer = (string) $container->getAlias($clearer);
}
} else {
$clearer = null;
}
unset($tags[0]['clearer'], $tags[0]['name']);
if (isset($tags[0]['provider'])) {
$tags[0]['provider'] = new Reference(static::getServiceProvider($container, $tags[0]['provider']));
}
if (ChainAdapter::class === $class) {
$adapters = [];
foreach ($adapter->getArgument(0) as $provider => $adapter) {
if ($adapter instanceof ChildDefinition) {
$chainedPool = $adapter;
} else {
$chainedPool = $adapter = new ChildDefinition($adapter);
}
$chainedTags = [\is_int($provider) ? [] : ['provider' => $provider]];
$chainedClass = '';
while ($adapter instanceof ChildDefinition) {
$adapter = $container->findDefinition($adapter->getParent());
$chainedClass = $chainedClass ?: $adapter->getClass();
if ($t = $adapter->getTag($this->cachePoolTag)) {
$chainedTags[0] += $t[0];
}
}
if (ChainAdapter::class === $chainedClass) {
throw new InvalidArgumentException(sprintf('Invalid service "%s": chain of adapters cannot reference another chain, found "%s".', $id, $chainedPool->getParent()));
}
$i = 0;
if (isset($chainedTags[0]['provider'])) {
$chainedPool->replaceArgument($i++, new Reference(static::getServiceProvider($container, $chainedTags[0]['provider'])));
}
if (isset($tags[0]['namespace']) && !\in_array($adapter->getClass(), [ArrayAdapter::class, NullAdapter::class], true)) {
$chainedPool->replaceArgument($i++, $tags[0]['namespace']);
}
if (isset($tags[0]['default_lifetime'])) {
$chainedPool->replaceArgument($i++, $tags[0]['default_lifetime']);
}
$adapters[] = $chainedPool;
}
$pool->replaceArgument(0, $adapters);
unset($tags[0]['provider'], $tags[0]['namespace']);
$i = 1;
} else {
$i = 0;
}
foreach ($attributes as $attr) {
if (!isset($tags[0][$attr])) {
// no-op
} elseif ('reset' === $attr) {
if ($tags[0][$attr]) {
$pool->addTag($this->kernelResetTag, ['method' => $tags[0][$attr]]);
}
} elseif ('namespace' !== $attr || !\in_array($class, [ArrayAdapter::class, NullAdapter::class], true)) {
$pool->replaceArgument($i++, $tags[0][$attr]);
}
unset($tags[0][$attr]);
}
if (!empty($tags[0])) {
throw new InvalidArgumentException(sprintf('Invalid "%s" tag for service "%s": accepted attributes are "clearer", "provider", "name", "namespace", "default_lifetime" and "reset", found "%s".', $this->cachePoolTag, $id, implode('", "', array_keys($tags[0]))));
}
if (null !== $clearer) {
$clearers[$clearer][$name] = new Reference($id, $container::IGNORE_ON_UNINITIALIZED_REFERENCE);
}
$allPools[$name] = new Reference($id, $container::IGNORE_ON_UNINITIALIZED_REFERENCE);
}
$notAliasedCacheClearerId = $this->cacheClearerId;
while ($container->hasAlias($this->cacheClearerId)) {
$this->cacheClearerId = (string) $container->getAlias($this->cacheClearerId);
}
if ($container->hasDefinition($this->cacheClearerId)) {
$clearers[$notAliasedCacheClearerId] = $allPools;
}
foreach ($clearers as $id => $pools) {
$clearer = $container->getDefinition($id);
if ($clearer instanceof ChildDefinition) {
$clearer->replaceArgument(0, $pools);
} else {
$clearer->setArgument(0, $pools);
}
$clearer->addTag($this->cachePoolClearerTag);
if ($this->cacheSystemClearerId === $id) {
$clearer->addTag($this->cacheSystemClearerTag);
}
}
if ($container->hasDefinition('console.command.cache_pool_list')) {
$container->getDefinition('console.command.cache_pool_list')->replaceArgument(0, array_keys($allPools));
}
}
private function getNamespace(string $seed, string $id)
{
return substr(str_replace('/', '-', base64_encode(hash('sha256', $id.$seed, true))), 0, 10);
}
/**
* @internal
*/
public static function getServiceProvider(ContainerBuilder $container, $name)
{
$container->resolveEnvPlaceholders($name, null, $usedEnvs);
if ($usedEnvs || preg_match('#^[a-z]++:#', $name)) {
$dsn = $name;
if (!$container->hasDefinition($name = '.cache_connection.'.ContainerBuilder::hash($dsn))) {
$definition = new Definition(AbstractAdapter::class);
$definition->setPublic(false);
$definition->setFactory([AbstractAdapter::class, 'createConnection']);
$definition->setArguments([$dsn, ['lazy' => true]]);
$container->setDefinition($name, $definition);
}
}
return $name;
}
}

View file

@ -0,0 +1,60 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Cache\DependencyInjection;
use Symfony\Component\Cache\PruneableInterface;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Reference;
/**
* @author Rob Frawley 2nd <rmf@src.run>
*/
class CachePoolPrunerPass implements CompilerPassInterface
{
private $cacheCommandServiceId;
private $cachePoolTag;
public function __construct(string $cacheCommandServiceId = 'console.command.cache_pool_prune', string $cachePoolTag = 'cache.pool')
{
$this->cacheCommandServiceId = $cacheCommandServiceId;
$this->cachePoolTag = $cachePoolTag;
}
/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition($this->cacheCommandServiceId)) {
return;
}
$services = [];
foreach ($container->findTaggedServiceIds($this->cachePoolTag) as $id => $tags) {
$class = $container->getParameterBag()->resolveValue($container->getDefinition($id)->getClass());
if (!$reflection = $container->getReflectionClass($class)) {
throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id));
}
if ($reflection->implementsInterface(PruneableInterface::class)) {
$services[$id] = new Reference($id);
}
}
$container->getDefinition($this->cacheCommandServiceId)->replaceArgument(0, new IteratorArgument($services));
}
}

View file

@ -13,6 +13,11 @@ namespace Symfony\Component\Cache;
use Doctrine\Common\Cache\CacheProvider; use Doctrine\Common\Cache\CacheProvider;
use Psr\Cache\CacheItemPoolInterface; use Psr\Cache\CacheItemPoolInterface;
use Symfony\Contracts\Service\ResetInterface;
if (!class_exists(CacheProvider::class)) {
return;
}
/** /**
* @author Nicolas Grekas <p@tchwork.com> * @author Nicolas Grekas <p@tchwork.com>
@ -39,7 +44,7 @@ class DoctrineProvider extends CacheProvider implements PruneableInterface, Rese
*/ */
public function reset() public function reset()
{ {
if ($this->pool instanceof ResettableInterface) { if ($this->pool instanceof ResetInterface) {
$this->pool->reset(); $this->pool->reset();
} }
$this->setNamespace($this->getNamespace()); $this->setNamespace($this->getNamespace());
@ -47,6 +52,8 @@ class DoctrineProvider extends CacheProvider implements PruneableInterface, Rese
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return mixed
*/ */
protected function doFetch($id) protected function doFetch($id)
{ {
@ -57,6 +64,8 @@ class DoctrineProvider extends CacheProvider implements PruneableInterface, Rese
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
protected function doContains($id) protected function doContains($id)
{ {
@ -65,6 +74,8 @@ class DoctrineProvider extends CacheProvider implements PruneableInterface, Rese
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
protected function doSave($id, $data, $lifeTime = 0) protected function doSave($id, $data, $lifeTime = 0)
{ {
@ -79,6 +90,8 @@ class DoctrineProvider extends CacheProvider implements PruneableInterface, Rese
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
protected function doDelete($id) protected function doDelete($id)
{ {
@ -87,6 +100,8 @@ class DoctrineProvider extends CacheProvider implements PruneableInterface, Rese
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
protected function doFlush() protected function doFlush()
{ {
@ -95,6 +110,8 @@ class DoctrineProvider extends CacheProvider implements PruneableInterface, Rese
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return array|null
*/ */
protected function doGetStats() protected function doGetStats()
{ {

View file

@ -14,6 +14,12 @@ namespace Symfony\Component\Cache\Exception;
use Psr\Cache\CacheException as Psr6CacheInterface; use Psr\Cache\CacheException as Psr6CacheInterface;
use Psr\SimpleCache\CacheException as SimpleCacheInterface; use Psr\SimpleCache\CacheException as SimpleCacheInterface;
class CacheException extends \Exception implements Psr6CacheInterface, SimpleCacheInterface if (interface_exists(SimpleCacheInterface::class)) {
{ class CacheException extends \Exception implements Psr6CacheInterface, SimpleCacheInterface
{
}
} else {
class CacheException extends \Exception implements Psr6CacheInterface
{
}
} }

View file

@ -14,6 +14,12 @@ namespace Symfony\Component\Cache\Exception;
use Psr\Cache\InvalidArgumentException as Psr6CacheInterface; use Psr\Cache\InvalidArgumentException as Psr6CacheInterface;
use Psr\SimpleCache\InvalidArgumentException as SimpleCacheInterface; use Psr\SimpleCache\InvalidArgumentException as SimpleCacheInterface;
class InvalidArgumentException extends \InvalidArgumentException implements Psr6CacheInterface, SimpleCacheInterface if (interface_exists(SimpleCacheInterface::class)) {
{ class InvalidArgumentException extends \InvalidArgumentException implements Psr6CacheInterface, SimpleCacheInterface
{
}
} else {
class InvalidArgumentException extends \InvalidArgumentException implements Psr6CacheInterface
{
}
} }

View file

@ -0,0 +1,25 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Cache\Exception;
use Psr\Cache\CacheException as Psr6CacheInterface;
use Psr\SimpleCache\CacheException as SimpleCacheInterface;
if (interface_exists(SimpleCacheInterface::class)) {
class LogicException extends \LogicException implements Psr6CacheInterface, SimpleCacheInterface
{
}
} else {
class LogicException extends \LogicException implements Psr6CacheInterface
{
}
}

View file

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

View file

@ -0,0 +1,161 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Cache;
use Psr\Log\LoggerInterface;
use Symfony\Contracts\Cache\CacheInterface;
use Symfony\Contracts\Cache\ItemInterface;
/**
* LockRegistry is used internally by existing adapters to protect against cache stampede.
*
* It does so by wrapping the computation of items in a pool of locks.
* Foreach each apps, there can be at most 20 concurrent processes that
* compute items at the same time and only one per cache-key.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
final class LockRegistry
{
private static $openedFiles = [];
private static $lockedFiles;
private static $signalingException;
private static $signalingCallback;
/**
* The number of items in this list controls the max number of concurrent processes.
*/
private static $files = [
__DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'AbstractAdapter.php',
__DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'AbstractTagAwareAdapter.php',
__DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'AdapterInterface.php',
__DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'ApcuAdapter.php',
__DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'ArrayAdapter.php',
__DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'ChainAdapter.php',
__DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'DoctrineAdapter.php',
__DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'FilesystemAdapter.php',
__DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'FilesystemTagAwareAdapter.php',
__DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'MemcachedAdapter.php',
__DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'NullAdapter.php',
__DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'PdoAdapter.php',
__DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'PhpArrayAdapter.php',
__DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'PhpFilesAdapter.php',
__DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'ProxyAdapter.php',
__DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'Psr16Adapter.php',
__DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'RedisAdapter.php',
__DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'RedisTagAwareAdapter.php',
__DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'SimpleCacheAdapter.php',
__DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'TagAwareAdapter.php',
__DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'TagAwareAdapterInterface.php',
__DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'TraceableAdapter.php',
__DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'TraceableTagAwareAdapter.php',
];
/**
* Defines a set of existing files that will be used as keys to acquire locks.
*
* @return array The previously defined set of files
*/
public static function setFiles(array $files): array
{
$previousFiles = self::$files;
self::$files = $files;
foreach (self::$openedFiles as $file) {
if ($file) {
flock($file, \LOCK_UN);
fclose($file);
}
}
self::$openedFiles = self::$lockedFiles = [];
return $previousFiles;
}
public static function compute(callable $callback, ItemInterface $item, bool &$save, CacheInterface $pool, \Closure $setMetadata = null, LoggerInterface $logger = null)
{
if ('\\' === \DIRECTORY_SEPARATOR && null === self::$lockedFiles) {
// disable locking on Windows by default
self::$files = self::$lockedFiles = [];
}
$key = self::$files ? abs(crc32($item->getKey())) % \count(self::$files) : -1;
if ($key < 0 || self::$lockedFiles || !$lock = self::open($key)) {
return $callback($item, $save);
}
self::$signalingException ?? self::$signalingException = unserialize("O:9:\"Exception\":1:{s:16:\"\0Exception\0trace\";a:0:{}}");
self::$signalingCallback ?? self::$signalingCallback = function () { throw self::$signalingException; };
while (true) {
try {
// race to get the lock in non-blocking mode
$locked = flock($lock, \LOCK_EX | \LOCK_NB, $wouldBlock);
if ($locked || !$wouldBlock) {
$logger && $logger->info(sprintf('Lock %s, now computing item "{key}"', $locked ? 'acquired' : 'not supported'), ['key' => $item->getKey()]);
self::$lockedFiles[$key] = true;
$value = $callback($item, $save);
if ($save) {
if ($setMetadata) {
$setMetadata($item);
}
$pool->save($item->set($value));
$save = false;
}
return $value;
}
// if we failed the race, retry locking in blocking mode to wait for the winner
$logger && $logger->info('Item "{key}" is locked, waiting for it to be released', ['key' => $item->getKey()]);
flock($lock, \LOCK_SH);
} finally {
flock($lock, \LOCK_UN);
unset(self::$lockedFiles[$key]);
}
try {
$value = $pool->get($item->getKey(), self::$signalingCallback, 0);
$logger && $logger->info('Item "{key}" retrieved after lock was released', ['key' => $item->getKey()]);
$save = false;
return $value;
} catch (\Exception $e) {
if (self::$signalingException !== $e) {
throw $e;
}
$logger && $logger->info('Item "{key}" not found while lock was released, now retrying', ['key' => $item->getKey()]);
}
}
return null;
}
private static function open(int $key)
{
if (null !== $h = self::$openedFiles[$key] ?? null) {
return $h;
}
set_error_handler(function () {});
try {
$h = fopen(self::$files[$key], 'r+');
} finally {
restore_error_handler();
}
return self::$openedFiles[$key] = $h ?: @fopen(self::$files[$key], 'r');
}
}

View file

@ -0,0 +1,99 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Cache\Marshaller;
use Symfony\Component\Cache\Exception\CacheException;
/**
* Serializes/unserializes values using igbinary_serialize() if available, serialize() otherwise.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
class DefaultMarshaller implements MarshallerInterface
{
private $useIgbinarySerialize = true;
public function __construct(bool $useIgbinarySerialize = null)
{
if (null === $useIgbinarySerialize) {
$useIgbinarySerialize = \extension_loaded('igbinary') && (\PHP_VERSION_ID < 70400 || version_compare('3.1.6', phpversion('igbinary'), '<='));
} elseif ($useIgbinarySerialize && (!\extension_loaded('igbinary') || (\PHP_VERSION_ID >= 70400 && version_compare('3.1.6', phpversion('igbinary'), '>')))) {
throw new CacheException(\extension_loaded('igbinary') && \PHP_VERSION_ID >= 70400 ? 'Please upgrade the "igbinary" PHP extension to v3.1.6 or higher.' : 'The "igbinary" PHP extension is not loaded.');
}
$this->useIgbinarySerialize = $useIgbinarySerialize;
}
/**
* {@inheritdoc}
*/
public function marshall(array $values, ?array &$failed): array
{
$serialized = $failed = [];
foreach ($values as $id => $value) {
try {
if ($this->useIgbinarySerialize) {
$serialized[$id] = igbinary_serialize($value);
} else {
$serialized[$id] = serialize($value);
}
} catch (\Exception $e) {
$failed[] = $id;
}
}
return $serialized;
}
/**
* {@inheritdoc}
*/
public function unmarshall(string $value)
{
if ('b:0;' === $value) {
return false;
}
if ('N;' === $value) {
return null;
}
static $igbinaryNull;
if ($value === ($igbinaryNull ?? $igbinaryNull = \extension_loaded('igbinary') ? igbinary_serialize(null) : false)) {
return null;
}
$unserializeCallbackHandler = ini_set('unserialize_callback_func', __CLASS__.'::handleUnserializeCallback');
try {
if (':' === ($value[1] ?? ':')) {
if (false !== $value = unserialize($value)) {
return $value;
}
} elseif (false === $igbinaryNull) {
throw new \RuntimeException('Failed to unserialize values, did you forget to install the "igbinary" extension?');
} elseif (null !== $value = igbinary_unserialize($value)) {
return $value;
}
throw new \DomainException(error_get_last() ? error_get_last()['message'] : 'Failed to unserialize values.');
} catch (\Error $e) {
throw new \ErrorException($e->getMessage(), $e->getCode(), \E_ERROR, $e->getFile(), $e->getLine());
} finally {
ini_set('unserialize_callback_func', $unserializeCallbackHandler);
}
}
/**
* @internal
*/
public static function handleUnserializeCallback($class)
{
throw new \DomainException('Class not found: '.$class);
}
}

View file

@ -0,0 +1,53 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Cache\Marshaller;
use Symfony\Component\Cache\Exception\CacheException;
/**
* Compresses values using gzdeflate().
*
* @author Nicolas Grekas <p@tchwork.com>
*/
class DeflateMarshaller implements MarshallerInterface
{
private $marshaller;
public function __construct(MarshallerInterface $marshaller)
{
if (!\function_exists('gzdeflate')) {
throw new CacheException('The "zlib" PHP extension is not loaded.');
}
$this->marshaller = $marshaller;
}
/**
* {@inheritdoc}
*/
public function marshall(array $values, ?array &$failed): array
{
return array_map('gzdeflate', $this->marshaller->marshall($values, $failed));
}
/**
* {@inheritdoc}
*/
public function unmarshall(string $value)
{
if (false !== $inflatedValue = @gzinflate($value)) {
$value = $inflatedValue;
}
return $this->marshaller->unmarshall($value);
}
}

View file

@ -0,0 +1,40 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Cache\Marshaller;
/**
* Serializes/unserializes PHP values.
*
* Implementations of this interface MUST deal with errors carefully. They MUST
* also deal with forward and backward compatibility at the storage format level.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
interface MarshallerInterface
{
/**
* Serializes a list of values.
*
* When serialization fails for a specific value, no exception should be
* thrown. Instead, its key should be listed in $failed.
*/
public function marshall(array $values, ?array &$failed): array;
/**
* Unserializes a single value and throws an exception if anything goes wrong.
*
* @return mixed
*
* @throws \Exception Whenever unserialization fails
*/
public function unmarshall(string $value);
}

View file

@ -0,0 +1,89 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Cache\Marshaller;
/**
* A marshaller optimized for data structures generated by AbstractTagAwareAdapter.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
class TagAwareMarshaller implements MarshallerInterface
{
private $marshaller;
public function __construct(MarshallerInterface $marshaller = null)
{
$this->marshaller = $marshaller ?? new DefaultMarshaller();
}
/**
* {@inheritdoc}
*/
public function marshall(array $values, ?array &$failed): array
{
$failed = $notSerialized = $serialized = [];
foreach ($values as $id => $value) {
if (\is_array($value) && \is_array($value['tags'] ?? null) && \array_key_exists('value', $value) && \count($value) === 2 + (\is_string($value['meta'] ?? null) && 8 === \strlen($value['meta']))) {
// if the value is an array with keys "tags", "value" and "meta", use a compact serialization format
// magic numbers in the form 9D-..-..-..-..-00-..-..-..-5F allow detecting this format quickly in unmarshall()
$v = $this->marshaller->marshall($value, $f);
if ($f) {
$f = [];
$failed[] = $id;
} else {
if ([] === $value['tags']) {
$v['tags'] = '';
}
$serialized[$id] = "\x9D".($value['meta'] ?? "\0\0\0\0\0\0\0\0").pack('N', \strlen($v['tags'])).$v['tags'].$v['value'];
$serialized[$id][9] = "\x5F";
}
} else {
// other arbitratry values are serialized using the decorated marshaller below
$notSerialized[$id] = $value;
}
}
if ($notSerialized) {
$serialized += $this->marshaller->marshall($notSerialized, $f);
$failed = array_merge($failed, $f);
}
return $serialized;
}
/**
* {@inheritdoc}
*/
public function unmarshall(string $value)
{
// detect the compact format used in marshall() using magic numbers in the form 9D-..-..-..-..-00-..-..-..-5F
if (13 >= \strlen($value) || "\x9D" !== $value[0] || "\0" !== $value[5] || "\x5F" !== $value[9]) {
return $this->marshaller->unmarshall($value);
}
// data consists of value, tags and metadata which we need to unpack
$meta = substr($value, 1, 12);
$meta[8] = "\0";
$tagLen = unpack('Nlen', $meta, 8)['len'];
$meta = substr($meta, 0, 8);
return [
'value' => $this->marshaller->unmarshall(substr($value, 13 + $tagLen)),
'tags' => $tagLen ? $this->marshaller->unmarshall(substr($value, 13, $tagLen)) : [],
'meta' => "\0\0\0\0\0\0\0\0" === $meta ? null : $meta,
];
}
}

View file

@ -0,0 +1,284 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Cache;
use Psr\Cache\CacheException as Psr6CacheException;
use Psr\Cache\CacheItemPoolInterface;
use Psr\SimpleCache\CacheException as SimpleCacheException;
use Psr\SimpleCache\CacheInterface;
use Symfony\Component\Cache\Adapter\AdapterInterface;
use Symfony\Component\Cache\Exception\InvalidArgumentException;
use Symfony\Component\Cache\Traits\ProxyTrait;
if (null !== (new \ReflectionMethod(CacheInterface::class, 'get'))->getReturnType()) {
throw new \LogicException('psr/simple-cache 3.0+ is not compatible with this version of symfony/cache. Please upgrade symfony/cache to 6.0+ or downgrade psr/simple-cache to 1.x or 2.x.');
}
/**
* Turns a PSR-6 cache into a PSR-16 one.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
class Psr16Cache implements CacheInterface, PruneableInterface, ResettableInterface
{
use ProxyTrait;
private const METADATA_EXPIRY_OFFSET = 1527506807;
private $createCacheItem;
private $cacheItemPrototype;
public function __construct(CacheItemPoolInterface $pool)
{
$this->pool = $pool;
if (!$pool instanceof AdapterInterface) {
return;
}
$cacheItemPrototype = &$this->cacheItemPrototype;
$createCacheItem = \Closure::bind(
static function ($key, $value, $allowInt = false) use (&$cacheItemPrototype) {
$item = clone $cacheItemPrototype;
$item->poolHash = $item->innerItem = null;
$item->key = $allowInt && \is_int($key) ? (string) $key : CacheItem::validateKey($key);
$item->value = $value;
$item->isHit = false;
return $item;
},
null,
CacheItem::class
);
$this->createCacheItem = function ($key, $value, $allowInt = false) use ($createCacheItem) {
if (null === $this->cacheItemPrototype) {
$this->get($allowInt && \is_int($key) ? (string) $key : $key);
}
$this->createCacheItem = $createCacheItem;
return $createCacheItem($key, null, $allowInt)->set($value);
};
}
/**
* {@inheritdoc}
*
* @return mixed
*/
public function get($key, $default = null)
{
try {
$item = $this->pool->getItem($key);
} catch (SimpleCacheException $e) {
throw $e;
} catch (Psr6CacheException $e) {
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
}
if (null === $this->cacheItemPrototype) {
$this->cacheItemPrototype = clone $item;
$this->cacheItemPrototype->set(null);
}
return $item->isHit() ? $item->get() : $default;
}
/**
* {@inheritdoc}
*
* @return bool
*/
public function set($key, $value, $ttl = null)
{
try {
if (null !== $f = $this->createCacheItem) {
$item = $f($key, $value);
} else {
$item = $this->pool->getItem($key)->set($value);
}
} catch (SimpleCacheException $e) {
throw $e;
} catch (Psr6CacheException $e) {
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
}
if (null !== $ttl) {
$item->expiresAfter($ttl);
}
return $this->pool->save($item);
}
/**
* {@inheritdoc}
*
* @return bool
*/
public function delete($key)
{
try {
return $this->pool->deleteItem($key);
} catch (SimpleCacheException $e) {
throw $e;
} catch (Psr6CacheException $e) {
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
}
}
/**
* {@inheritdoc}
*
* @return bool
*/
public function clear()
{
return $this->pool->clear();
}
/**
* {@inheritdoc}
*
* @return iterable
*/
public function getMultiple($keys, $default = null)
{
if ($keys instanceof \Traversable) {
$keys = iterator_to_array($keys, false);
} elseif (!\is_array($keys)) {
throw new InvalidArgumentException(sprintf('Cache keys must be array or Traversable, "%s" given.', \is_object($keys) ? \get_class($keys) : \gettype($keys)));
}
try {
$items = $this->pool->getItems($keys);
} catch (SimpleCacheException $e) {
throw $e;
} catch (Psr6CacheException $e) {
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
}
$values = [];
if (!$this->pool instanceof AdapterInterface) {
foreach ($items as $key => $item) {
$values[$key] = $item->isHit() ? $item->get() : $default;
}
return $values;
}
foreach ($items as $key => $item) {
if (!$item->isHit()) {
$values[$key] = $default;
continue;
}
$values[$key] = $item->get();
if (!$metadata = $item->getMetadata()) {
continue;
}
unset($metadata[CacheItem::METADATA_TAGS]);
if ($metadata) {
$values[$key] = ["\x9D".pack('VN', (int) (0.1 + $metadata[CacheItem::METADATA_EXPIRY] - self::METADATA_EXPIRY_OFFSET), $metadata[CacheItem::METADATA_CTIME])."\x5F" => $values[$key]];
}
}
return $values;
}
/**
* {@inheritdoc}
*
* @return bool
*/
public function setMultiple($values, $ttl = null)
{
$valuesIsArray = \is_array($values);
if (!$valuesIsArray && !$values instanceof \Traversable) {
throw new InvalidArgumentException(sprintf('Cache values must be array or Traversable, "%s" given.', \is_object($values) ? \get_class($values) : \gettype($values)));
}
$items = [];
try {
if (null !== $f = $this->createCacheItem) {
$valuesIsArray = false;
foreach ($values as $key => $value) {
$items[$key] = $f($key, $value, true);
}
} elseif ($valuesIsArray) {
$items = [];
foreach ($values as $key => $value) {
$items[] = (string) $key;
}
$items = $this->pool->getItems($items);
} else {
foreach ($values as $key => $value) {
if (\is_int($key)) {
$key = (string) $key;
}
$items[$key] = $this->pool->getItem($key)->set($value);
}
}
} catch (SimpleCacheException $e) {
throw $e;
} catch (Psr6CacheException $e) {
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
}
$ok = true;
foreach ($items as $key => $item) {
if ($valuesIsArray) {
$item->set($values[$key]);
}
if (null !== $ttl) {
$item->expiresAfter($ttl);
}
$ok = $this->pool->saveDeferred($item) && $ok;
}
return $this->pool->commit() && $ok;
}
/**
* {@inheritdoc}
*
* @return bool
*/
public function deleteMultiple($keys)
{
if ($keys instanceof \Traversable) {
$keys = iterator_to_array($keys, false);
} elseif (!\is_array($keys)) {
throw new InvalidArgumentException(sprintf('Cache keys must be array or Traversable, "%s" given.', \is_object($keys) ? \get_class($keys) : \gettype($keys)));
}
try {
return $this->pool->deleteItems($keys);
} catch (SimpleCacheException $e) {
throw $e;
} catch (Psr6CacheException $e) {
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
}
}
/**
* {@inheritdoc}
*
* @return bool
*/
public function has($key)
{
try {
return $this->pool->hasItem($key);
} catch (SimpleCacheException $e) {
throw $e;
} catch (Psr6CacheException $e) {
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
}
}
}

View file

@ -1,12 +1,13 @@
Symfony PSR-6 implementation for caching Symfony PSR-6 implementation for caching
======================================== ========================================
This component provides an extended [PSR-6](http://www.php-fig.org/psr/psr-6/) The Cache component provides extended
implementation for adding cache to your applications. It is designed to have a [PSR-6](https://www.php-fig.org/psr/psr-6/) implementations for adding cache to
low overhead so that caching is fastest. It ships with a few caching adapters your applications. It is designed to have a low overhead so that caching is
for the most widespread and suited to caching backends. It also provides a fastest. It ships with adapters for the most widespread caching backends.
`doctrine/cache` proxy adapter to cover more advanced caching needs and a proxy It also provides a [PSR-16](https://www.php-fig.org/psr/psr-16/) adapter,
adapter for greater interoperability between PSR-6 implementations. and implementations for [symfony/cache-contracts](https://github.com/symfony/cache-contracts)'
`CacheInterface` and `TagAwareCacheInterface`.
Resources Resources
--------- ---------

View file

@ -11,10 +11,11 @@
namespace Symfony\Component\Cache; namespace Symfony\Component\Cache;
use Symfony\Contracts\Service\ResetInterface;
/** /**
* Resets a pool's local state. * Resets a pool's local state.
*/ */
interface ResettableInterface interface ResettableInterface extends ResetInterface
{ {
public function reset();
} }

View file

@ -12,37 +12,37 @@
namespace Symfony\Component\Cache\Simple; namespace Symfony\Component\Cache\Simple;
use Psr\Log\LoggerAwareInterface; use Psr\Log\LoggerAwareInterface;
use Psr\SimpleCache\CacheInterface; use Psr\SimpleCache\CacheInterface as Psr16CacheInterface;
use Symfony\Component\Cache\Adapter\AbstractAdapter;
use Symfony\Component\Cache\CacheItem; use Symfony\Component\Cache\CacheItem;
use Symfony\Component\Cache\Exception\InvalidArgumentException; use Symfony\Component\Cache\Exception\InvalidArgumentException;
use Symfony\Component\Cache\ResettableInterface; use Symfony\Component\Cache\ResettableInterface;
use Symfony\Component\Cache\Traits\AbstractTrait; use Symfony\Component\Cache\Traits\AbstractTrait;
use Symfony\Contracts\Cache\CacheInterface;
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', AbstractCache::class, AbstractAdapter::class, CacheInterface::class), \E_USER_DEPRECATED);
/** /**
* @author Nicolas Grekas <p@tchwork.com> * @deprecated since Symfony 4.3, use AbstractAdapter and type-hint for CacheInterface instead.
*/ */
abstract class AbstractCache implements CacheInterface, LoggerAwareInterface, ResettableInterface abstract class AbstractCache implements Psr16CacheInterface, LoggerAwareInterface, ResettableInterface
{ {
/**
* @internal
*/
const NS_SEPARATOR = ':';
use AbstractTrait { use AbstractTrait {
deleteItems as private; deleteItems as private;
AbstractTrait::deleteItem as delete; AbstractTrait::deleteItem as delete;
AbstractTrait::hasItem as has; AbstractTrait::hasItem as has;
} }
/**
* @internal
*/
protected const NS_SEPARATOR = ':';
private $defaultLifetime; private $defaultLifetime;
/** protected function __construct(string $namespace = '', int $defaultLifetime = 0)
* @param string $namespace
* @param int $defaultLifetime
*/
protected function __construct($namespace = '', $defaultLifetime = 0)
{ {
$this->defaultLifetime = max(0, (int) $defaultLifetime); $this->defaultLifetime = max(0, $defaultLifetime);
$this->namespace = '' === $namespace ? '' : CacheItem::validateKey($namespace).':'; $this->namespace = '' === $namespace ? '' : CacheItem::validateKey($namespace).':';
if (null !== $this->maxIdLength && \strlen($namespace) > $this->maxIdLength - 24) { if (null !== $this->maxIdLength && \strlen($namespace) > $this->maxIdLength - 24) {
throw new InvalidArgumentException(sprintf('Namespace must be %d chars max, %d given ("%s").', $this->maxIdLength - 24, \strlen($namespace), $namespace)); throw new InvalidArgumentException(sprintf('Namespace must be %d chars max, %d given ("%s").', $this->maxIdLength - 24, \strlen($namespace), $namespace));
@ -61,7 +61,7 @@ abstract class AbstractCache implements CacheInterface, LoggerAwareInterface, Re
return $value; return $value;
} }
} catch (\Exception $e) { } catch (\Exception $e) {
CacheItem::log($this->logger, 'Failed to fetch key "{key}"', ['key' => $key, 'exception' => $e]); CacheItem::log($this->logger, 'Failed to fetch key "{key}": '.$e->getMessage(), ['key' => $key, 'exception' => $e]);
} }
return $default; return $default;
@ -69,6 +69,8 @@ abstract class AbstractCache implements CacheInterface, LoggerAwareInterface, Re
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function set($key, $value, $ttl = null) public function set($key, $value, $ttl = null)
{ {
@ -79,6 +81,8 @@ abstract class AbstractCache implements CacheInterface, LoggerAwareInterface, Re
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return iterable
*/ */
public function getMultiple($keys, $default = null) public function getMultiple($keys, $default = null)
{ {
@ -95,7 +99,7 @@ abstract class AbstractCache implements CacheInterface, LoggerAwareInterface, Re
try { try {
$values = $this->doFetch($ids); $values = $this->doFetch($ids);
} catch (\Exception $e) { } catch (\Exception $e) {
CacheItem::log($this->logger, 'Failed to fetch requested values', ['keys' => $keys, 'exception' => $e]); CacheItem::log($this->logger, 'Failed to fetch values: '.$e->getMessage(), ['keys' => $keys, 'exception' => $e]);
$values = []; $values = [];
} }
$ids = array_combine($ids, $keys); $ids = array_combine($ids, $keys);
@ -105,6 +109,8 @@ abstract class AbstractCache implements CacheInterface, LoggerAwareInterface, Re
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function setMultiple($values, $ttl = null) public function setMultiple($values, $ttl = null)
{ {
@ -134,13 +140,16 @@ abstract class AbstractCache implements CacheInterface, LoggerAwareInterface, Re
foreach (\is_array($e) ? $e : array_keys($valuesById) as $id) { foreach (\is_array($e) ? $e : array_keys($valuesById) as $id) {
$keys[] = substr($id, \strlen($this->namespace)); $keys[] = substr($id, \strlen($this->namespace));
} }
CacheItem::log($this->logger, 'Failed to save values', ['keys' => $keys, 'exception' => $e instanceof \Exception ? $e : null]); $message = 'Failed to save values'.($e instanceof \Exception ? ': '.$e->getMessage() : '.');
CacheItem::log($this->logger, $message, ['keys' => $keys, 'exception' => $e instanceof \Exception ? $e : null]);
return false; return false;
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function deleteMultiple($keys) public function deleteMultiple($keys)
{ {
@ -168,19 +177,19 @@ abstract class AbstractCache implements CacheInterface, LoggerAwareInterface, Re
throw new InvalidArgumentException(sprintf('Expiration date must be an integer, a DateInterval or null, "%s" given.', \is_object($ttl) ? \get_class($ttl) : \gettype($ttl))); throw new InvalidArgumentException(sprintf('Expiration date must be an integer, a DateInterval or null, "%s" given.', \is_object($ttl) ? \get_class($ttl) : \gettype($ttl)));
} }
private function generateValues($values, &$keys, $default) private function generateValues(iterable $values, array &$keys, $default): iterable
{ {
try { try {
foreach ($values as $id => $value) { foreach ($values as $id => $value) {
if (!isset($keys[$id])) { if (!isset($keys[$id])) {
$id = key($keys); throw new InvalidArgumentException(sprintf('Could not match value id "%s" to keys "%s".', $id, implode('", "', $keys)));
} }
$key = $keys[$id]; $key = $keys[$id];
unset($keys[$id]); unset($keys[$id]);
yield $key => $value; yield $key => $value;
} }
} catch (\Exception $e) { } catch (\Exception $e) {
CacheItem::log($this->logger, 'Failed to fetch requested values', ['keys' => array_values($keys), 'exception' => $e]); CacheItem::log($this->logger, 'Failed to fetch values: '.$e->getMessage(), ['keys' => array_values($keys), 'exception' => $e]);
} }
foreach ($keys as $key) { foreach ($keys as $key) {

View file

@ -11,18 +11,20 @@
namespace Symfony\Component\Cache\Simple; namespace Symfony\Component\Cache\Simple;
use Symfony\Component\Cache\Adapter\ApcuAdapter;
use Symfony\Component\Cache\Traits\ApcuTrait; use Symfony\Component\Cache\Traits\ApcuTrait;
use Symfony\Contracts\Cache\CacheInterface;
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', ApcuCache::class, ApcuAdapter::class, CacheInterface::class), \E_USER_DEPRECATED);
/**
* @deprecated since Symfony 4.3, use ApcuAdapter and type-hint for CacheInterface instead.
*/
class ApcuCache extends AbstractCache class ApcuCache extends AbstractCache
{ {
use ApcuTrait; use ApcuTrait;
/** public function __construct(string $namespace = '', int $defaultLifetime = 0, string $version = null)
* @param string $namespace
* @param int $defaultLifetime
* @param string|null $version
*/
public function __construct($namespace = '', $defaultLifetime = 0, $version = null)
{ {
$this->init($namespace, $defaultLifetime, $version); $this->init($namespace, $defaultLifetime, $version);
} }

View file

@ -12,16 +12,20 @@
namespace Symfony\Component\Cache\Simple; namespace Symfony\Component\Cache\Simple;
use Psr\Log\LoggerAwareInterface; use Psr\Log\LoggerAwareInterface;
use Psr\SimpleCache\CacheInterface; use Psr\SimpleCache\CacheInterface as Psr16CacheInterface;
use Symfony\Component\Cache\Adapter\ArrayAdapter;
use Symfony\Component\Cache\CacheItem; use Symfony\Component\Cache\CacheItem;
use Symfony\Component\Cache\Exception\InvalidArgumentException; use Symfony\Component\Cache\Exception\InvalidArgumentException;
use Symfony\Component\Cache\ResettableInterface; use Symfony\Component\Cache\ResettableInterface;
use Symfony\Component\Cache\Traits\ArrayTrait; use Symfony\Component\Cache\Traits\ArrayTrait;
use Symfony\Contracts\Cache\CacheInterface;
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', ArrayCache::class, ArrayAdapter::class, CacheInterface::class), \E_USER_DEPRECATED);
/** /**
* @author Nicolas Grekas <p@tchwork.com> * @deprecated since Symfony 4.3, use ArrayAdapter and type-hint for CacheInterface instead.
*/ */
class ArrayCache implements CacheInterface, LoggerAwareInterface, ResettableInterface class ArrayCache implements Psr16CacheInterface, LoggerAwareInterface, ResettableInterface
{ {
use ArrayTrait { use ArrayTrait {
ArrayTrait::deleteItem as delete; ArrayTrait::deleteItem as delete;
@ -31,12 +35,11 @@ class ArrayCache implements CacheInterface, LoggerAwareInterface, ResettableInte
private $defaultLifetime; private $defaultLifetime;
/** /**
* @param int $defaultLifetime
* @param bool $storeSerialized Disabling serialization can lead to cache corruptions when storing mutable values but increases performance otherwise * @param bool $storeSerialized Disabling serialization can lead to cache corruptions when storing mutable values but increases performance otherwise
*/ */
public function __construct($defaultLifetime = 0, $storeSerialized = true) public function __construct(int $defaultLifetime = 0, bool $storeSerialized = true)
{ {
$this->defaultLifetime = (int) $defaultLifetime; $this->defaultLifetime = $defaultLifetime;
$this->storeSerialized = $storeSerialized; $this->storeSerialized = $storeSerialized;
} }
@ -45,13 +48,26 @@ class ArrayCache implements CacheInterface, LoggerAwareInterface, ResettableInte
*/ */
public function get($key, $default = null) public function get($key, $default = null)
{ {
foreach ($this->getMultiple([$key], $default) as $v) { if (!\is_string($key) || !isset($this->expiries[$key])) {
return $v; CacheItem::validateKey($key);
} }
if (!$isHit = isset($this->expiries[$key]) && ($this->expiries[$key] > microtime(true) || !$this->delete($key))) {
$this->values[$key] = null;
return $default;
}
if (!$this->storeSerialized) {
return $this->values[$key];
}
$value = $this->unfreeze($key, $isHit);
return $isHit ? $value : $default;
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return iterable
*/ */
public function getMultiple($keys, $default = null) public function getMultiple($keys, $default = null)
{ {
@ -61,14 +77,18 @@ class ArrayCache implements CacheInterface, LoggerAwareInterface, ResettableInte
throw new InvalidArgumentException(sprintf('Cache keys must be array or Traversable, "%s" given.', \is_object($keys) ? \get_class($keys) : \gettype($keys))); throw new InvalidArgumentException(sprintf('Cache keys must be array or Traversable, "%s" given.', \is_object($keys) ? \get_class($keys) : \gettype($keys)));
} }
foreach ($keys as $key) { foreach ($keys as $key) {
if (!\is_string($key) || !isset($this->expiries[$key])) {
CacheItem::validateKey($key); CacheItem::validateKey($key);
} }
}
return $this->generateItems($keys, time(), function ($k, $v, $hit) use ($default) { return $hit ? $v : $default; }); return $this->generateItems($keys, microtime(true), function ($k, $v, $hit) use ($default) { return $hit ? $v : $default; });
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function deleteMultiple($keys) public function deleteMultiple($keys)
{ {
@ -84,16 +104,22 @@ class ArrayCache implements CacheInterface, LoggerAwareInterface, ResettableInte
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function set($key, $value, $ttl = null) public function set($key, $value, $ttl = null)
{ {
if (!\is_string($key)) {
CacheItem::validateKey($key); CacheItem::validateKey($key);
}
return $this->setMultiple([$key => $value], $ttl); return $this->setMultiple([$key => $value], $ttl);
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function setMultiple($values, $ttl = null) public function setMultiple($values, $ttl = null)
{ {
@ -103,27 +129,20 @@ class ArrayCache implements CacheInterface, LoggerAwareInterface, ResettableInte
$valuesArray = []; $valuesArray = [];
foreach ($values as $key => $value) { foreach ($values as $key => $value) {
\is_int($key) || CacheItem::validateKey($key); if (!\is_int($key) && !(\is_string($key) && isset($this->expiries[$key]))) {
CacheItem::validateKey($key);
}
$valuesArray[$key] = $value; $valuesArray[$key] = $value;
} }
if (false === $ttl = $this->normalizeTtl($ttl)) { if (false === $ttl = $this->normalizeTtl($ttl)) {
return $this->deleteMultiple(array_keys($valuesArray)); return $this->deleteMultiple(array_keys($valuesArray));
} }
if ($this->storeSerialized) { $expiry = 0 < $ttl ? microtime(true) + $ttl : \PHP_INT_MAX;
foreach ($valuesArray as $key => $value) {
try {
$valuesArray[$key] = serialize($value);
} catch (\Exception $e) {
$type = \is_object($value) ? \get_class($value) : \gettype($value);
CacheItem::log($this->logger, 'Failed to save key "{key}" ({type})', ['key' => $key, 'type' => $type, 'exception' => $e]);
foreach ($valuesArray as $key => $value) {
if ($this->storeSerialized && null === $value = $this->freeze($value, $key)) {
return false; return false;
} }
}
}
$expiry = 0 < $ttl ? time() + $ttl : \PHP_INT_MAX;
foreach ($valuesArray as $key => $value) {
$this->values[$key] = $value; $this->values[$key] = $value;
$this->expiries[$key] = $expiry; $this->expiries[$key] = $expiry;
} }

View file

@ -11,10 +11,15 @@
namespace Symfony\Component\Cache\Simple; namespace Symfony\Component\Cache\Simple;
use Psr\SimpleCache\CacheInterface; use Psr\SimpleCache\CacheInterface as Psr16CacheInterface;
use Symfony\Component\Cache\Adapter\ChainAdapter;
use Symfony\Component\Cache\Exception\InvalidArgumentException; use Symfony\Component\Cache\Exception\InvalidArgumentException;
use Symfony\Component\Cache\PruneableInterface; use Symfony\Component\Cache\PruneableInterface;
use Symfony\Component\Cache\ResettableInterface; use Symfony\Component\Cache\ResettableInterface;
use Symfony\Contracts\Cache\CacheInterface;
use Symfony\Contracts\Service\ResetInterface;
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', ChainCache::class, ChainAdapter::class, CacheInterface::class), \E_USER_DEPRECATED);
/** /**
* Chains several caches together. * Chains several caches together.
@ -22,9 +27,9 @@ use Symfony\Component\Cache\ResettableInterface;
* Cached items are fetched from the first cache having them in its data store. * Cached items are fetched from the first cache having them in its data store.
* They are saved and deleted in all caches at once. * They are saved and deleted in all caches at once.
* *
* @author Nicolas Grekas <p@tchwork.com> * @deprecated since Symfony 4.3, use ChainAdapter and type-hint for CacheInterface instead.
*/ */
class ChainCache implements CacheInterface, PruneableInterface, ResettableInterface class ChainCache implements Psr16CacheInterface, PruneableInterface, ResettableInterface
{ {
private $miss; private $miss;
private $caches = []; private $caches = [];
@ -32,25 +37,25 @@ class ChainCache implements CacheInterface, PruneableInterface, ResettableInterf
private $cacheCount; private $cacheCount;
/** /**
* @param CacheInterface[] $caches The ordered list of caches used to fetch cached items * @param Psr16CacheInterface[] $caches The ordered list of caches used to fetch cached items
* @param int $defaultLifetime The lifetime of items propagated from lower caches to upper ones * @param int $defaultLifetime The lifetime of items propagated from lower caches to upper ones
*/ */
public function __construct(array $caches, $defaultLifetime = 0) public function __construct(array $caches, int $defaultLifetime = 0)
{ {
if (!$caches) { if (!$caches) {
throw new InvalidArgumentException('At least one cache must be specified.'); throw new InvalidArgumentException('At least one cache must be specified.');
} }
foreach ($caches as $cache) { foreach ($caches as $cache) {
if (!$cache instanceof CacheInterface) { if (!$cache instanceof Psr16CacheInterface) {
throw new InvalidArgumentException(sprintf('The class "%s" does not implement the "%s" interface.', \get_class($cache), CacheInterface::class)); throw new InvalidArgumentException(sprintf('The class "%s" does not implement the "%s" interface.', \get_class($cache), Psr16CacheInterface::class));
} }
} }
$this->miss = new \stdClass(); $this->miss = new \stdClass();
$this->caches = array_values($caches); $this->caches = array_values($caches);
$this->cacheCount = \count($this->caches); $this->cacheCount = \count($this->caches);
$this->defaultLifetime = 0 < $defaultLifetime ? (int) $defaultLifetime : null; $this->defaultLifetime = 0 < $defaultLifetime ? $defaultLifetime : null;
} }
/** /**
@ -77,6 +82,8 @@ class ChainCache implements CacheInterface, PruneableInterface, ResettableInterf
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return iterable
*/ */
public function getMultiple($keys, $default = null) public function getMultiple($keys, $default = null)
{ {
@ -85,11 +92,11 @@ class ChainCache implements CacheInterface, PruneableInterface, ResettableInterf
return $this->generateItems($this->caches[0]->getMultiple($keys, $miss), 0, $miss, $default); return $this->generateItems($this->caches[0]->getMultiple($keys, $miss), 0, $miss, $default);
} }
private function generateItems($values, $cacheIndex, $miss, $default) private function generateItems(iterable $values, int $cacheIndex, $miss, $default): iterable
{ {
$missing = []; $missing = [];
$nextCacheIndex = $cacheIndex + 1; $nextCacheIndex = $cacheIndex + 1;
$nextCache = isset($this->caches[$nextCacheIndex]) ? $this->caches[$nextCacheIndex] : null; $nextCache = $this->caches[$nextCacheIndex] ?? null;
foreach ($values as $k => $value) { foreach ($values as $k => $value) {
if ($miss !== $value) { if ($miss !== $value) {
@ -118,6 +125,8 @@ class ChainCache implements CacheInterface, PruneableInterface, ResettableInterf
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function has($key) public function has($key)
{ {
@ -132,6 +141,8 @@ class ChainCache implements CacheInterface, PruneableInterface, ResettableInterf
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function clear() public function clear()
{ {
@ -147,6 +158,8 @@ class ChainCache implements CacheInterface, PruneableInterface, ResettableInterf
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function delete($key) public function delete($key)
{ {
@ -162,6 +175,8 @@ class ChainCache implements CacheInterface, PruneableInterface, ResettableInterf
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function deleteMultiple($keys) public function deleteMultiple($keys)
{ {
@ -180,6 +195,8 @@ class ChainCache implements CacheInterface, PruneableInterface, ResettableInterf
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function set($key, $value, $ttl = null) public function set($key, $value, $ttl = null)
{ {
@ -195,6 +212,8 @@ class ChainCache implements CacheInterface, PruneableInterface, ResettableInterf
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function setMultiple($values, $ttl = null) public function setMultiple($values, $ttl = null)
{ {
@ -244,7 +263,7 @@ class ChainCache implements CacheInterface, PruneableInterface, ResettableInterf
public function reset() public function reset()
{ {
foreach ($this->caches as $cache) { foreach ($this->caches as $cache) {
if ($cache instanceof ResettableInterface) { if ($cache instanceof ResetInterface) {
$cache->reset(); $cache->reset();
} }
} }

View file

@ -12,17 +12,20 @@
namespace Symfony\Component\Cache\Simple; namespace Symfony\Component\Cache\Simple;
use Doctrine\Common\Cache\CacheProvider; use Doctrine\Common\Cache\CacheProvider;
use Symfony\Component\Cache\Adapter\DoctrineAdapter;
use Symfony\Component\Cache\Traits\DoctrineTrait; use Symfony\Component\Cache\Traits\DoctrineTrait;
use Symfony\Contracts\Cache\CacheInterface;
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', DoctrineCache::class, DoctrineAdapter::class, CacheInterface::class), \E_USER_DEPRECATED);
/**
* @deprecated since Symfony 4.3, use DoctrineAdapter and type-hint for CacheInterface instead.
*/
class DoctrineCache extends AbstractCache class DoctrineCache extends AbstractCache
{ {
use DoctrineTrait; use DoctrineTrait;
/** public function __construct(CacheProvider $provider, string $namespace = '', int $defaultLifetime = 0)
* @param string $namespace
* @param int $defaultLifetime
*/
public function __construct(CacheProvider $provider, $namespace = '', $defaultLifetime = 0)
{ {
parent::__construct('', $defaultLifetime); parent::__construct('', $defaultLifetime);
$this->provider = $provider; $this->provider = $provider;

View file

@ -11,20 +11,25 @@
namespace Symfony\Component\Cache\Simple; namespace Symfony\Component\Cache\Simple;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
use Symfony\Component\Cache\Marshaller\DefaultMarshaller;
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
use Symfony\Component\Cache\PruneableInterface; use Symfony\Component\Cache\PruneableInterface;
use Symfony\Component\Cache\Traits\FilesystemTrait; use Symfony\Component\Cache\Traits\FilesystemTrait;
use Symfony\Contracts\Cache\CacheInterface;
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', FilesystemCache::class, FilesystemAdapter::class, CacheInterface::class), \E_USER_DEPRECATED);
/**
* @deprecated since Symfony 4.3, use FilesystemAdapter and type-hint for CacheInterface instead.
*/
class FilesystemCache extends AbstractCache implements PruneableInterface class FilesystemCache extends AbstractCache implements PruneableInterface
{ {
use FilesystemTrait; use FilesystemTrait;
/** public function __construct(string $namespace = '', int $defaultLifetime = 0, string $directory = null, MarshallerInterface $marshaller = null)
* @param string $namespace
* @param int $defaultLifetime
* @param string|null $directory
*/
public function __construct($namespace = '', $defaultLifetime = 0, $directory = null)
{ {
$this->marshaller = $marshaller ?? new DefaultMarshaller();
parent::__construct('', $defaultLifetime); parent::__construct('', $defaultLifetime);
$this->init($namespace, $directory); $this->init($namespace, $directory);
} }

View file

@ -11,20 +11,24 @@
namespace Symfony\Component\Cache\Simple; namespace Symfony\Component\Cache\Simple;
use Symfony\Component\Cache\Adapter\MemcachedAdapter;
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
use Symfony\Component\Cache\Traits\MemcachedTrait; use Symfony\Component\Cache\Traits\MemcachedTrait;
use Symfony\Contracts\Cache\CacheInterface;
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', MemcachedCache::class, MemcachedAdapter::class, CacheInterface::class), \E_USER_DEPRECATED);
/**
* @deprecated since Symfony 4.3, use MemcachedAdapter and type-hint for CacheInterface instead.
*/
class MemcachedCache extends AbstractCache class MemcachedCache extends AbstractCache
{ {
use MemcachedTrait; use MemcachedTrait;
protected $maxIdLength = 250; protected $maxIdLength = 250;
/** public function __construct(\Memcached $client, string $namespace = '', int $defaultLifetime = 0, MarshallerInterface $marshaller = null)
* @param string $namespace
* @param int $defaultLifetime
*/
public function __construct(\Memcached $client, $namespace = '', $defaultLifetime = 0)
{ {
$this->init($client, $namespace, $defaultLifetime); $this->init($client, $namespace, $defaultLifetime, $marshaller);
} }
} }

View file

@ -11,12 +11,16 @@
namespace Symfony\Component\Cache\Simple; namespace Symfony\Component\Cache\Simple;
use Psr\SimpleCache\CacheInterface; use Psr\SimpleCache\CacheInterface as Psr16CacheInterface;
use Symfony\Component\Cache\Adapter\NullAdapter;
use Symfony\Contracts\Cache\CacheInterface;
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', NullCache::class, NullAdapter::class, CacheInterface::class), \E_USER_DEPRECATED);
/** /**
* @author Nicolas Grekas <p@tchwork.com> * @deprecated since Symfony 4.3, use NullAdapter and type-hint for CacheInterface instead.
*/ */
class NullCache implements CacheInterface class NullCache implements Psr16CacheInterface
{ {
/** /**
* {@inheritdoc} * {@inheritdoc}
@ -28,6 +32,8 @@ class NullCache implements CacheInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return iterable
*/ */
public function getMultiple($keys, $default = null) public function getMultiple($keys, $default = null)
{ {
@ -38,6 +44,8 @@ class NullCache implements CacheInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function has($key) public function has($key)
{ {
@ -46,6 +54,8 @@ class NullCache implements CacheInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function clear() public function clear()
{ {
@ -54,6 +64,8 @@ class NullCache implements CacheInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function delete($key) public function delete($key)
{ {
@ -62,6 +74,8 @@ class NullCache implements CacheInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function deleteMultiple($keys) public function deleteMultiple($keys)
{ {
@ -70,6 +84,8 @@ class NullCache implements CacheInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function set($key, $value, $ttl = null) public function set($key, $value, $ttl = null)
{ {
@ -78,6 +94,8 @@ class NullCache implements CacheInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function setMultiple($values, $ttl = null) public function setMultiple($values, $ttl = null)
{ {

View file

@ -11,9 +11,17 @@
namespace Symfony\Component\Cache\Simple; namespace Symfony\Component\Cache\Simple;
use Symfony\Component\Cache\Adapter\PdoAdapter;
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
use Symfony\Component\Cache\PruneableInterface; use Symfony\Component\Cache\PruneableInterface;
use Symfony\Component\Cache\Traits\PdoTrait; use Symfony\Component\Cache\Traits\PdoTrait;
use Symfony\Contracts\Cache\CacheInterface;
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', PdoCache::class, PdoAdapter::class, CacheInterface::class), \E_USER_DEPRECATED);
/**
* @deprecated since Symfony 4.3, use PdoAdapter and type-hint for CacheInterface instead.
*/
class PdoCache extends AbstractCache implements PruneableInterface class PdoCache extends AbstractCache implements PruneableInterface
{ {
use PdoTrait; use PdoTrait;
@ -25,6 +33,9 @@ class PdoCache extends AbstractCache implements PruneableInterface
* a Doctrine DBAL Connection or a DSN string that will be used to * a Doctrine DBAL Connection or a DSN string that will be used to
* lazy-connect to the database when the cache is actually used. * lazy-connect to the database when the cache is actually used.
* *
* When a Doctrine DBAL Connection is passed, the cache table is created
* automatically when possible. Otherwise, use the createTable() method.
*
* List of available options: * List of available options:
* * db_table: The name of the table [default: cache_items] * * db_table: The name of the table [default: cache_items]
* * db_id_col: The column where to store the cache id [default: item_id] * * db_id_col: The column where to store the cache id [default: item_id]
@ -35,17 +46,14 @@ class PdoCache extends AbstractCache implements PruneableInterface
* * db_password: The password when lazy-connect [default: ''] * * db_password: The password when lazy-connect [default: '']
* * db_connection_options: An array of driver-specific connection options [default: []] * * db_connection_options: An array of driver-specific connection options [default: []]
* *
* @param \PDO|Connection|string $connOrDsn A \PDO or Connection instance or DSN string or null * @param \PDO|Connection|string $connOrDsn a \PDO or Connection instance or DSN string or null
* @param string $namespace
* @param int $defaultLifetime
* @param array $options An associative array of options
* *
* @throws InvalidArgumentException When first argument is not PDO nor Connection nor string * @throws InvalidArgumentException When first argument is not PDO nor Connection nor string
* @throws InvalidArgumentException When PDO error mode is not PDO::ERRMODE_EXCEPTION * @throws InvalidArgumentException When PDO error mode is not PDO::ERRMODE_EXCEPTION
* @throws InvalidArgumentException When namespace contains invalid characters * @throws InvalidArgumentException When namespace contains invalid characters
*/ */
public function __construct($connOrDsn, $namespace = '', $defaultLifetime = 0, array $options = []) public function __construct($connOrDsn, string $namespace = '', int $defaultLifetime = 0, array $options = [], MarshallerInterface $marshaller = null)
{ {
$this->init($connOrDsn, $namespace, $defaultLifetime, $options); $this->init($connOrDsn, $namespace, $defaultLifetime, $options, $marshaller);
} }
} }

View file

@ -11,53 +11,46 @@
namespace Symfony\Component\Cache\Simple; namespace Symfony\Component\Cache\Simple;
use Psr\SimpleCache\CacheInterface; use Psr\SimpleCache\CacheInterface as Psr16CacheInterface;
use Symfony\Component\Cache\Adapter\PhpArrayAdapter;
use Symfony\Component\Cache\Exception\InvalidArgumentException; use Symfony\Component\Cache\Exception\InvalidArgumentException;
use Symfony\Component\Cache\PruneableInterface; use Symfony\Component\Cache\PruneableInterface;
use Symfony\Component\Cache\ResettableInterface; use Symfony\Component\Cache\ResettableInterface;
use Symfony\Component\Cache\Traits\PhpArrayTrait; use Symfony\Component\Cache\Traits\PhpArrayTrait;
use Symfony\Contracts\Cache\CacheInterface;
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', PhpArrayCache::class, PhpArrayAdapter::class, CacheInterface::class), \E_USER_DEPRECATED);
/** /**
* Caches items at warm up time using a PHP array that is stored in shared memory by OPCache since PHP 7.0. * @deprecated since Symfony 4.3, use PhpArrayAdapter and type-hint for CacheInterface instead.
* Warmed up items are read-only and run-time discovered items are cached using a fallback adapter.
*
* @author Titouan Galopin <galopintitouan@gmail.com>
* @author Nicolas Grekas <p@tchwork.com>
*/ */
class PhpArrayCache implements CacheInterface, PruneableInterface, ResettableInterface class PhpArrayCache implements Psr16CacheInterface, PruneableInterface, ResettableInterface
{ {
use PhpArrayTrait; use PhpArrayTrait;
/** /**
* @param string $file The PHP file were values are cached * @param string $file The PHP file were values are cached
* @param CacheInterface $fallbackPool A pool to fallback on when an item is not hit * @param Psr16CacheInterface $fallbackPool A pool to fallback on when an item is not hit
*/ */
public function __construct($file, CacheInterface $fallbackPool) public function __construct(string $file, Psr16CacheInterface $fallbackPool)
{ {
$this->file = $file; $this->file = $file;
$this->pool = $fallbackPool; $this->pool = $fallbackPool;
$this->zendDetectUnicode = filter_var(ini_get('zend.detect_unicode'), \FILTER_VALIDATE_BOOLEAN);
} }
/** /**
* This adapter should only be used on PHP 7.0+ to take advantage of how PHP * This adapter takes advantage of how PHP stores arrays in its latest versions.
* stores arrays in its latest versions. This factory method decorates the given
* fallback pool with this adapter only if the current PHP version is supported.
* *
* @param string $file The PHP file were values are cached * @param string $file The PHP file were values are cached
* @param CacheInterface $fallbackPool A pool to fallback on when an item is not hit * @param CacheInterface $fallbackPool A pool to fallback on when an item is not hit
* *
* @return CacheInterface * @return Psr16CacheInterface
*/ */
public static function create($file, CacheInterface $fallbackPool) public static function create($file, Psr16CacheInterface $fallbackPool)
{ {
if (\PHP_VERSION_ID >= 70000) {
return new static($file, $fallbackPool); return new static($file, $fallbackPool);
} }
return $fallbackPool;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
@ -69,22 +62,18 @@ class PhpArrayCache implements CacheInterface, PruneableInterface, ResettableInt
if (null === $this->values) { if (null === $this->values) {
$this->initialize(); $this->initialize();
} }
if (!isset($this->values[$key])) { if (!isset($this->keys[$key])) {
return $this->pool->get($key, $default); return $this->pool->get($key, $default);
} }
$value = $this->values[$this->keys[$key]];
$value = $this->values[$key];
if ('N;' === $value) { if ('N;' === $value) {
$value = null; return null;
} elseif (\is_string($value) && isset($value[2]) && ':' === $value[1]) {
try {
$e = null;
$value = unserialize($value);
} catch (\Error $e) {
} catch (\Exception $e) {
} }
if (null !== $e) { if ($value instanceof \Closure) {
try {
return $value();
} catch (\Throwable $e) {
return $default; return $default;
} }
} }
@ -94,6 +83,8 @@ class PhpArrayCache implements CacheInterface, PruneableInterface, ResettableInt
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return iterable
*/ */
public function getMultiple($keys, $default = null) public function getMultiple($keys, $default = null)
{ {
@ -116,6 +107,8 @@ class PhpArrayCache implements CacheInterface, PruneableInterface, ResettableInt
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function has($key) public function has($key)
{ {
@ -126,11 +119,13 @@ class PhpArrayCache implements CacheInterface, PruneableInterface, ResettableInt
$this->initialize(); $this->initialize();
} }
return isset($this->values[$key]) || $this->pool->has($key); return isset($this->keys[$key]) || $this->pool->has($key);
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function delete($key) public function delete($key)
{ {
@ -141,11 +136,13 @@ class PhpArrayCache implements CacheInterface, PruneableInterface, ResettableInt
$this->initialize(); $this->initialize();
} }
return !isset($this->values[$key]) && $this->pool->delete($key); return !isset($this->keys[$key]) && $this->pool->delete($key);
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function deleteMultiple($keys) public function deleteMultiple($keys)
{ {
@ -161,7 +158,7 @@ class PhpArrayCache implements CacheInterface, PruneableInterface, ResettableInt
throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key))); throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key)));
} }
if (isset($this->values[$key])) { if (isset($this->keys[$key])) {
$deleted = false; $deleted = false;
} else { } else {
$fallbackKeys[] = $key; $fallbackKeys[] = $key;
@ -180,6 +177,8 @@ class PhpArrayCache implements CacheInterface, PruneableInterface, ResettableInt
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function set($key, $value, $ttl = null) public function set($key, $value, $ttl = null)
{ {
@ -190,11 +189,13 @@ class PhpArrayCache implements CacheInterface, PruneableInterface, ResettableInt
$this->initialize(); $this->initialize();
} }
return !isset($this->values[$key]) && $this->pool->set($key, $value, $ttl); return !isset($this->keys[$key]) && $this->pool->set($key, $value, $ttl);
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function setMultiple($values, $ttl = null) public function setMultiple($values, $ttl = null)
{ {
@ -210,7 +211,7 @@ class PhpArrayCache implements CacheInterface, PruneableInterface, ResettableInt
throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key))); throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key)));
} }
if (isset($this->values[$key])) { if (isset($this->keys[$key])) {
$saved = false; $saved = false;
} else { } else {
$fallbackValues[$key] = $value; $fallbackValues[$key] = $value;
@ -224,22 +225,20 @@ class PhpArrayCache implements CacheInterface, PruneableInterface, ResettableInt
return $saved; return $saved;
} }
private function generateItems(array $keys, $default) private function generateItems(array $keys, $default): iterable
{ {
$fallbackKeys = []; $fallbackKeys = [];
foreach ($keys as $key) { foreach ($keys as $key) {
if (isset($this->values[$key])) { if (isset($this->keys[$key])) {
$value = $this->values[$key]; $value = $this->values[$this->keys[$key]];
if ('N;' === $value) { if ('N;' === $value) {
yield $key => null; yield $key => null;
} elseif (\is_string($value) && isset($value[2]) && ':' === $value[1]) { } elseif ($value instanceof \Closure) {
try { try {
yield $key => unserialize($value); yield $key => $value();
} catch (\Error $e) { } catch (\Throwable $e) {
yield $key => $default;
} catch (\Exception $e) {
yield $key => $default; yield $key => $default;
} }
} else { } else {
@ -251,9 +250,7 @@ class PhpArrayCache implements CacheInterface, PruneableInterface, ResettableInt
} }
if ($fallbackKeys) { if ($fallbackKeys) {
foreach ($this->pool->getMultiple($fallbackKeys, $default) as $key => $item) { yield from $this->pool->getMultiple($fallbackKeys, $default);
yield $key => $item;
}
} }
} }
} }

View file

@ -11,31 +11,35 @@
namespace Symfony\Component\Cache\Simple; namespace Symfony\Component\Cache\Simple;
use Symfony\Component\Cache\Adapter\PhpFilesAdapter;
use Symfony\Component\Cache\Exception\CacheException; use Symfony\Component\Cache\Exception\CacheException;
use Symfony\Component\Cache\PruneableInterface; use Symfony\Component\Cache\PruneableInterface;
use Symfony\Component\Cache\Traits\PhpFilesTrait; use Symfony\Component\Cache\Traits\PhpFilesTrait;
use Symfony\Contracts\Cache\CacheInterface;
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', PhpFilesCache::class, PhpFilesAdapter::class, CacheInterface::class), \E_USER_DEPRECATED);
/**
* @deprecated since Symfony 4.3, use PhpFilesAdapter and type-hint for CacheInterface instead.
*/
class PhpFilesCache extends AbstractCache implements PruneableInterface class PhpFilesCache extends AbstractCache implements PruneableInterface
{ {
use PhpFilesTrait; use PhpFilesTrait;
/** /**
* @param string $namespace * @param $appendOnly Set to `true` to gain extra performance when the items stored in this pool never expire.
* @param int $defaultLifetime * Doing so is encouraged because it fits perfectly OPcache's memory model.
* @param string|null $directory
* *
* @throws CacheException if OPcache is not enabled * @throws CacheException if OPcache is not enabled
*/ */
public function __construct($namespace = '', $defaultLifetime = 0, $directory = null) public function __construct(string $namespace = '', int $defaultLifetime = 0, string $directory = null, bool $appendOnly = false)
{ {
if (!static::isSupported()) { $this->appendOnly = $appendOnly;
throw new CacheException('OPcache is not enabled.'); self::$startTime = self::$startTime ?? $_SERVER['REQUEST_TIME'] ?? time();
}
parent::__construct('', $defaultLifetime); parent::__construct('', $defaultLifetime);
$this->init($namespace, $directory); $this->init($namespace, $directory);
$this->includeHandler = static function ($type, $msg, $file, $line) {
$e = new \Exception(); throw new \ErrorException($msg, 0, $type, $file, $line);
$this->includeHandler = function () use ($e) { throw $e; }; };
$this->zendDetectUnicode = filter_var(ini_get('zend.detect_unicode'), \FILTER_VALIDATE_BOOLEAN);
} }
} }

View file

@ -11,231 +11,13 @@
namespace Symfony\Component\Cache\Simple; namespace Symfony\Component\Cache\Simple;
use Psr\Cache\CacheException as Psr6CacheException; use Symfony\Component\Cache\Psr16Cache;
use Psr\Cache\CacheItemPoolInterface;
use Psr\SimpleCache\CacheException as SimpleCacheException; @trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" instead.', Psr6Cache::class, Psr16Cache::class), \E_USER_DEPRECATED);
use Psr\SimpleCache\CacheInterface;
use Symfony\Component\Cache\Adapter\AdapterInterface;
use Symfony\Component\Cache\CacheItem;
use Symfony\Component\Cache\Exception\InvalidArgumentException;
use Symfony\Component\Cache\PruneableInterface;
use Symfony\Component\Cache\ResettableInterface;
use Symfony\Component\Cache\Traits\ProxyTrait;
/** /**
* @author Nicolas Grekas <p@tchwork.com> * @deprecated since Symfony 4.3, use Psr16Cache instead.
*/ */
class Psr6Cache implements CacheInterface, PruneableInterface, ResettableInterface class Psr6Cache extends Psr16Cache
{ {
use ProxyTrait;
private $createCacheItem;
private $cacheItemPrototype;
public function __construct(CacheItemPoolInterface $pool)
{
$this->pool = $pool;
if (!$pool instanceof AdapterInterface) {
return;
}
$cacheItemPrototype = &$this->cacheItemPrototype;
$createCacheItem = \Closure::bind(
static function ($key, $value, $allowInt = false) use (&$cacheItemPrototype) {
$item = clone $cacheItemPrototype;
$item->key = $allowInt && \is_int($key) ? (string) $key : CacheItem::validateKey($key);
$item->value = $value;
$item->isHit = false;
return $item;
},
null,
CacheItem::class
);
$this->createCacheItem = function ($key, $value, $allowInt = false) use ($createCacheItem) {
if (null === $this->cacheItemPrototype) {
$this->get($allowInt && \is_int($key) ? (string) $key : $key);
}
$this->createCacheItem = $createCacheItem;
return $createCacheItem($key, $value, $allowInt);
};
}
/**
* {@inheritdoc}
*/
public function get($key, $default = null)
{
try {
$item = $this->pool->getItem($key);
} catch (SimpleCacheException $e) {
throw $e;
} catch (Psr6CacheException $e) {
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
}
if (null === $this->cacheItemPrototype) {
$this->cacheItemPrototype = clone $item;
$this->cacheItemPrototype->set(null);
}
return $item->isHit() ? $item->get() : $default;
}
/**
* {@inheritdoc}
*/
public function set($key, $value, $ttl = null)
{
try {
if (null !== $f = $this->createCacheItem) {
$item = $f($key, $value);
} else {
$item = $this->pool->getItem($key)->set($value);
}
} catch (SimpleCacheException $e) {
throw $e;
} catch (Psr6CacheException $e) {
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
}
if (null !== $ttl) {
$item->expiresAfter($ttl);
}
return $this->pool->save($item);
}
/**
* {@inheritdoc}
*/
public function delete($key)
{
try {
return $this->pool->deleteItem($key);
} catch (SimpleCacheException $e) {
throw $e;
} catch (Psr6CacheException $e) {
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
}
}
/**
* {@inheritdoc}
*/
public function clear()
{
return $this->pool->clear();
}
/**
* {@inheritdoc}
*/
public function getMultiple($keys, $default = null)
{
if ($keys instanceof \Traversable) {
$keys = iterator_to_array($keys, false);
} elseif (!\is_array($keys)) {
throw new InvalidArgumentException(sprintf('Cache keys must be array or Traversable, "%s" given.', \is_object($keys) ? \get_class($keys) : \gettype($keys)));
}
try {
$items = $this->pool->getItems($keys);
} catch (SimpleCacheException $e) {
throw $e;
} catch (Psr6CacheException $e) {
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
}
$values = [];
foreach ($items as $key => $item) {
$values[$key] = $item->isHit() ? $item->get() : $default;
}
return $values;
}
/**
* {@inheritdoc}
*/
public function setMultiple($values, $ttl = null)
{
$valuesIsArray = \is_array($values);
if (!$valuesIsArray && !$values instanceof \Traversable) {
throw new InvalidArgumentException(sprintf('Cache values must be array or Traversable, "%s" given.', \is_object($values) ? \get_class($values) : \gettype($values)));
}
$items = [];
try {
if (null !== $f = $this->createCacheItem) {
$valuesIsArray = false;
foreach ($values as $key => $value) {
$items[$key] = $f($key, $value, true);
}
} elseif ($valuesIsArray) {
$items = [];
foreach ($values as $key => $value) {
$items[] = (string) $key;
}
$items = $this->pool->getItems($items);
} else {
foreach ($values as $key => $value) {
if (\is_int($key)) {
$key = (string) $key;
}
$items[$key] = $this->pool->getItem($key)->set($value);
}
}
} catch (SimpleCacheException $e) {
throw $e;
} catch (Psr6CacheException $e) {
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
}
$ok = true;
foreach ($items as $key => $item) {
if ($valuesIsArray) {
$item->set($values[$key]);
}
if (null !== $ttl) {
$item->expiresAfter($ttl);
}
$ok = $this->pool->saveDeferred($item) && $ok;
}
return $this->pool->commit() && $ok;
}
/**
* {@inheritdoc}
*/
public function deleteMultiple($keys)
{
if ($keys instanceof \Traversable) {
$keys = iterator_to_array($keys, false);
} elseif (!\is_array($keys)) {
throw new InvalidArgumentException(sprintf('Cache keys must be array or Traversable, "%s" given.', \is_object($keys) ? \get_class($keys) : \gettype($keys)));
}
try {
return $this->pool->deleteItems($keys);
} catch (SimpleCacheException $e) {
throw $e;
} catch (Psr6CacheException $e) {
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
}
}
/**
* {@inheritdoc}
*/
public function has($key)
{
try {
return $this->pool->hasItem($key);
} catch (SimpleCacheException $e) {
throw $e;
} catch (Psr6CacheException $e) {
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
}
}
} }

View file

@ -11,19 +11,27 @@
namespace Symfony\Component\Cache\Simple; namespace Symfony\Component\Cache\Simple;
use Symfony\Component\Cache\Adapter\RedisAdapter;
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
use Symfony\Component\Cache\Traits\RedisClusterProxy;
use Symfony\Component\Cache\Traits\RedisProxy;
use Symfony\Component\Cache\Traits\RedisTrait; use Symfony\Component\Cache\Traits\RedisTrait;
use Symfony\Contracts\Cache\CacheInterface;
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', RedisCache::class, RedisAdapter::class, CacheInterface::class), \E_USER_DEPRECATED);
/**
* @deprecated since Symfony 4.3, use RedisAdapter and type-hint for CacheInterface instead.
*/
class RedisCache extends AbstractCache class RedisCache extends AbstractCache
{ {
use RedisTrait; use RedisTrait;
/** /**
* @param \Redis|\RedisArray|\RedisCluster|\Predis\Client $redisClient * @param \Redis|\RedisArray|\RedisCluster|\Predis\ClientInterface|RedisProxy|RedisClusterProxy $redis
* @param string $namespace
* @param int $defaultLifetime
*/ */
public function __construct($redisClient, $namespace = '', $defaultLifetime = 0) public function __construct($redis, string $namespace = '', int $defaultLifetime = 0, MarshallerInterface $marshaller = null)
{ {
$this->init($redisClient, $namespace, $defaultLifetime); $this->init($redis, $namespace, $defaultLifetime, $marshaller);
} }
} }

View file

@ -11,22 +11,25 @@
namespace Symfony\Component\Cache\Simple; namespace Symfony\Component\Cache\Simple;
use Psr\SimpleCache\CacheInterface; use Psr\SimpleCache\CacheInterface as Psr16CacheInterface;
use Symfony\Component\Cache\Adapter\TraceableAdapter;
use Symfony\Component\Cache\PruneableInterface; use Symfony\Component\Cache\PruneableInterface;
use Symfony\Component\Cache\ResettableInterface; use Symfony\Component\Cache\ResettableInterface;
use Symfony\Contracts\Cache\CacheInterface;
use Symfony\Contracts\Service\ResetInterface;
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', TraceableCache::class, TraceableAdapter::class, CacheInterface::class), \E_USER_DEPRECATED);
/** /**
* An adapter that collects data about all cache calls. * @deprecated since Symfony 4.3, use TraceableAdapter and type-hint for CacheInterface instead.
*
* @author Nicolas Grekas <p@tchwork.com>
*/ */
class TraceableCache implements CacheInterface, PruneableInterface, ResettableInterface class TraceableCache implements Psr16CacheInterface, PruneableInterface, ResettableInterface
{ {
private $pool; private $pool;
private $miss; private $miss;
private $calls = []; private $calls = [];
public function __construct(CacheInterface $pool) public function __construct(Psr16CacheInterface $pool)
{ {
$this->pool = $pool; $this->pool = $pool;
$this->miss = new \stdClass(); $this->miss = new \stdClass();
@ -56,6 +59,8 @@ class TraceableCache implements CacheInterface, PruneableInterface, ResettableIn
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function has($key) public function has($key)
{ {
@ -69,6 +74,8 @@ class TraceableCache implements CacheInterface, PruneableInterface, ResettableIn
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function delete($key) public function delete($key)
{ {
@ -82,6 +89,8 @@ class TraceableCache implements CacheInterface, PruneableInterface, ResettableIn
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function set($key, $value, $ttl = null) public function set($key, $value, $ttl = null)
{ {
@ -95,6 +104,8 @@ class TraceableCache implements CacheInterface, PruneableInterface, ResettableIn
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function setMultiple($values, $ttl = null) public function setMultiple($values, $ttl = null)
{ {
@ -122,6 +133,8 @@ class TraceableCache implements CacheInterface, PruneableInterface, ResettableIn
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return iterable
*/ */
public function getMultiple($keys, $default = null) public function getMultiple($keys, $default = null)
{ {
@ -150,6 +163,8 @@ class TraceableCache implements CacheInterface, PruneableInterface, ResettableIn
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function clear() public function clear()
{ {
@ -163,6 +178,8 @@ class TraceableCache implements CacheInterface, PruneableInterface, ResettableIn
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function deleteMultiple($keys) public function deleteMultiple($keys)
{ {
@ -200,7 +217,7 @@ class TraceableCache implements CacheInterface, PruneableInterface, ResettableIn
*/ */
public function reset() public function reset()
{ {
if (!$this->pool instanceof ResettableInterface) { if (!$this->pool instanceof ResetInterface) {
return; return;
} }
$event = $this->start(__FUNCTION__); $event = $this->start(__FUNCTION__);
@ -220,7 +237,7 @@ class TraceableCache implements CacheInterface, PruneableInterface, ResettableIn
} }
} }
private function start($name) private function start(string $name): TraceableCacheEvent
{ {
$this->calls[] = $event = new TraceableCacheEvent(); $this->calls[] = $event = new TraceableCacheEvent();
$event->name = $name; $event->name = $name;

View file

@ -0,0 +1,164 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Cache\Traits;
use Psr\Cache\CacheItemInterface;
use Symfony\Component\Cache\CacheItem;
use Symfony\Component\Cache\Exception\InvalidArgumentException;
/**
* @author Nicolas Grekas <p@tchwork.com>
*
* @internal
*/
trait AbstractAdapterTrait
{
use AbstractTrait;
/**
* @var \Closure needs to be set by class, signature is function(string <key>, mixed <value>, bool <isHit>)
*/
private $createCacheItem;
/**
* @var \Closure needs to be set by class, signature is function(array <deferred>, string <namespace>, array <&expiredIds>)
*/
private $mergeByLifetime;
/**
* {@inheritdoc}
*/
public function getItem($key)
{
$id = $this->getId($key);
if (isset($this->deferred[$key])) {
$this->commit();
}
$f = $this->createCacheItem;
$isHit = false;
$value = null;
try {
foreach ($this->doFetch([$id]) as $value) {
$isHit = true;
}
return $f($key, $value, $isHit);
} catch (\Exception $e) {
CacheItem::log($this->logger, 'Failed to fetch key "{key}": '.$e->getMessage(), ['key' => $key, 'exception' => $e]);
}
return $f($key, null, false);
}
/**
* {@inheritdoc}
*/
public function getItems(array $keys = [])
{
$ids = [];
$commit = false;
foreach ($keys as $key) {
$ids[] = $this->getId($key);
$commit = $commit || isset($this->deferred[$key]);
}
if ($commit) {
$this->commit();
}
try {
$items = $this->doFetch($ids);
} catch (\Exception $e) {
CacheItem::log($this->logger, 'Failed to fetch items: '.$e->getMessage(), ['keys' => $keys, 'exception' => $e]);
$items = [];
}
$ids = array_combine($ids, $keys);
return $this->generateItems($items, $ids);
}
/**
* {@inheritdoc}
*
* @return bool
*/
public function save(CacheItemInterface $item)
{
if (!$item instanceof CacheItem) {
return false;
}
$this->deferred[$item->getKey()] = $item;
return $this->commit();
}
/**
* {@inheritdoc}
*
* @return bool
*/
public function saveDeferred(CacheItemInterface $item)
{
if (!$item instanceof CacheItem) {
return false;
}
$this->deferred[$item->getKey()] = $item;
return true;
}
/**
* @return array
*/
public function __sleep()
{
throw new \BadMethodCallException('Cannot serialize '.__CLASS__);
}
public function __wakeup()
{
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
}
public function __destruct()
{
if ($this->deferred) {
$this->commit();
}
}
private function generateItems(iterable $items, array &$keys): iterable
{
$f = $this->createCacheItem;
try {
foreach ($items as $id => $value) {
if (!isset($keys[$id])) {
throw new InvalidArgumentException(sprintf('Could not match value id "%s" to keys "%s".', $id, implode('", "', $keys)));
}
$key = $keys[$id];
unset($keys[$id]);
yield $key => $f($key, $value, true);
}
} catch (\Exception $e) {
CacheItem::log($this->logger, 'Failed to fetch items: '.$e->getMessage(), ['keys' => array_values($keys), 'exception' => $e]);
}
foreach ($keys as $key) {
yield $key => $f($key, null, false);
}
}
}

View file

@ -27,6 +27,7 @@ trait AbstractTrait
private $namespaceVersion = ''; private $namespaceVersion = '';
private $versioningIsEnabled = false; private $versioningIsEnabled = false;
private $deferred = []; private $deferred = [];
private $ids = [];
/** /**
* @var int|null The maximum length to enforce for identifiers or null when no limit applies * @var int|null The maximum length to enforce for identifiers or null when no limit applies
@ -77,10 +78,12 @@ trait AbstractTrait
* *
* @return array|bool The identifiers that failed to be cached or a boolean stating if caching succeeded or not * @return array|bool The identifiers that failed to be cached or a boolean stating if caching succeeded or not
*/ */
abstract protected function doSave(array $values, $lifetime); abstract protected function doSave(array $values, int $lifetime);
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function hasItem($key) public function hasItem($key)
{ {
@ -93,7 +96,7 @@ trait AbstractTrait
try { try {
return $this->doHave($id); return $this->doHave($id);
} catch (\Exception $e) { } catch (\Exception $e) {
CacheItem::log($this->logger, 'Failed to check if key "{key}" is cached', ['key' => $key, 'exception' => $e]); CacheItem::log($this->logger, 'Failed to check if key "{key}" is cached: '.$e->getMessage(), ['key' => $key, 'exception' => $e]);
return false; return false;
} }
@ -101,26 +104,43 @@ trait AbstractTrait
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @param string $prefix
*
* @return bool
*/ */
public function clear() public function clear(/* string $prefix = '' */)
{ {
$this->deferred = []; $this->deferred = [];
if ($cleared = $this->versioningIsEnabled) { if ($cleared = $this->versioningIsEnabled) {
$namespaceVersion = substr_replace(base64_encode(pack('V', mt_rand())), static::NS_SEPARATOR, 5); if ('' === $namespaceVersionToClear = $this->namespaceVersion) {
foreach ($this->doFetch([static::NS_SEPARATOR.$this->namespace]) as $v) {
$namespaceVersionToClear = $v;
}
}
$namespaceToClear = $this->namespace.$namespaceVersionToClear;
$namespaceVersion = self::formatNamespaceVersion(mt_rand());
try { try {
$cleared = $this->doSave([static::NS_SEPARATOR.$this->namespace => $namespaceVersion], 0); $e = $this->doSave([static::NS_SEPARATOR.$this->namespace => $namespaceVersion], 0);
} catch (\Exception $e) { } catch (\Exception $e) {
}
if (true !== $e && [] !== $e) {
$cleared = false; $cleared = false;
} $message = 'Failed to save the new namespace'.($e instanceof \Exception ? ': '.$e->getMessage() : '.');
if ($cleared = true === $cleared || [] === $cleared) { CacheItem::log($this->logger, $message, ['exception' => $e instanceof \Exception ? $e : null]);
} else {
$this->namespaceVersion = $namespaceVersion; $this->namespaceVersion = $namespaceVersion;
$this->ids = [];
} }
} else {
$prefix = 0 < \func_num_args() ? (string) func_get_arg(0) : '';
$namespaceToClear = $this->namespace.$prefix;
} }
try { try {
return $this->doClear($this->namespace) || $cleared; return $this->doClear($namespaceToClear) || $cleared;
} catch (\Exception $e) { } catch (\Exception $e) {
CacheItem::log($this->logger, 'Failed to clear the cache', ['exception' => $e]); CacheItem::log($this->logger, 'Failed to clear the cache: '.$e->getMessage(), ['exception' => $e]);
return false; return false;
} }
@ -128,6 +148,8 @@ trait AbstractTrait
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function deleteItem($key) public function deleteItem($key)
{ {
@ -136,6 +158,8 @@ trait AbstractTrait
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function deleteItems(array $keys) public function deleteItems(array $keys)
{ {
@ -164,7 +188,8 @@ trait AbstractTrait
} }
} catch (\Exception $e) { } catch (\Exception $e) {
} }
CacheItem::log($this->logger, 'Failed to delete key "{key}"', ['key' => $key, 'exception' => $e]); $message = 'Failed to delete key "{key}"'.($e instanceof \Exception ? ': '.$e->getMessage() : '.');
CacheItem::log($this->logger, $message, ['key' => $key, 'exception' => $e]);
$ok = false; $ok = false;
} }
@ -188,6 +213,7 @@ trait AbstractTrait
$wasEnabled = $this->versioningIsEnabled; $wasEnabled = $this->versioningIsEnabled;
$this->versioningIsEnabled = (bool) $enable; $this->versioningIsEnabled = (bool) $enable;
$this->namespaceVersion = ''; $this->namespaceVersion = '';
$this->ids = [];
return $wasEnabled; return $wasEnabled;
} }
@ -201,6 +227,7 @@ trait AbstractTrait
$this->commit(); $this->commit();
} }
$this->namespaceVersion = ''; $this->namespaceVersion = '';
$this->ids = [];
} }
/** /**
@ -211,9 +238,13 @@ trait AbstractTrait
* @return mixed * @return mixed
* *
* @throws \Exception * @throws \Exception
*
* @deprecated since Symfony 4.2, use DefaultMarshaller instead.
*/ */
protected static function unserialize($value) protected static function unserialize($value)
{ {
@trigger_error(sprintf('The "%s::unserialize()" method is deprecated since Symfony 4.2, use DefaultMarshaller instead.', __CLASS__), \E_USER_DEPRECATED);
if ('b:0;' === $value) { if ('b:0;' === $value) {
return false; return false;
} }
@ -230,29 +261,45 @@ trait AbstractTrait
} }
} }
private function getId($key) private function getId($key): string
{ {
CacheItem::validateKey($key);
if ($this->versioningIsEnabled && '' === $this->namespaceVersion) { if ($this->versioningIsEnabled && '' === $this->namespaceVersion) {
$this->ids = [];
$this->namespaceVersion = '1'.static::NS_SEPARATOR; $this->namespaceVersion = '1'.static::NS_SEPARATOR;
try { try {
foreach ($this->doFetch([static::NS_SEPARATOR.$this->namespace]) as $v) { foreach ($this->doFetch([static::NS_SEPARATOR.$this->namespace]) as $v) {
$this->namespaceVersion = $v; $this->namespaceVersion = $v;
} }
$e = true;
if ('1'.static::NS_SEPARATOR === $this->namespaceVersion) { if ('1'.static::NS_SEPARATOR === $this->namespaceVersion) {
$this->namespaceVersion = substr_replace(base64_encode(pack('V', time())), static::NS_SEPARATOR, 5); $this->namespaceVersion = self::formatNamespaceVersion(time());
$this->doSave([static::NS_SEPARATOR.$this->namespace => $this->namespaceVersion], 0); $e = $this->doSave([static::NS_SEPARATOR.$this->namespace => $this->namespaceVersion], 0);
} }
} catch (\Exception $e) { } catch (\Exception $e) {
} }
if (true !== $e && [] !== $e) {
$message = 'Failed to save the new namespace'.($e instanceof \Exception ? ': '.$e->getMessage() : '.');
CacheItem::log($this->logger, $message, ['exception' => $e instanceof \Exception ? $e : null]);
}
}
if (\is_string($key) && isset($this->ids[$key])) {
return $this->namespace.$this->namespaceVersion.$this->ids[$key];
}
CacheItem::validateKey($key);
$this->ids[$key] = $key;
if (\count($this->ids) > 1000) {
$this->ids = \array_slice($this->ids, 500, null, true); // stop memory leak if there are many keys
} }
if (null === $this->maxIdLength) { if (null === $this->maxIdLength) {
return $this->namespace.$this->namespaceVersion.$key; return $this->namespace.$this->namespaceVersion.$key;
} }
if (\strlen($id = $this->namespace.$this->namespaceVersion.$key) > $this->maxIdLength) { if (\strlen($id = $this->namespace.$this->namespaceVersion.$key) > $this->maxIdLength) {
$id = $this->namespace.$this->namespaceVersion.substr_replace(base64_encode(hash('sha256', $key, true)), static::NS_SEPARATOR, -(\strlen($this->namespaceVersion) + 22)); // Use MD5 to favor speed over security, which is not an issue here
$this->ids[$key] = $id = substr_replace(base64_encode(hash('md5', $key, true)), static::NS_SEPARATOR, -(\strlen($this->namespaceVersion) + 2));
$id = $this->namespace.$this->namespaceVersion.$id;
} }
return $id; return $id;
@ -265,4 +312,9 @@ trait AbstractTrait
{ {
throw new \DomainException('Class not found: '.$class); throw new \DomainException('Class not found: '.$class);
} }
private static function formatNamespaceVersion(int $value): string
{
return strtr(substr_replace(base64_encode(pack('V', $value)), static::NS_SEPARATOR, 5), '/', '_');
}
} }

View file

@ -23,10 +23,10 @@ trait ApcuTrait
{ {
public static function isSupported() public static function isSupported()
{ {
return \function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), \FILTER_VALIDATE_BOOLEAN); return \function_exists('apcu_fetch') && filter_var(\ini_get('apc.enabled'), \FILTER_VALIDATE_BOOLEAN);
} }
private function init($namespace, $defaultLifetime, $version) private function init(string $namespace, int $defaultLifetime, ?string $version)
{ {
if (!static::isSupported()) { if (!static::isSupported()) {
throw new CacheException('APCu is not enabled.'); throw new CacheException('APCu is not enabled.');
@ -51,14 +51,27 @@ trait ApcuTrait
*/ */
protected function doFetch(array $ids) protected function doFetch(array $ids)
{ {
$unserializeCallbackHandler = ini_set('unserialize_callback_func', __CLASS__.'::handleUnserializeCallback');
try { try {
foreach (apcu_fetch($ids, $ok) ?: [] as $k => $v) { $values = [];
$ids = array_flip($ids);
foreach (apcu_fetch(array_keys($ids), $ok) ?: [] as $k => $v) {
if (!isset($ids[$k])) {
// work around https://github.com/krakjoe/apcu/issues/247
$k = key($ids);
}
unset($ids[$k]);
if (null !== $v || $ok) { if (null !== $v || $ok) {
yield $k => $v; $values[$k] = $v;
} }
} }
return $values;
} catch (\Error $e) { } catch (\Error $e) {
throw new \ErrorException($e->getMessage(), $e->getCode(), \E_ERROR, $e->getFile(), $e->getLine()); throw new \ErrorException($e->getMessage(), $e->getCode(), \E_ERROR, $e->getFile(), $e->getLine());
} finally {
ini_set('unserialize_callback_func', $unserializeCallbackHandler);
} }
} }
@ -75,8 +88,8 @@ trait ApcuTrait
*/ */
protected function doClear($namespace) protected function doClear($namespace)
{ {
return isset($namespace[0]) && class_exists('APCuIterator', false) && ('cli' !== \PHP_SAPI || filter_var(ini_get('apc.enable_cli'), \FILTER_VALIDATE_BOOLEAN)) return isset($namespace[0]) && class_exists(\APCUIterator::class, false) && ('cli' !== \PHP_SAPI || filter_var(\ini_get('apc.enable_cli'), \FILTER_VALIDATE_BOOLEAN))
? apcu_delete(new \APCuIterator(sprintf('/^%s/', preg_quote($namespace, '/')), \APC_ITER_KEY)) ? apcu_delete(new \APCUIterator(sprintf('/^%s/', preg_quote($namespace, '/')), \APC_ITER_KEY))
: apcu_clear_cache(); : apcu_clear_cache();
} }
@ -95,7 +108,7 @@ trait ApcuTrait
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
protected function doSave(array $values, $lifetime) protected function doSave(array $values, int $lifetime)
{ {
try { try {
if (false === $failures = apcu_store($values, null, $lifetime)) { if (false === $failures = apcu_store($values, null, $lifetime)) {
@ -103,15 +116,13 @@ trait ApcuTrait
} }
return array_keys($failures); return array_keys($failures);
} catch (\Error $e) { } catch (\Throwable $e) {
} catch (\Exception $e) {
}
if (1 === \count($values)) { if (1 === \count($values)) {
// Workaround https://github.com/krakjoe/apcu/issues/170 // Workaround https://github.com/krakjoe/apcu/issues/170
apcu_delete(key($values)); apcu_delete(array_key_first($values));
} }
throw $e; throw $e;
} }
}
} }

View file

@ -34,36 +34,72 @@ trait ArrayTrait
*/ */
public function getValues() public function getValues()
{ {
if (!$this->storeSerialized) {
return $this->values; return $this->values;
} }
/** $values = $this->values;
* {@inheritdoc} foreach ($values as $k => $v) {
*/ if (null === $v || 'N;' === $v) {
public function hasItem($key) continue;
{ }
CacheItem::validateKey($key); if (!\is_string($v) || !isset($v[2]) || ':' !== $v[1]) {
$values[$k] = serialize($v);
}
}
return isset($this->expiries[$key]) && ($this->expiries[$key] > time() || !$this->deleteItem($key)); return $values;
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function clear() public function hasItem($key)
{ {
if (\is_string($key) && isset($this->expiries[$key]) && $this->expiries[$key] > microtime(true)) {
return true;
}
CacheItem::validateKey($key);
return isset($this->expiries[$key]) && !$this->deleteItem($key);
}
/**
* {@inheritdoc}
*
* @param string $prefix
*
* @return bool
*/
public function clear(/* string $prefix = '' */)
{
$prefix = 0 < \func_num_args() ? (string) func_get_arg(0) : '';
if ('' !== $prefix) {
foreach ($this->values as $key => $value) {
if (str_starts_with($key, $prefix)) {
unset($this->values[$key], $this->expiries[$key]);
}
}
} else {
$this->values = $this->expiries = []; $this->values = $this->expiries = [];
}
return true; return true;
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @return bool
*/ */
public function deleteItem($key) public function deleteItem($key)
{ {
if (!\is_string($key) || !isset($this->expiries[$key])) {
CacheItem::validateKey($key); CacheItem::validateKey($key);
}
unset($this->values[$key], $this->expiries[$key]); unset($this->values[$key], $this->expiries[$key]);
return true; return true;
@ -77,24 +113,13 @@ trait ArrayTrait
$this->clear(); $this->clear();
} }
private function generateItems(array $keys, $now, $f) private function generateItems(array $keys, float $now, callable $f): iterable
{ {
foreach ($keys as $i => $key) { foreach ($keys as $i => $key) {
try {
if (!$isHit = isset($this->expiries[$key]) && ($this->expiries[$key] > $now || !$this->deleteItem($key))) { if (!$isHit = isset($this->expiries[$key]) && ($this->expiries[$key] > $now || !$this->deleteItem($key))) {
$this->values[$key] = $value = null; $this->values[$key] = $value = null;
} elseif (!$this->storeSerialized) { } else {
$value = $this->values[$key]; $value = $this->storeSerialized ? $this->unfreeze($key, $isHit) : $this->values[$key];
} elseif ('b:0;' === $value = $this->values[$key]) {
$value = false;
} elseif (false === $value = unserialize($value)) {
$this->values[$key] = $value = null;
$isHit = false;
}
} catch (\Exception $e) {
CacheItem::log($this->logger, 'Failed to unserialize key "{key}"', ['key' => $key, 'exception' => $e]);
$this->values[$key] = $value = null;
$isHit = false;
} }
unset($keys[$i]); unset($keys[$i]);
@ -105,4 +130,55 @@ trait ArrayTrait
yield $key => $f($key, null, false); yield $key => $f($key, null, false);
} }
} }
private function freeze($value, $key)
{
if (null === $value) {
return 'N;';
}
if (\is_string($value)) {
// Serialize strings if they could be confused with serialized objects or arrays
if ('N;' === $value || (isset($value[2]) && ':' === $value[1])) {
return serialize($value);
}
} elseif (!\is_scalar($value)) {
try {
$serialized = serialize($value);
} catch (\Exception $e) {
unset($this->values[$key]);
$type = \is_object($value) ? \get_class($value) : \gettype($value);
$message = sprintf('Failed to save key "{key}" of type %s: ', $type).$e->getMessage();
CacheItem::log($this->logger, $message, ['key' => $key, 'exception' => $e]);
return null;
}
// Keep value serialized if it contains any objects or any internal references
if ('C' === $serialized[0] || 'O' === $serialized[0] || preg_match('/;[OCRr]:[1-9]/', $serialized)) {
return $serialized;
}
}
return $value;
}
private function unfreeze(string $key, bool &$isHit)
{
if ('N;' === $value = $this->values[$key]) {
return null;
}
if (\is_string($value) && isset($value[2]) && ':' === $value[1]) {
try {
$value = unserialize($value);
} catch (\Exception $e) {
CacheItem::log($this->logger, 'Failed to unserialize key "{key}": '.$e->getMessage(), ['key' => $key, 'exception' => $e]);
$value = false;
}
if (false === $value) {
$this->values[$key] = $value = null;
$isHit = false;
}
}
return $value;
}
} }

View file

@ -0,0 +1,109 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Cache\Traits;
use Psr\Log\LoggerInterface;
use Symfony\Component\Cache\Adapter\AdapterInterface;
use Symfony\Component\Cache\CacheItem;
use Symfony\Component\Cache\Exception\InvalidArgumentException;
use Symfony\Component\Cache\LockRegistry;
use Symfony\Contracts\Cache\CacheInterface;
use Symfony\Contracts\Cache\CacheTrait;
use Symfony\Contracts\Cache\ItemInterface;
/**
* @author Nicolas Grekas <p@tchwork.com>
*
* @internal
*/
trait ContractsTrait
{
use CacheTrait {
doGet as private contractsGet;
}
private $callbackWrapper;
private $computing = [];
/**
* Wraps the callback passed to ->get() in a callable.
*
* @return callable the previous callback wrapper
*/
public function setCallbackWrapper(?callable $callbackWrapper): callable
{
if (!isset($this->callbackWrapper)) {
$this->callbackWrapper = \Closure::fromCallable([LockRegistry::class, 'compute']);
if (\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) {
$this->setCallbackWrapper(null);
}
}
$previousWrapper = $this->callbackWrapper;
$this->callbackWrapper = $callbackWrapper ?? static function (callable $callback, ItemInterface $item, bool &$save, CacheInterface $pool, \Closure $setMetadata, ?LoggerInterface $logger) {
return $callback($item, $save);
};
return $previousWrapper;
}
private function doGet(AdapterInterface $pool, string $key, callable $callback, ?float $beta, array &$metadata = null)
{
if (0 > $beta = $beta ?? 1.0) {
throw new InvalidArgumentException(sprintf('Argument "$beta" provided to "%s::get()" must be a positive number, %f given.', static::class, $beta));
}
static $setMetadata;
$setMetadata = $setMetadata ?? \Closure::bind(
static function (CacheItem $item, float $startTime, ?array &$metadata) {
if ($item->expiry > $endTime = microtime(true)) {
$item->newMetadata[CacheItem::METADATA_EXPIRY] = $metadata[CacheItem::METADATA_EXPIRY] = $item->expiry;
$item->newMetadata[CacheItem::METADATA_CTIME] = $metadata[CacheItem::METADATA_CTIME] = (int) ceil(1000 * ($endTime - $startTime));
} else {
unset($metadata[CacheItem::METADATA_EXPIRY], $metadata[CacheItem::METADATA_CTIME]);
}
},
null,
CacheItem::class
);
return $this->contractsGet($pool, $key, function (CacheItem $item, bool &$save) use ($pool, $callback, $setMetadata, &$metadata, $key) {
// don't wrap nor save recursive calls
if (isset($this->computing[$key])) {
$value = $callback($item, $save);
$save = false;
return $value;
}
$this->computing[$key] = $key;
$startTime = microtime(true);
if (!isset($this->callbackWrapper)) {
$this->setCallbackWrapper($this->setCallbackWrapper(null));
}
try {
$value = ($this->callbackWrapper)($callback, $item, $save, $pool, function (CacheItem $item) use ($setMetadata, $startTime, &$metadata) {
$setMetadata($item, $startTime, $metadata);
}, $this->logger ?? null);
$setMetadata($item, $startTime, $metadata);
return $value;
} finally {
unset($this->computing[$key]);
}
}, $beta, $metadata, $this->logger ?? null);
}
}

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