"composer/installers": "^1.2",
"drupal-composer/drupal-scaffold": "^2.2",
"cweagans/composer-patches": "~1.0",
- "drupal/core": "^8.4.0",
+ "drupal/core": "^8.4.6",
"drupal/console": "^1.0",
"drupal/token": "^1.0@RC",
"drupal/ctools": "^3.0@alpha",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
- "content-hash": "01a5df84551bef7c3f76ff65995f296f",
+ "content-hash": "08077751106b61fc89fbb36241674293",
"packages": [
{
"name": "alchemy/zippy",
},
{
"name": "doctrine/annotations",
- "version": "v1.4.0",
+ "version": "v1.6.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/annotations.git",
- "reference": "54cacc9b81758b14e3ce750f205a393d52339e97"
+ "reference": "c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/doctrine/annotations/zipball/54cacc9b81758b14e3ce750f205a393d52339e97",
- "reference": "54cacc9b81758b14e3ce750f205a393d52339e97",
+ "url": "https://api.github.com/repos/doctrine/annotations/zipball/c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5",
+ "reference": "c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5",
"shasum": ""
},
"require": {
"doctrine/lexer": "1.*",
- "php": "^5.6 || ^7.0"
+ "php": "^7.1"
},
"require-dev": {
"doctrine/cache": "1.*",
- "phpunit/phpunit": "^5.7"
+ "phpunit/phpunit": "^6.4"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.4.x-dev"
+ "dev-master": "1.6.x-dev"
}
},
"autoload": {
"docblock",
"parser"
],
- "time": "2017-02-24T16:22:25+00:00"
+ "time": "2017-12-06T07:11:42+00:00"
},
{
"name": "doctrine/cache",
- "version": "v1.6.2",
+ "version": "v1.7.1",
"source": {
"type": "git",
"url": "https://github.com/doctrine/cache.git",
- "reference": "eb152c5100571c7a45470ff2a35095ab3f3b900b"
+ "reference": "b3217d58609e9c8e661cd41357a54d926c4a2a1a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/doctrine/cache/zipball/eb152c5100571c7a45470ff2a35095ab3f3b900b",
- "reference": "eb152c5100571c7a45470ff2a35095ab3f3b900b",
+ "url": "https://api.github.com/repos/doctrine/cache/zipball/b3217d58609e9c8e661cd41357a54d926c4a2a1a",
+ "reference": "b3217d58609e9c8e661cd41357a54d926c4a2a1a",
"shasum": ""
},
"require": {
- "php": "~5.5|~7.0"
+ "php": "~7.1"
},
"conflict": {
"doctrine/common": ">2.2,<2.4"
},
"require-dev": {
- "phpunit/phpunit": "~4.8|~5.0",
- "predis/predis": "~1.0",
- "satooshi/php-coveralls": "~0.6"
+ "alcaeus/mongo-php-adapter": "^1.1",
+ "mongodb/mongodb": "^1.1",
+ "phpunit/phpunit": "^5.7",
+ "predis/predis": "~1.0"
+ },
+ "suggest": {
+ "alcaeus/mongo-php-adapter": "Required to use legacy MongoDB driver"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.6.x-dev"
+ "dev-master": "1.7.x-dev"
}
},
"autoload": {
"cache",
"caching"
],
- "time": "2017-07-22T12:49:21+00:00"
+ "time": "2017-08-25T07:02:50+00:00"
},
{
"name": "doctrine/collections",
- "version": "v1.4.0",
+ "version": "v1.5.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/collections.git",
- "reference": "1a4fb7e902202c33cce8c55989b945612943c2ba"
+ "reference": "a01ee38fcd999f34d9bfbcee59dbda5105449cbf"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/doctrine/collections/zipball/1a4fb7e902202c33cce8c55989b945612943c2ba",
- "reference": "1a4fb7e902202c33cce8c55989b945612943c2ba",
+ "url": "https://api.github.com/repos/doctrine/collections/zipball/a01ee38fcd999f34d9bfbcee59dbda5105449cbf",
+ "reference": "a01ee38fcd999f34d9bfbcee59dbda5105449cbf",
"shasum": ""
},
"require": {
- "php": "^5.6 || ^7.0"
+ "php": "^7.1"
},
"require-dev": {
"doctrine/coding-standard": "~0.1@dev",
"collections",
"iterator"
],
- "time": "2017-01-03T10:49:41+00:00"
+ "time": "2017-07-22T10:37:32+00:00"
},
{
"name": "doctrine/common",
- "version": "v2.7.3",
+ "version": "v2.8.1",
"source": {
"type": "git",
"url": "https://github.com/doctrine/common.git",
- "reference": "4acb8f89626baafede6ee5475bc5844096eba8a9"
+ "reference": "f68c297ce6455e8fd794aa8ffaf9fa458f6ade66"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/doctrine/common/zipball/4acb8f89626baafede6ee5475bc5844096eba8a9",
- "reference": "4acb8f89626baafede6ee5475bc5844096eba8a9",
+ "url": "https://api.github.com/repos/doctrine/common/zipball/f68c297ce6455e8fd794aa8ffaf9fa458f6ade66",
+ "reference": "f68c297ce6455e8fd794aa8ffaf9fa458f6ade66",
"shasum": ""
},
"require": {
"doctrine/collections": "1.*",
"doctrine/inflector": "1.*",
"doctrine/lexer": "1.*",
- "php": "~5.6|~7.0"
+ "php": "~7.1"
},
"require-dev": {
- "phpunit/phpunit": "^5.4.6"
+ "phpunit/phpunit": "^5.7"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.7.x-dev"
+ "dev-master": "2.8.x-dev"
}
},
"autoload": {
"persistence",
"spl"
],
- "time": "2017-07-22T08:35:12+00:00"
+ "time": "2017-08-31T08:43:38+00:00"
},
{
"name": "doctrine/inflector",
- "version": "v1.2.0",
+ "version": "v1.3.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/inflector.git",
- "reference": "e11d84c6e018beedd929cff5220969a3c6d1d462"
+ "reference": "5527a48b7313d15261292c149e55e26eae771b0a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/doctrine/inflector/zipball/e11d84c6e018beedd929cff5220969a3c6d1d462",
- "reference": "e11d84c6e018beedd929cff5220969a3c6d1d462",
+ "url": "https://api.github.com/repos/doctrine/inflector/zipball/5527a48b7313d15261292c149e55e26eae771b0a",
+ "reference": "5527a48b7313d15261292c149e55e26eae771b0a",
"shasum": ""
},
"require": {
- "php": "^7.0"
+ "php": "^7.1"
},
"require-dev": {
"phpunit/phpunit": "^6.2"
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.2.x-dev"
+ "dev-master": "1.3.x-dev"
}
},
"autoload": {
"singularize",
"string"
],
- "time": "2017-07-22T12:18:28+00:00"
+ "time": "2018-01-09T20:05:19+00:00"
},
{
"name": "doctrine/instantiator",
},
{
"name": "drupal/core",
- "version": "8.4.5",
+ "version": "8.4.6",
"source": {
"type": "git",
"url": "https://github.com/drupal/core.git",
- "reference": "44a857df6f7ffd063cffed9a41767cdc50dd7474"
+ "reference": "f439a43c7da1df8d16067f797c0bc3498eb4c699"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/drupal/core/zipball/44a857df6f7ffd063cffed9a41767cdc50dd7474",
- "reference": "44a857df6f7ffd063cffed9a41767cdc50dd7474",
+ "url": "https://api.github.com/repos/drupal/core/zipball/f439a43c7da1df8d16067f797c0bc3498eb4c699",
+ "reference": "f439a43c7da1df8d16067f797c0bc3498eb4c699",
"shasum": ""
},
"require": {
"GPL-2.0+"
],
"description": "Drupal is an open source content management platform powering millions of websites and applications.",
- "time": "2018-02-20T21:35:13+00:00"
+ "time": "2018-03-27T10:03:10+00:00"
},
{
"name": "drupal/crop",
},
{
"name": "guzzlehttp/guzzle",
- "version": "6.3.0",
+ "version": "6.3.2",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
- "reference": "f4db5a78a5ea468d4831de7f0bf9d9415e348699"
+ "reference": "68d0ea14d5a3f42a20e87632a5f84931e2709c90"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/guzzle/guzzle/zipball/f4db5a78a5ea468d4831de7f0bf9d9415e348699",
- "reference": "f4db5a78a5ea468d4831de7f0bf9d9415e348699",
+ "url": "https://api.github.com/repos/guzzle/guzzle/zipball/68d0ea14d5a3f42a20e87632a5f84931e2709c90",
+ "reference": "68d0ea14d5a3f42a20e87632a5f84931e2709c90",
"shasum": ""
},
"require": {
},
"require-dev": {
"ext-curl": "*",
- "phpunit/phpunit": "^4.0 || ^5.0",
+ "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4",
"psr/log": "^1.0"
},
"suggest": {
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "6.2-dev"
+ "dev-master": "6.3-dev"
}
},
"autoload": {
"rest",
"web service"
],
- "time": "2017-06-22T18:50:49+00:00"
+ "time": "2018-03-26T16:33:04+00:00"
},
{
"name": "guzzlehttp/promises",
},
{
"name": "symfony/debug",
- "version": "v3.4.5",
+ "version": "v3.4.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/debug.git",
},
{
"name": "twig/twig",
- "version": "v1.35.1",
+ "version": "v1.35.3",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
- "reference": "ffda54d85cc3aec3ba499c8c0eda9b2176edfe83"
+ "reference": "b48680b6eb7d16b5025b9bfc4108d86f6b8af86f"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/twigphp/Twig/zipball/ffda54d85cc3aec3ba499c8c0eda9b2176edfe83",
- "reference": "ffda54d85cc3aec3ba499c8c0eda9b2176edfe83",
+ "url": "https://api.github.com/repos/twigphp/Twig/zipball/b48680b6eb7d16b5025b9bfc4108d86f6b8af86f",
+ "reference": "b48680b6eb7d16b5025b9bfc4108d86f6b8af86f",
"shasum": ""
},
"require": {
},
"require-dev": {
"psr/container": "^1.0",
- "symfony/debug": "~2.7",
- "symfony/phpunit-bridge": "~3.3@dev"
+ "symfony/debug": "^2.7",
+ "symfony/phpunit-bridge": "^3.3"
},
"type": "library",
"extra": {
"keywords": [
"templating"
],
- "time": "2018-03-02T18:06:27+00:00"
+ "time": "2018-03-20T04:25:58+00:00"
},
{
"name": "webflo/drupal-finder",
},
{
"name": "doctrine/annotations",
- "version": "v1.4.0",
- "version_normalized": "1.4.0.0",
+ "version": "v1.6.0",
+ "version_normalized": "1.6.0.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/annotations.git",
- "reference": "54cacc9b81758b14e3ce750f205a393d52339e97"
+ "reference": "c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/doctrine/annotations/zipball/54cacc9b81758b14e3ce750f205a393d52339e97",
- "reference": "54cacc9b81758b14e3ce750f205a393d52339e97",
+ "url": "https://api.github.com/repos/doctrine/annotations/zipball/c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5",
+ "reference": "c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5",
"shasum": ""
},
"require": {
"doctrine/lexer": "1.*",
- "php": "^5.6 || ^7.0"
+ "php": "^7.1"
},
"require-dev": {
"doctrine/cache": "1.*",
- "phpunit/phpunit": "^5.7"
+ "phpunit/phpunit": "^6.4"
},
- "time": "2017-02-24T16:22:25+00:00",
+ "time": "2017-12-06T07:11:42+00:00",
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.4.x-dev"
+ "dev-master": "1.6.x-dev"
}
},
"installation-source": "dist",
},
{
"name": "doctrine/cache",
- "version": "v1.6.2",
- "version_normalized": "1.6.2.0",
+ "version": "v1.7.1",
+ "version_normalized": "1.7.1.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/cache.git",
- "reference": "eb152c5100571c7a45470ff2a35095ab3f3b900b"
+ "reference": "b3217d58609e9c8e661cd41357a54d926c4a2a1a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/doctrine/cache/zipball/eb152c5100571c7a45470ff2a35095ab3f3b900b",
- "reference": "eb152c5100571c7a45470ff2a35095ab3f3b900b",
+ "url": "https://api.github.com/repos/doctrine/cache/zipball/b3217d58609e9c8e661cd41357a54d926c4a2a1a",
+ "reference": "b3217d58609e9c8e661cd41357a54d926c4a2a1a",
"shasum": ""
},
"require": {
- "php": "~5.5|~7.0"
+ "php": "~7.1"
},
"conflict": {
"doctrine/common": ">2.2,<2.4"
},
"require-dev": {
- "phpunit/phpunit": "~4.8|~5.0",
- "predis/predis": "~1.0",
- "satooshi/php-coveralls": "~0.6"
+ "alcaeus/mongo-php-adapter": "^1.1",
+ "mongodb/mongodb": "^1.1",
+ "phpunit/phpunit": "^5.7",
+ "predis/predis": "~1.0"
+ },
+ "suggest": {
+ "alcaeus/mongo-php-adapter": "Required to use legacy MongoDB driver"
},
- "time": "2017-07-22T12:49:21+00:00",
+ "time": "2017-08-25T07:02:50+00:00",
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.6.x-dev"
+ "dev-master": "1.7.x-dev"
}
},
"installation-source": "dist",
},
{
"name": "doctrine/collections",
- "version": "v1.4.0",
- "version_normalized": "1.4.0.0",
+ "version": "v1.5.0",
+ "version_normalized": "1.5.0.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/collections.git",
- "reference": "1a4fb7e902202c33cce8c55989b945612943c2ba"
+ "reference": "a01ee38fcd999f34d9bfbcee59dbda5105449cbf"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/doctrine/collections/zipball/1a4fb7e902202c33cce8c55989b945612943c2ba",
- "reference": "1a4fb7e902202c33cce8c55989b945612943c2ba",
+ "url": "https://api.github.com/repos/doctrine/collections/zipball/a01ee38fcd999f34d9bfbcee59dbda5105449cbf",
+ "reference": "a01ee38fcd999f34d9bfbcee59dbda5105449cbf",
"shasum": ""
},
"require": {
- "php": "^5.6 || ^7.0"
+ "php": "^7.1"
},
"require-dev": {
"doctrine/coding-standard": "~0.1@dev",
"phpunit/phpunit": "^5.7"
},
- "time": "2017-01-03T10:49:41+00:00",
+ "time": "2017-07-22T10:37:32+00:00",
"type": "library",
"extra": {
"branch-alias": {
},
{
"name": "doctrine/common",
- "version": "v2.7.3",
- "version_normalized": "2.7.3.0",
+ "version": "v2.8.1",
+ "version_normalized": "2.8.1.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/common.git",
- "reference": "4acb8f89626baafede6ee5475bc5844096eba8a9"
+ "reference": "f68c297ce6455e8fd794aa8ffaf9fa458f6ade66"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/doctrine/common/zipball/4acb8f89626baafede6ee5475bc5844096eba8a9",
- "reference": "4acb8f89626baafede6ee5475bc5844096eba8a9",
+ "url": "https://api.github.com/repos/doctrine/common/zipball/f68c297ce6455e8fd794aa8ffaf9fa458f6ade66",
+ "reference": "f68c297ce6455e8fd794aa8ffaf9fa458f6ade66",
"shasum": ""
},
"require": {
"doctrine/collections": "1.*",
"doctrine/inflector": "1.*",
"doctrine/lexer": "1.*",
- "php": "~5.6|~7.0"
+ "php": "~7.1"
},
"require-dev": {
- "phpunit/phpunit": "^5.4.6"
+ "phpunit/phpunit": "^5.7"
},
- "time": "2017-07-22T08:35:12+00:00",
+ "time": "2017-08-31T08:43:38+00:00",
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.7.x-dev"
+ "dev-master": "2.8.x-dev"
}
},
"installation-source": "dist",
},
{
"name": "doctrine/inflector",
- "version": "v1.2.0",
- "version_normalized": "1.2.0.0",
+ "version": "v1.3.0",
+ "version_normalized": "1.3.0.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/inflector.git",
- "reference": "e11d84c6e018beedd929cff5220969a3c6d1d462"
+ "reference": "5527a48b7313d15261292c149e55e26eae771b0a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/doctrine/inflector/zipball/e11d84c6e018beedd929cff5220969a3c6d1d462",
- "reference": "e11d84c6e018beedd929cff5220969a3c6d1d462",
+ "url": "https://api.github.com/repos/doctrine/inflector/zipball/5527a48b7313d15261292c149e55e26eae771b0a",
+ "reference": "5527a48b7313d15261292c149e55e26eae771b0a",
"shasum": ""
},
"require": {
- "php": "^7.0"
+ "php": "^7.1"
},
"require-dev": {
"phpunit/phpunit": "^6.2"
},
- "time": "2017-07-22T12:18:28+00:00",
+ "time": "2018-01-09T20:05:19+00:00",
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.2.x-dev"
+ "dev-master": "1.3.x-dev"
}
},
"installation-source": "dist",
},
{
"name": "drupal/core",
- "version": "8.4.5",
- "version_normalized": "8.4.5.0",
+ "version": "8.4.6",
+ "version_normalized": "8.4.6.0",
"source": {
"type": "git",
"url": "https://github.com/drupal/core.git",
- "reference": "44a857df6f7ffd063cffed9a41767cdc50dd7474"
+ "reference": "f439a43c7da1df8d16067f797c0bc3498eb4c699"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/drupal/core/zipball/44a857df6f7ffd063cffed9a41767cdc50dd7474",
- "reference": "44a857df6f7ffd063cffed9a41767cdc50dd7474",
+ "url": "https://api.github.com/repos/drupal/core/zipball/f439a43c7da1df8d16067f797c0bc3498eb4c699",
+ "reference": "f439a43c7da1df8d16067f797c0bc3498eb4c699",
"shasum": ""
},
"require": {
"symfony/css-selector": "~3.2.8",
"symfony/phpunit-bridge": "~3.2.8"
},
- "time": "2018-02-20T21:35:13+00:00",
+ "time": "2018-03-27T10:03:10+00:00",
"type": "drupal-core",
"installation-source": "dist",
"autoload": {
},
{
"name": "guzzlehttp/guzzle",
- "version": "6.3.0",
- "version_normalized": "6.3.0.0",
+ "version": "6.3.2",
+ "version_normalized": "6.3.2.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
- "reference": "f4db5a78a5ea468d4831de7f0bf9d9415e348699"
+ "reference": "68d0ea14d5a3f42a20e87632a5f84931e2709c90"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/guzzle/guzzle/zipball/f4db5a78a5ea468d4831de7f0bf9d9415e348699",
- "reference": "f4db5a78a5ea468d4831de7f0bf9d9415e348699",
+ "url": "https://api.github.com/repos/guzzle/guzzle/zipball/68d0ea14d5a3f42a20e87632a5f84931e2709c90",
+ "reference": "68d0ea14d5a3f42a20e87632a5f84931e2709c90",
"shasum": ""
},
"require": {
},
"require-dev": {
"ext-curl": "*",
- "phpunit/phpunit": "^4.0 || ^5.0",
+ "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4",
"psr/log": "^1.0"
},
"suggest": {
"psr/log": "Required for using the Log middleware"
},
- "time": "2017-06-22T18:50:49+00:00",
+ "time": "2018-03-26T16:33:04+00:00",
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "6.2-dev"
+ "dev-master": "6.3-dev"
}
},
"installation-source": "dist",
},
{
"name": "symfony/debug",
- "version": "v3.4.5",
- "version_normalized": "3.4.5.0",
+ "version": "v3.4.6",
+ "version_normalized": "3.4.6.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/debug.git",
},
{
"name": "twig/twig",
- "version": "v1.35.1",
- "version_normalized": "1.35.1.0",
+ "version": "v1.35.3",
+ "version_normalized": "1.35.3.0",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
- "reference": "ffda54d85cc3aec3ba499c8c0eda9b2176edfe83"
+ "reference": "b48680b6eb7d16b5025b9bfc4108d86f6b8af86f"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/twigphp/Twig/zipball/ffda54d85cc3aec3ba499c8c0eda9b2176edfe83",
- "reference": "ffda54d85cc3aec3ba499c8c0eda9b2176edfe83",
+ "url": "https://api.github.com/repos/twigphp/Twig/zipball/b48680b6eb7d16b5025b9bfc4108d86f6b8af86f",
+ "reference": "b48680b6eb7d16b5025b9bfc4108d86f6b8af86f",
"shasum": ""
},
"require": {
},
"require-dev": {
"psr/container": "^1.0",
- "symfony/debug": "~2.7",
- "symfony/phpunit-bridge": "~3.3@dev"
+ "symfony/debug": "^2.7",
+ "symfony/phpunit-bridge": "^3.3"
},
- "time": "2018-03-02T18:06:27+00:00",
+ "time": "2018-03-20T04:25:58+00:00",
"type": "library",
"extra": {
"branch-alias": {
## Changelog
+### 1.5.0
+
+This release increments the minimum supported PHP version to 7.1.0.
+
+Also, HHVM official support has been dropped.
+
+Some noticeable performance improvements to annotation autoloading
+have been applied, making failed annotation autoloading less heavy
+on the filesystem access.
+
+- [133: Add @throws annotation in AnnotationReader#__construct()](https://github.com/doctrine/annotations/issues/133) thanks to @SenseException
+- [134: Require PHP 7.1, drop HHVM support](https://github.com/doctrine/annotations/issues/134) thanks to @lcobucci
+- [135: Prevent the same loader from being registered twice](https://github.com/doctrine/annotations/issues/135) thanks to @jrjohnson
+- [137: #135 optimise multiple class load attempts in AnnotationRegistry](https://github.com/doctrine/annotations/issues/137) thanks to @Ocramius
+
### 1.4.0
{"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"}
],
"require": {
- "php": "^5.6 || ^7.0",
+ "php": "^7.1",
"doctrine/lexer": "1.*"
},
"require-dev": {
"doctrine/cache": "1.*",
- "phpunit/phpunit": "^5.7"
+ "phpunit/phpunit": "^6.4"
},
"autoload": {
"psr-4": { "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" }
},
"extra": {
"branch-alias": {
- "dev-master": "1.4.x-dev"
+ "dev-master": "1.6.x-dev"
}
}
}
'package_version' => true,
// PlantUML
'startuml' => true, 'enduml' => true,
+ // Symfony 3.3 Cache Adapter
+ 'experimental' => true
);
/**
* Initializes a new AnnotationReader.
*
* @param DocParser $parser
+ *
+ * @throws AnnotationException
*/
public function __construct(DocParser $parser = null)
{
namespace Doctrine\Common\Annotations;
-/**
- * AnnotationRegistry.
- */
final class AnnotationRegistry
{
/**
*
* This autoloading mechanism does not utilize the PHP autoloading but implements autoloading on its own.
*
- * @var array
+ * @var string[][]|string[]|null[]
*/
- static private $autoloadNamespaces = array();
+ static private $autoloadNamespaces = [];
/**
* A map of autoloader callables.
*
- * @var array
+ * @var callable[]
*/
- static private $loaders = array();
+ static private $loaders = [];
/**
- * @return void
+ * An array of classes which cannot be found
+ *
+ * @var null[] indexed by class name
*/
- static public function reset()
+ static private $failedToAutoload = [];
+
+ public static function reset() : void
{
- self::$autoloadNamespaces = array();
- self::$loaders = array();
+ self::$autoloadNamespaces = [];
+ self::$loaders = [];
+ self::$failedToAutoload = [];
}
/**
* Registers file.
*
- * @param string $file
- *
- * @return void
+ * @deprecated this method is deprecated and will be removed in doctrine/annotations 2.0
+ * autoloading should be deferred to the globally registered autoloader by then. For now,
+ * use @example AnnotationRegistry::registerLoader('class_exists')
*/
- static public function registerFile($file)
+ public static function registerFile(string $file) : void
{
require_once $file;
}
* @param string $namespace
* @param string|array|null $dirs
*
- * @return void
+ * @deprecated this method is deprecated and will be removed in doctrine/annotations 2.0
+ * autoloading should be deferred to the globally registered autoloader by then. For now,
+ * use @example AnnotationRegistry::registerLoader('class_exists')
*/
- static public function registerAutoloadNamespace($namespace, $dirs = null)
+ public static function registerAutoloadNamespace(string $namespace, $dirs = null) : void
{
self::$autoloadNamespaces[$namespace] = $dirs;
}
*
* Loading of this namespaces will be done with a PSR-0 namespace loading algorithm.
*
- * @param array $namespaces
+ * @param string[][]|string[]|null[] $namespaces indexed by namespace name
*
- * @return void
+ * @deprecated this method is deprecated and will be removed in doctrine/annotations 2.0
+ * autoloading should be deferred to the globally registered autoloader by then. For now,
+ * use @example AnnotationRegistry::registerLoader('class_exists')
*/
- static public function registerAutoloadNamespaces(array $namespaces)
+ public static function registerAutoloadNamespaces(array $namespaces) : void
{
- self::$autoloadNamespaces = array_merge(self::$autoloadNamespaces, $namespaces);
+ self::$autoloadNamespaces = \array_merge(self::$autoloadNamespaces, $namespaces);
}
/**
* NOTE: These class loaders HAVE to be silent when a class was not found!
* IMPORTANT: Loaders have to return true if they loaded a class that could contain the searched annotation class.
*
- * @param callable $callable
- *
- * @return void
+ * @deprecated this method is deprecated and will be removed in doctrine/annotations 2.0
+ * autoloading should be deferred to the globally registered autoloader by then. For now,
+ * use @example AnnotationRegistry::registerLoader('class_exists')
+ */
+ public static function registerLoader(callable $callable) : void
+ {
+ // Reset our static cache now that we have a new loader to work with
+ self::$failedToAutoload = [];
+ self::$loaders[] = $callable;
+ }
+
+ /**
+ * Registers an autoloading callable for annotations, if it is not already registered
*
- * @throws \InvalidArgumentException
+ * @deprecated this method is deprecated and will be removed in doctrine/annotations 2.0
*/
- static public function registerLoader($callable)
+ public static function registerUniqueLoader(callable $callable) : void
{
- if (!is_callable($callable)) {
- throw new \InvalidArgumentException("A callable is expected in AnnotationRegistry::registerLoader().");
+ if ( ! in_array($callable, self::$loaders, true) ) {
+ self::registerLoader($callable);
}
- self::$loaders[] = $callable;
}
/**
* Autoloads an annotation class silently.
- *
- * @param string $class
- *
- * @return boolean
*/
- static public function loadAnnotationClass($class)
+ public static function loadAnnotationClass(string $class) : bool
{
+ if (\class_exists($class, false)) {
+ return true;
+ }
+
+ if (\array_key_exists($class, self::$failedToAutoload)) {
+ return false;
+ }
+
foreach (self::$autoloadNamespaces AS $namespace => $dirs) {
- if (strpos($class, $namespace) === 0) {
- $file = str_replace("\\", DIRECTORY_SEPARATOR, $class) . ".php";
+ if (\strpos($class, $namespace) === 0) {
+ $file = \str_replace('\\', \DIRECTORY_SEPARATOR, $class) . '.php';
+
if ($dirs === null) {
if ($path = stream_resolve_include_path($file)) {
require $path;
return true;
}
} else {
- foreach((array)$dirs AS $dir) {
- if (is_file($dir . DIRECTORY_SEPARATOR . $file)) {
- require $dir . DIRECTORY_SEPARATOR . $file;
+ foreach((array) $dirs AS $dir) {
+ if (is_file($dir . \DIRECTORY_SEPARATOR . $file)) {
+ require $dir . \DIRECTORY_SEPARATOR . $file;
return true;
}
}
}
foreach (self::$loaders AS $loader) {
- if (call_user_func($loader, $class) === true) {
+ if ($loader($class) === true) {
return true;
}
}
+
+ self::$failedToAutoload[$class] = null;
+
return false;
}
}
- %currentWorkingDirectory%/tests/Doctrine/Tests/Common/Annotations/DocParserTest.php
excludes_analyse:
- %currentWorkingDirectory%/tests/*/Fixtures/*
+ - %currentWorkingDirectory%/tests/Doctrine/Tests/Common/Annotations/ReservedKeywordsClasses.php
+ - %currentWorkingDirectory%/tests/Doctrine/Tests/Common/Annotations/Ticket/DCOM58Entity.php
+ - %currentWorkingDirectory%/tests/Doctrine/Tests/DoctrineTestCase.php
polluteScopeWithLoopInitialAssignments: true
ignoreErrors:
- '#Class Doctrine_Tests_Common_Annotations_Fixtures_ClassNoNamespaceNoComment not found#'
+++ /dev/null
-# for php-coveralls
-service_name: travis-ci
-src_dir: lib
-coverage_clover: build/logs/clover.xml
+++ /dev/null
-vendor/
-build/
-phpunit.xml
-composer.lock
\ No newline at end of file
+++ /dev/null
-language: php
-
-sudo: false
-
-cache:
- directories:
- - vendor
- - $HOME/.composer/cache
-
-php:
- - 5.5
- - 5.6
- - 7.0
- - hhvm
-
-services:
- - riak
- - mongodb
- - memcached
- - redis-server
-
-before_install:
- - if [[ $TRAVIS_PHP_VERSION != 'hhvm' ]] ; then pecl channel-update pecl.php.net; fi;
- - if [[ $TRAVIS_PHP_VERSION != 'hhvm' && $TRAVIS_PHP_VERSION != '7.0' ]]; then pecl install riak-beta; fi;
- - if [[ $TRAVIS_PHP_VERSION =~ 5.[56] ]] ; then echo yes | pecl install apcu-4.0.10; fi;
- - if [[ $TRAVIS_PHP_VERSION = 7.* ]] ; then pecl config-set preferred_state beta; echo yes | pecl install apcu; fi;
- - if [[ $TRAVIS_PHP_VERSION != 'hhvm' ]]; then phpenv config-add ./tests/travis/php.ini; fi;
-
-install:
- - travis_retry composer install
-
-script:
- - ./vendor/bin/phpunit -c ./tests/travis/phpunit.travis.xml -v
-
-after_script:
- - php vendor/bin/coveralls -v
-
-matrix:
- fast_finish: true
- allow_failures:
- - php: hhvm
- - php: 7.0
# Doctrine Cache
-Master: [![Build Status](https://secure.travis-ci.org/doctrine/cache.png?branch=master)](http://travis-ci.org/doctrine/cache) [![Coverage Status](https://coveralls.io/repos/doctrine/cache/badge.png?branch=master)](https://coveralls.io/r/doctrine/cache?branch=master)
+[![Build Status](https://secure.travis-ci.org/doctrine/cache.png?branch=master)](http://travis-ci.org/doctrine/cache)
+[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/doctrine/cache/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/doctrine/cache/?branch=master)
+[![Code Coverage](https://scrutinizer-ci.com/g/doctrine/cache/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/doctrine/cache/?branch=master)
[![Latest Stable Version](https://poser.pugx.org/doctrine/cache/v/stable.png)](https://packagist.org/packages/doctrine/cache) [![Total Downloads](https://poser.pugx.org/doctrine/cache/downloads.png)](https://packagist.org/packages/doctrine/cache)
-Cache component extracted from the Doctrine Common project.
-
-## Changelog
-
-### v1.2
-
-* Added support for MongoDB as Cache Provider
-* Fix namespace version reset
+Cache component extracted from the Doctrine Common project. [Documentation](http://doctrine-orm.readthedocs.io/projects/doctrine-orm/en/latest/reference/caching.html)
+++ /dev/null
-# Version class and file
-project.version_class = Doctrine\\Common\\Cache\\Version
-project.version_file = lib/Doctrine/Common/Cache/Version.php
+++ /dev/null
-<?xml version="1.0"?>
-<project name="DoctrineCommonCache" default="build" basedir=".">
- <property file="build.properties" />
-
- <target name="php">
- <exec executable="which" outputproperty="php_executable">
- <arg value="php" />
- </exec>
- </target>
-
- <target name="prepare">
- <mkdir dir="build" />
- </target>
-
- <target name="build" depends="check-git-checkout-clean,prepare,php,composer">
- <exec executable="${php_executable}">
- <arg value="build/composer.phar" />
- <arg value="archive" />
- <arg value="--dir=build" />
- </exec>
- </target>
-
- <target name="composer" depends="php,composer-check,composer-download">
- <exec executable="${php_executable}">
- <arg value="build/composer.phar" />
- <arg value="install" />
- </exec>
- </target>
-
- <target name="composer-check" depends="prepare">
- <available file="build/composer.phar" property="composer.present"/>
- </target>
-
- <target name="composer-download" unless="composer.present">
- <exec executable="wget">
- <arg value="-Obuild/composer.phar" />
- <arg value="http://getcomposer.org/composer.phar" />
- </exec>
- </target>
-
- <target name="make-release" depends="check-git-checkout-clean,prepare,php">
- <replace file="${project.version_file}" token="-DEV" value="" failOnNoReplacements="true" />
- <exec executable="git" failonerror="true" outputproperty="current_git_branch">
- <arg value="rev-parse" />
- <arg value="--abbrev-ref" />
- <arg value="HEAD" />
- </exec>
- <exec executable="${php_executable}" outputproperty="doctrine.current_version" failonerror="true">
- <arg value="-r" />
- <arg value="require_once '${project.version_file}';echo ${project.version_class}::VERSION;" />
- </exec>
- <exec executable="${php_executable}" outputproperty="doctrine.next_version" failonerror="true">
- <arg value="-r" />
- <arg value="$parts = explode('.', str_ireplace(array('-DEV', '-ALPHA', '-BETA'), '', '${doctrine.current_version}'));
- if (count($parts) != 3) {
- throw new \InvalidArgumentException('Version is assumed in format x.y.z, ${doctrine.current_version} given');
- }
- if ('${current_git_branch}' === 'master') {
- $parts[1]++;
- } else {
- $parts[2]++;
- }
- echo implode('.', $parts);
- " />
- </exec>
-
- <git-commit file="${project.version_file}" message="Release ${doctrine.current_version}" />
- <git-tag version="${doctrine.current_version}" />
- <replace file="${project.version_file}" token="${doctrine.current_version}" value="${doctrine.next_version}-DEV" />
- <git-commit file="${project.version_file}" message="Bump version to ${doctrine.next_version}" />
- </target>
-
- <target name="check-git-checkout-clean">
- <exec executable="git" failonerror="true">
- <arg value="diff-index" />
- <arg value="--quiet" />
- <arg value="HEAD" />
- </exec>
- </target>
-
- <macrodef name="git-commit">
- <attribute name="file" default="NOT SET"/>
- <attribute name="message" default="NOT SET"/>
-
- <sequential>
- <exec executable="git">
- <arg value="add" />
- <arg value="@{file}" />
- </exec>
- <exec executable="git">
- <arg value="commit" />
- <arg value="-m" />
- <arg value="@{message}" />
- </exec>
- </sequential>
- </macrodef>
-
- <macrodef name="git-tag">
- <attribute name="version" default="NOT SET" />
-
- <sequential>
- <exec executable="git">
- <arg value="tag" />
- <arg value="-m" />
- <arg value="v@{version}" />
- <arg value="v@{version}" />
- </exec>
- </sequential>
- </macrodef>
-</project>
{"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"}
],
"require": {
- "php": "~5.5|~7.0"
+ "php": "~7.1"
},
"require-dev": {
- "phpunit/phpunit": "~4.8|~5.0",
- "satooshi/php-coveralls": "~0.6",
- "predis/predis": "~1.0"
+ "alcaeus/mongo-php-adapter": "^1.1",
+ "mongodb/mongodb": "^1.1",
+ "phpunit/phpunit": "^5.7",
+ "predis/predis": "~1.0"
+ },
+ "suggest": {
+ "alcaeus/mongo-php-adapter": "Required to use legacy MongoDB driver"
},
"conflict": {
"doctrine/common": ">2.2,<2.4"
},
"extra": {
"branch-alias": {
- "dev-master": "1.6.x-dev"
+ "dev-master": "1.7.x-dev"
}
}
}
$info['start_time'] = isset($info['start_time']) ? $info['start_time'] : $info['stime'];
}
- return array(
+ return [
Cache::STATS_HITS => $info['num_hits'],
Cache::STATS_MISSES => $info['num_misses'],
Cache::STATS_UPTIME => $info['start_time'],
Cache::STATS_MEMORY_USAGE => $info['mem_size'],
Cache::STATS_MEMORY_AVAILABLE => $sma['avail_mem'],
- );
+ ];
}
}
return apcu_delete($id) || ! apcu_exists($id);
}
+ /**
+ * {@inheritdoc}
+ */
+ protected function doDeleteMultiple(array $keys)
+ {
+ $result = apcu_delete($keys);
+
+ return false !== $result && count($result) !== count($keys);
+ }
+
/**
* {@inheritdoc}
*/
$info = apcu_cache_info(true);
$sma = apcu_sma_info();
- return array(
+ return [
Cache::STATS_HITS => $info['num_hits'],
Cache::STATS_MISSES => $info['num_misses'],
Cache::STATS_UPTIME => $info['start_time'],
Cache::STATS_MEMORY_USAGE => $info['mem_size'],
Cache::STATS_MEMORY_AVAILABLE => $sma['avail_mem'],
- );
+ ];
}
}
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
+ * @author Benoit Burnichon <bburnichon@gmail.com>
*/
-abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, MultiGetCache, MultiPutCache
+abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, MultiOperationCache
{
const DOCTRINE_NAMESPACE_CACHEKEY = 'DoctrineNamespaceCacheKey[%s]';
public function fetchMultiple(array $keys)
{
if (empty($keys)) {
- return array();
+ return [];
}
-
+
// note: the array_combine() is in place to keep an association between our $keys and the $namespacedKeys
- $namespacedKeys = array_combine($keys, array_map(array($this, 'getNamespacedId'), $keys));
+ $namespacedKeys = array_combine($keys, array_map([$this, 'getNamespacedId'], $keys));
$items = $this->doFetchMultiple($namespacedKeys);
- $foundItems = array();
+ $foundItems = [];
// no internal array function supports this sort of mapping: needs to be iterative
// this filters and combines keys in one pass
*/
public function saveMultiple(array $keysAndValues, $lifetime = 0)
{
- $namespacedKeysAndValues = array();
+ $namespacedKeysAndValues = [];
foreach ($keysAndValues as $key => $value) {
$namespacedKeysAndValues[$this->getNamespacedId($key)] = $value;
}
return $this->doSave($this->getNamespacedId($id), $data, $lifeTime);
}
+ /**
+ * {@inheritdoc}
+ */
+ public function deleteMultiple(array $keys)
+ {
+ return $this->doDeleteMultiple(array_map(array($this, 'getNamespacedId'), $keys));
+ }
+
/**
* {@inheritdoc}
*/
*
* @return string The namespaced id.
*/
- private function getNamespacedId($id)
+ private function getNamespacedId(string $id) : string
{
$namespaceVersion = $this->getNamespaceVersion();
*
* @return string
*/
- private function getNamespaceCacheKey()
+ private function getNamespaceCacheKey() : string
{
return sprintf(self::DOCTRINE_NAMESPACE_CACHEKEY, $this->namespace);
}
*
* @return integer
*/
- private function getNamespaceVersion()
+ private function getNamespaceVersion() : int
{
if (null !== $this->namespaceVersion) {
return $this->namespaceVersion;
}
- $namespaceCacheKey = $this->getNamespaceCacheKey();
- $this->namespaceVersion = $this->doFetch($namespaceCacheKey) ?: 1;
+ $namespaceCacheKey = $this->getNamespaceCacheKey();
+ $this->namespaceVersion = (int) $this->doFetch($namespaceCacheKey) ?: 1;
return $this->namespaceVersion;
}
*/
protected function doFetchMultiple(array $keys)
{
- $returnValues = array();
+ $returnValues = [];
foreach ($keys as $key) {
if (false !== ($item = $this->doFetch($key)) || $this->doContains($key)) {
*/
abstract protected function doSave($id, $data, $lifeTime = 0);
+ /**
+ * Default implementation of doDeleteMultiple. Each driver that supports multi-delete should override it.
+ *
+ * @param array $keys Array of keys to delete from cache
+ *
+ * @return bool TRUE if the operation was successful, FALSE if it wasn't
+ */
+ protected function doDeleteMultiple(array $keys)
+ {
+ $success = true;
+
+ foreach ($keys as $key) {
+ if (! $this->doDelete($key)) {
+ $success = false;
+ }
+ }
+
+ return $success;
+ }
+
/**
* Deletes a cache entry.
*
/**
* @var CacheProvider[]
*/
- private $cacheProviders = array();
+ private $cacheProviders = [];
/**
* Constructor
*
* @param CacheProvider[] $cacheProviders
*/
- public function __construct($cacheProviders = array())
+ public function __construct($cacheProviders = [])
{
- $this->cacheProviders = $cacheProviders;
+ $this->cacheProviders = $cacheProviders instanceof \Traversable
+ ? iterator_to_array($cacheProviders, false)
+ : array_values($cacheProviders);
}
/**
return false;
}
+ /**
+ * {@inheritdoc}
+ */
+ protected function doFetchMultiple(array $keys)
+ {
+ /* @var $traversedProviders CacheProvider[] */
+ $traversedProviders = [];
+ $keysCount = count($keys);
+ $fetchedValues = [];
+
+ foreach ($this->cacheProviders as $key => $cacheProvider) {
+ $fetchedValues = $cacheProvider->doFetchMultiple($keys);
+
+ // We populate all the previous cache layers (that are assumed to be faster)
+ if (count($fetchedValues) === $keysCount) {
+ foreach ($traversedProviders as $previousCacheProvider) {
+ $previousCacheProvider->doSaveMultiple($fetchedValues);
+ }
+
+ return $fetchedValues;
+ }
+
+ $traversedProviders[] = $cacheProvider;
+ }
+
+ return $fetchedValues;
+ }
+
/**
* {@inheritDoc}
*/
return $stored;
}
+ /**
+ * {@inheritdoc}
+ */
+ protected function doSaveMultiple(array $keysAndValues, $lifetime = 0)
+ {
+ $stored = true;
+
+ foreach ($this->cacheProviders as $cacheProvider) {
+ $stored = $cacheProvider->doSaveMultiple($keysAndValues, $lifetime) && $stored;
+ }
+
+ return $stored;
+ }
+
/**
* {@inheritDoc}
*/
return $deleted;
}
+ /**
+ * {@inheritdoc}
+ */
+ protected function doDeleteMultiple(array $keys)
+ {
+ $deleted = true;
+
+ foreach ($this->cacheProviders as $cacheProvider) {
+ $deleted = $cacheProvider->doDeleteMultiple($keys) && $deleted;
+ }
+
+ return $deleted;
+ }
+
/**
* {@inheritDoc}
*/
protected function doGetStats()
{
// We return all the stats from all adapters
- $stats = array();
+ $stats = [];
foreach ($this->cacheProviders as $cacheProvider) {
$stats[] = $cacheProvider->doGetStats();
$server = explode(":", $servers[0]);
$key = $server[0] . ":" . "11210";
$stats = $stats[$key];
- return array(
+ return [
Cache::STATS_HITS => $stats['get_hits'],
Cache::STATS_MISSES => $stats['get_misses'],
Cache::STATS_UPTIME => $stats['uptime'],
Cache::STATS_MEMORY_USAGE => $stats['bytes'],
Cache::STATS_MEMORY_AVAILABLE => $stats['limit_maxbytes'],
- );
+ ];
}
}
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+/*
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the MIT license. For more information, see
+ * <http://www.doctrine-project.org>.
+ */
+
+namespace Doctrine\Common\Cache;
+
+use MongoDB\BSON\Binary;
+use MongoDB\BSON\UTCDateTime;
+use MongoDB\Collection;
+use MongoDB\Database;
+use MongoDB\Driver\Exception\Exception;
+use MongoDB\Model\BSONDocument;
+
+/**
+ * MongoDB cache provider for ext-mongodb
+ *
+ * @internal Do not use - will be removed in 2.0. Use MongoDBCache instead
+ */
+class ExtMongoDBCache extends CacheProvider
+{
+ /**
+ * @var Database
+ */
+ private $database;
+
+ /**
+ * @var Collection
+ */
+ private $collection;
+
+ /**
+ * @var bool
+ */
+ private $expirationIndexCreated = false;
+
+ /**
+ * Constructor.
+ *
+ * This provider will default to the write concern and read preference
+ * options set on the Database instance (or inherited from MongoDB or
+ * Client). Using an unacknowledged write concern (< 1) may make the return
+ * values of delete() and save() unreliable. Reading from secondaries may
+ * make contain() and fetch() unreliable.
+ *
+ * @see http://www.php.net/manual/en/mongo.readpreferences.php
+ * @see http://www.php.net/manual/en/mongo.writeconcerns.php
+ * @param Collection $collection
+ */
+ public function __construct(Collection $collection)
+ {
+ // Ensure there is no typemap set - we want to use our own
+ $this->collection = $collection->withOptions(['typeMap' => null]);
+ $this->database = new Database($collection->getManager(), $collection->getDatabaseName());
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doFetch($id)
+ {
+ $document = $this->collection->findOne(['_id' => $id], [MongoDBCache::DATA_FIELD, MongoDBCache::EXPIRATION_FIELD]);
+
+ if ($document === null) {
+ return false;
+ }
+
+ if ($this->isExpired($document)) {
+ $this->createExpirationIndex();
+ $this->doDelete($id);
+ return false;
+ }
+
+ return unserialize($document[MongoDBCache::DATA_FIELD]->getData());
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doContains($id)
+ {
+ $document = $this->collection->findOne(['_id' => $id], [MongoDBCache::EXPIRATION_FIELD]);
+
+ if ($document === null) {
+ return false;
+ }
+
+ if ($this->isExpired($document)) {
+ $this->createExpirationIndex();
+ $this->doDelete($id);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doSave($id, $data, $lifeTime = 0)
+ {
+ try {
+ $this->collection->updateOne(
+ ['_id' => $id],
+ ['$set' => [
+ MongoDBCache::EXPIRATION_FIELD => ($lifeTime > 0 ? new UTCDateTime((time() + $lifeTime) * 1000): null),
+ MongoDBCache::DATA_FIELD => new Binary(serialize($data), Binary::TYPE_GENERIC),
+ ]],
+ ['upsert' => true]
+ );
+ } catch (Exception $e) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doDelete($id)
+ {
+ try {
+ $this->collection->deleteOne(['_id' => $id]);
+ } catch (Exception $e) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doFlush()
+ {
+ try {
+ // Use remove() in lieu of drop() to maintain any collection indexes
+ $this->collection->deleteMany([]);
+ } catch (Exception $e) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doGetStats()
+ {
+ $uptime = null;
+ $memoryUsage = null;
+
+ try {
+ $serverStatus = $this->database->command([
+ 'serverStatus' => 1,
+ 'locks' => 0,
+ 'metrics' => 0,
+ 'recordStats' => 0,
+ 'repl' => 0,
+ ])->toArray()[0];
+ $uptime = $serverStatus['uptime'] ?? null;
+ } catch (Exception $e) {
+ }
+
+ try {
+ $collStats = $this->database->command(['collStats' => $this->collection->getCollectionName()])->toArray()[0];
+ $memoryUsage = $collStats['size'] ?? null;
+ } catch (Exception $e) {
+ }
+
+ return [
+ Cache::STATS_HITS => null,
+ Cache::STATS_MISSES => null,
+ Cache::STATS_UPTIME => $uptime,
+ Cache::STATS_MEMORY_USAGE => $memoryUsage,
+ Cache::STATS_MEMORY_AVAILABLE => null,
+ ];
+ }
+
+ /**
+ * Check if the document is expired.
+ *
+ * @param BSONDocument $document
+ *
+ * @return bool
+ */
+ private function isExpired(BSONDocument $document): bool
+ {
+ return isset($document[MongoDBCache::EXPIRATION_FIELD]) &&
+ $document[MongoDBCache::EXPIRATION_FIELD] instanceof UTCDateTime &&
+ $document[MongoDBCache::EXPIRATION_FIELD]->toDateTime() < new \DateTime();
+ }
+
+ private function createExpirationIndex(): void
+ {
+ if ($this->expirationIndexCreated) {
+ return;
+ }
+
+ $this->collection->createIndex([MongoDBCache::EXPIRATION_FIELD => 1], ['background' => true, 'expireAfterSeconds' => 0]);
+ }
+}
$free = disk_free_space($this->directory);
- return array(
+ return [
Cache::STATS_HITS => null,
Cache::STATS_MISSES => null,
Cache::STATS_UPTIME => null,
Cache::STATS_MEMORY_USAGE => $usage,
Cache::STATS_MEMORY_AVAILABLE => $free,
- );
+ ];
}
/**
* @param string $path
* @return bool TRUE on success or if path already exists, FALSE if path cannot be created.
*/
- private function createPathIfNeeded($path)
+ private function createPathIfNeeded(string $path) : bool
{
if ( ! is_dir($path)) {
if (false === @mkdir($path, 0777 & (~$this->umask), true) && !is_dir($path)) {
*
* @return bool TRUE on success, FALSE if path cannot be created, if path is not writable or an any other error.
*/
- protected function writeFile($filename, $content)
+ protected function writeFile(string $filename, string $content) : bool
{
$filepath = pathinfo($filename, PATHINFO_DIRNAME);
@chmod($tmpFile, 0666 & (~$this->umask));
if (file_put_contents($tmpFile, $content) !== false) {
+ @chmod($tmpFile, 0666 & (~$this->umask));
if (@rename($tmpFile, $filename)) {
return true;
}
/**
* @return \Iterator
*/
- private function getIterator()
+ private function getIterator() : \Iterator
{
return new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS),
*
* @return bool
*/
- private function isFilenameEndingWithExtension($name)
+ private function isFilenameEndingWithExtension(string $name) : bool
{
return '' === $this->extension
|| strrpos($name, $this->extension) === (strlen($name) - $this->extensionStringLength);
--- /dev/null
+<?php
+/*
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the MIT license. For more information, see
+ * <http://www.doctrine-project.org>.
+ */
+
+namespace Doctrine\Common\Cache;
+
+use MongoBinData;
+use MongoCollection;
+use MongoCursorException;
+use MongoDate;
+
+/**
+ * MongoDB cache provider.
+ *
+ * @author Jeremy Mikola <jmikola@gmail.com>
+ * @internal Do not use - will be removed in 2.0. Use MongoDBCache instead
+ */
+class LegacyMongoDBCache extends CacheProvider
+{
+ /**
+ * @var MongoCollection
+ */
+ private $collection;
+
+ /**
+ * @var bool
+ */
+ private $expirationIndexCreated = false;
+
+ /**
+ * Constructor.
+ *
+ * This provider will default to the write concern and read preference
+ * options set on the MongoCollection instance (or inherited from MongoDB or
+ * MongoClient). Using an unacknowledged write concern (< 1) may make the
+ * return values of delete() and save() unreliable. Reading from secondaries
+ * may make contain() and fetch() unreliable.
+ *
+ * @see http://www.php.net/manual/en/mongo.readpreferences.php
+ * @see http://www.php.net/manual/en/mongo.writeconcerns.php
+ * @param MongoCollection $collection
+ */
+ public function __construct(MongoCollection $collection)
+ {
+ @trigger_error('Using the legacy MongoDB cache provider is deprecated and will be removed in 2.0', E_USER_DEPRECATED);
+ $this->collection = $collection;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doFetch($id)
+ {
+ $document = $this->collection->findOne(['_id' => $id], [MongoDBCache::DATA_FIELD, MongoDBCache::EXPIRATION_FIELD]);
+
+ if ($document === null) {
+ return false;
+ }
+
+ if ($this->isExpired($document)) {
+ $this->createExpirationIndex();
+ $this->doDelete($id);
+ return false;
+ }
+
+ return unserialize($document[MongoDBCache::DATA_FIELD]->bin);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doContains($id)
+ {
+ $document = $this->collection->findOne(['_id' => $id], [MongoDBCache::EXPIRATION_FIELD]);
+
+ if ($document === null) {
+ return false;
+ }
+
+ if ($this->isExpired($document)) {
+ $this->createExpirationIndex();
+ $this->doDelete($id);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doSave($id, $data, $lifeTime = 0)
+ {
+ try {
+ $result = $this->collection->update(
+ ['_id' => $id],
+ ['$set' => [
+ MongoDBCache::EXPIRATION_FIELD => ($lifeTime > 0 ? new MongoDate(time() + $lifeTime) : null),
+ MongoDBCache::DATA_FIELD => new MongoBinData(serialize($data), MongoBinData::BYTE_ARRAY),
+ ]],
+ ['upsert' => true, 'multiple' => false]
+ );
+ } catch (MongoCursorException $e) {
+ return false;
+ }
+
+ return ($result['ok'] ?? 1) == 1;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doDelete($id)
+ {
+ $result = $this->collection->remove(['_id' => $id]);
+
+ return ($result['ok'] ?? 1) == 1;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doFlush()
+ {
+ // Use remove() in lieu of drop() to maintain any collection indexes
+ $result = $this->collection->remove();
+
+ return ($result['ok'] ?? 1) == 1;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doGetStats()
+ {
+ $serverStatus = $this->collection->db->command([
+ 'serverStatus' => 1,
+ 'locks' => 0,
+ 'metrics' => 0,
+ 'recordStats' => 0,
+ 'repl' => 0,
+ ]);
+
+ $collStats = $this->collection->db->command(['collStats' => 1]);
+
+ return [
+ Cache::STATS_HITS => null,
+ Cache::STATS_MISSES => null,
+ Cache::STATS_UPTIME => $serverStatus['uptime'] ?? null,
+ Cache::STATS_MEMORY_USAGE => $collStats['size'] ?? null,
+ Cache::STATS_MEMORY_AVAILABLE => null,
+ ];
+ }
+
+ /**
+ * Check if the document is expired.
+ *
+ * @param array $document
+ *
+ * @return bool
+ */
+ private function isExpired(array $document) : bool
+ {
+ return isset($document[MongoDBCache::EXPIRATION_FIELD]) &&
+ $document[MongoDBCache::EXPIRATION_FIELD] instanceof MongoDate &&
+ $document[MongoDBCache::EXPIRATION_FIELD]->sec < time();
+ }
+
+
+ private function createExpirationIndex(): void
+ {
+ if ($this->expirationIndexCreated) {
+ return;
+ }
+
+ $this->expirationIndexCreated = true;
+ $this->collection->createIndex([MongoDBCache::EXPIRATION_FIELD => 1], ['background' => true, 'expireAfterSeconds' => 0]);
+ }
+}
protected function doGetStats()
{
$stats = $this->memcache->getStats();
- return array(
+ return [
Cache::STATS_HITS => $stats['get_hits'],
Cache::STATS_MISSES => $stats['get_misses'],
Cache::STATS_UPTIME => $stats['uptime'],
Cache::STATS_MEMORY_USAGE => $stats['bytes'],
Cache::STATS_MEMORY_AVAILABLE => $stats['limit_maxbytes'],
- );
+ ];
}
}
return $this->memcached->set($id, $data, (int) $lifeTime);
}
+ /**
+ * {@inheritdoc}
+ */
+ protected function doDeleteMultiple(array $keys)
+ {
+ return $this->memcached->deleteMulti($keys)
+ || $this->memcached->getResultCode() === Memcached::RES_NOTFOUND;
+ }
+
/**
* {@inheritdoc}
*/
$servers = $this->memcached->getServerList();
$key = $servers[0]['host'] . ':' . $servers[0]['port'];
$stats = $stats[$key];
- return array(
+ return [
Cache::STATS_HITS => $stats['get_hits'],
Cache::STATS_MISSES => $stats['get_misses'],
Cache::STATS_UPTIME => $stats['uptime'],
Cache::STATS_MEMORY_USAGE => $stats['bytes'],
Cache::STATS_MEMORY_AVAILABLE => $stats['limit_maxbytes'],
- );
+ ];
}
}
namespace Doctrine\Common\Cache;
-use MongoBinData;
use MongoCollection;
-use MongoCursorException;
-use MongoDate;
+use MongoDB\Collection;
/**
* MongoDB cache provider.
const EXPIRATION_FIELD = 'e';
/**
- * @var MongoCollection
+ * @var CacheProvider
*/
- private $collection;
+ private $provider;
/**
* Constructor.
*
* This provider will default to the write concern and read preference
- * options set on the MongoCollection instance (or inherited from MongoDB or
+ * options set on the collection instance (or inherited from MongoDB or
* MongoClient). Using an unacknowledged write concern (< 1) may make the
* return values of delete() and save() unreliable. Reading from secondaries
* may make contain() and fetch() unreliable.
*
* @see http://www.php.net/manual/en/mongo.readpreferences.php
* @see http://www.php.net/manual/en/mongo.writeconcerns.php
- * @param MongoCollection $collection
+ * @param MongoCollection|Collection $collection
*/
- public function __construct(MongoCollection $collection)
+ public function __construct($collection)
{
- $this->collection = $collection;
+ if ($collection instanceof MongoCollection) {
+ @trigger_error('Using a MongoCollection instance for creating a cache adapter is deprecated and will be removed in 2.0', E_USER_DEPRECATED);
+ $this->provider = new LegacyMongoDBCache($collection);
+ } elseif ($collection instanceof Collection) {
+ $this->provider = new ExtMongoDBCache($collection);
+ } else {
+ throw new \InvalidArgumentException('Invalid collection given - expected a MongoCollection or MongoDB\Collection instance');
+ }
}
/**
*/
protected function doFetch($id)
{
- $document = $this->collection->findOne(array('_id' => $id), array(self::DATA_FIELD, self::EXPIRATION_FIELD));
-
- if ($document === null) {
- return false;
- }
-
- if ($this->isExpired($document)) {
- $this->doDelete($id);
- return false;
- }
-
- return unserialize($document[self::DATA_FIELD]->bin);
+ return $this->provider->doFetch($id);
}
/**
*/
protected function doContains($id)
{
- $document = $this->collection->findOne(array('_id' => $id), array(self::EXPIRATION_FIELD));
-
- if ($document === null) {
- return false;
- }
-
- if ($this->isExpired($document)) {
- $this->doDelete($id);
- return false;
- }
-
- return true;
+ return $this->provider->doContains($id);
}
/**
*/
protected function doSave($id, $data, $lifeTime = 0)
{
- try {
- $result = $this->collection->update(
- array('_id' => $id),
- array('$set' => array(
- self::EXPIRATION_FIELD => ($lifeTime > 0 ? new MongoDate(time() + $lifeTime) : null),
- self::DATA_FIELD => new MongoBinData(serialize($data), MongoBinData::BYTE_ARRAY),
- )),
- array('upsert' => true, 'multiple' => false)
- );
- } catch (MongoCursorException $e) {
- return false;
- }
-
- return isset($result['ok']) ? $result['ok'] == 1 : true;
+ return $this->provider->doSave($id, $data, $lifeTime);
}
/**
*/
protected function doDelete($id)
{
- $result = $this->collection->remove(array('_id' => $id));
-
- return isset($result['ok']) ? $result['ok'] == 1 : true;
+ return $this->provider->doDelete($id);
}
/**
*/
protected function doFlush()
{
- // Use remove() in lieu of drop() to maintain any collection indexes
- $result = $this->collection->remove();
-
- return isset($result['ok']) ? $result['ok'] == 1 : true;
+ return $this->provider->doFlush();
}
/**
*/
protected function doGetStats()
{
- $serverStatus = $this->collection->db->command(array(
- 'serverStatus' => 1,
- 'locks' => 0,
- 'metrics' => 0,
- 'recordStats' => 0,
- 'repl' => 0,
- ));
-
- $collStats = $this->collection->db->command(array('collStats' => 1));
-
- return array(
- Cache::STATS_HITS => null,
- Cache::STATS_MISSES => null,
- Cache::STATS_UPTIME => (isset($serverStatus['uptime']) ? (int) $serverStatus['uptime'] : null),
- Cache::STATS_MEMORY_USAGE => (isset($collStats['size']) ? (int) $collStats['size'] : null),
- Cache::STATS_MEMORY_AVAILABLE => null,
- );
- }
-
- /**
- * Check if the document is expired.
- *
- * @param array $document
- *
- * @return bool
- */
- private function isExpired(array $document)
- {
- return isset($document[self::EXPIRATION_FIELD]) &&
- $document[self::EXPIRATION_FIELD] instanceof MongoDate &&
- $document[self::EXPIRATION_FIELD]->sec < time();
+ return $this->provider->doGetStats();
}
}
--- /dev/null
+<?php
+/*
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the MIT license. For more information, see
+ * <http://www.doctrine-project.org>.
+ */
+
+namespace Doctrine\Common\Cache;
+
+/**
+ * Interface for cache drivers that allows to put many items at once.
+ *
+ * @link www.doctrine-project.org
+ * @since 1.7
+ * @author Benoit Burnichon <bburnichon@gmail.com>
+ *
+ * @deprecated
+ */
+interface MultiDeleteCache
+{
+ /**
+ * Deletes several cache entries.
+ *
+ * @param string[] $keys Array of keys to delete from cache
+ *
+ * @return bool TRUE if the operation was successful, FALSE if it wasn't.
+ */
+ function deleteMultiple(array $keys);
+}
* @link www.doctrine-project.org
* @since 1.4
* @author Asmir Mustafic <goetas@gmail.com>
+ *
+ * @deprecated
*/
interface MultiGetCache
{
--- /dev/null
+<?php
+/*
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the MIT license. For more information, see
+ * <http://www.doctrine-project.org>.
+ */
+
+namespace Doctrine\Common\Cache;
+
+/**
+ * Interface for cache drivers that supports multiple items manipulation.
+ *
+ * @link www.doctrine-project.org
+ * @since 1.7
+ * @author LuÃs Cobucci <lcobucci@gmail.com>
+ */
+interface MultiOperationCache extends MultiGetCache, MultiDeleteCache, MultiPutCache
+{
+}
* @link www.doctrine-project.org
* @since 1.6
* @author Daniel Gorgan <danut007ro@gmail.com>
+ *
+ * @deprecated
*/
interface MultiPutCache
{
{
const EXTENSION = '.doctrinecache.php';
+ /**
+ * @var callable
+ *
+ * This is cached in a local static variable to avoid instantiating a closure each time we need an empty handler
+ */
+ private static $emptyErrorHandler;
+
/**
* {@inheritdoc}
*/
public function __construct($directory, $extension = self::EXTENSION, $umask = 0002)
{
parent::__construct($directory, $extension, $umask);
+
+ self::$emptyErrorHandler = function () {
+ };
}
/**
{
$value = $this->includeFileForId($id);
- if (! $value) {
+ if ($value === null) {
return false;
}
{
$value = $this->includeFileForId($id);
- if (! $value) {
+ if ($value === null) {
return false;
}
$lifeTime = time() + $lifeTime;
}
- if (is_object($data) && ! method_exists($data, '__set_state')) {
- throw new \InvalidArgumentException(
- "Invalid argument given, PhpFileCache only allows objects that implement __set_state() " .
- "and fully support var_export(). You can use the FilesystemCache to save arbitrary object " .
- "graphs using serialize()/deserialize()."
- );
- }
-
$filename = $this->getFilename($id);
- $value = array(
+ $value = [
'lifetime' => $lifeTime,
'data' => $data
- );
-
- $value = var_export($value, true);
- $code = sprintf('<?php return %s;', $value);
+ ];
+
+ if (is_object($data) && method_exists($data, '__set_state')) {
+ $value = var_export($value, true);
+ $code = sprintf('<?php return %s;', $value);
+ } else {
+ $value = var_export(serialize($value), true);
+ $code = sprintf('<?php return unserialize(%s);', $value);
+ }
return $this->writeFile($filename, $code);
}
/**
* @param string $id
*
- * @return array|false
+ * @return array|null
*/
- private function includeFileForId($id)
+ private function includeFileForId(string $id) : ?array
{
$fileName = $this->getFilename($id);
// note: error suppression is still faster than `file_exists`, `is_file` and `is_readable`
- $value = @include $fileName;
+ set_error_handler(self::$emptyErrorHandler);
+
+ $value = include $fileName;
+
+ restore_error_handler();
if (! isset($value['lifetime'])) {
- return false;
+ return null;
}
return $value;
<?php
+/*
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the MIT license. For more information, see
+ * <http://www.doctrine-project.org>.
+ */
namespace Doctrine\Common\Cache;
*/
protected function doFetchMultiple(array $keys)
{
- $fetchedItems = call_user_func_array(array($this->client, 'mget'), $keys);
+ $fetchedItems = call_user_func_array([$this->client, 'mget'], $keys);
return array_map('unserialize', array_filter(array_combine($keys, $fetchedItems)));
}
return $this->client->del($id) >= 0;
}
+ /**
+ * {@inheritdoc}
+ */
+ protected function doDeleteMultiple(array $keys)
+ {
+ return $this->client->del($keys) >= 0;
+ }
+
/**
* {@inheritdoc}
*/
{
$info = $this->client->info();
- return array(
+ return [
Cache::STATS_HITS => $info['Stats']['keyspace_hits'],
Cache::STATS_MISSES => $info['Stats']['keyspace_misses'],
Cache::STATS_UPTIME => $info['Server']['uptime_in_seconds'],
Cache::STATS_MEMORY_USAGE => $info['Memory']['used_memory'],
Cache::STATS_MEMORY_AVAILABLE => false
- );
+ ];
}
}
$fetchedItems = array_combine($keys, $this->redis->mget($keys));
// Redis mget returns false for keys that do not exist. So we need to filter those out unless it's the real data.
- $foundItems = array();
+ $foundItems = [];
foreach ($fetchedItems as $key => $value) {
if (false !== $value || $this->redis->exists($key)) {
return $this->redis->delete($id) >= 0;
}
+ /**
+ * {@inheritdoc}
+ */
+ protected function doDeleteMultiple(array $keys)
+ {
+ return $this->redis->delete($keys) >= 0;
+ }
+
/**
* {@inheritdoc}
*/
protected function doGetStats()
{
$info = $this->redis->info();
- return array(
+ return [
Cache::STATS_HITS => $info['keyspace_hits'],
Cache::STATS_MISSES => $info['keyspace_misses'],
Cache::STATS_UPTIME => $info['uptime_in_seconds'],
Cache::STATS_MEMORY_USAGE => $info['used_memory'],
Cache::STATS_MEMORY_AVAILABLE => false
- );
+ ];
}
/**
*/
protected function getSerializerValue()
{
- if (defined('HHVM_VERSION')) {
- return Redis::SERIALIZER_PHP;
- }
-
if (defined('Redis::SERIALIZER_IGBINARY') && extension_loaded('igbinary')) {
return Redis::SERIALIZER_IGBINARY;
}
namespace Doctrine\Common\Cache;
use Riak\Bucket;
-use Riak\Connection;
use Riak\Input;
use Riak\Exception;
use Riak\Object;
*
* @return bool
*/
- private function isExpired(Object $object)
+ private function isExpired(Object $object) : bool
{
$metadataMap = $object->getMetadataMap();
/**
* Constructor.
*
- * Calling the constructor will ensure that the database file and table
+ * Calling the constructor will ensure that the database file and table
* exist and will create both if they don't.
*
* @param SQLite3 $sqlite
*/
protected function doFetch($id)
{
- if ($item = $this->findById($id)) {
- return unserialize($item[self::DATA_FIELD]);
+ $item = $this->findById($id);
+
+ if (!$item) {
+ return false;
}
- return false;
+ return unserialize($item[self::DATA_FIELD]);
}
/**
*
* @return array|null
*/
- private function findById($id, $includeData = true)
+ private function findById($id, bool $includeData = true) : ?array
{
list($idField) = $fields = $this->getFields();
*
* @return array
*/
- private function getFields()
+ private function getFields() : array
{
- return array(static::ID_FIELD, static::DATA_FIELD, static::EXPIRATION_FIELD);
+ return [static::ID_FIELD, static::DATA_FIELD, static::EXPIRATION_FIELD];
}
/**
*
* @return bool
*/
- private function isExpired(array $item)
+ private function isExpired(array $item) : bool
{
return isset($item[static::EXPIRATION_FIELD]) &&
$item[self::EXPIRATION_FIELD] !== null &&
class Version
{
- const VERSION = '1.6.1-DEV';
+ const VERSION = '1.7.1';
}
return empty($result);
}
+ /**
+ * {@inheritdoc}
+ */
+ protected function doDeleteMultiple(array $keys)
+ {
+ $result = wincache_ucache_delete($keys);
+
+ return is_array($result) && count($result) !== count($keys);
+ }
+
/**
* {@inheritdoc}
*/
$info = wincache_ucache_info();
$meminfo = wincache_ucache_meminfo();
- return array(
+ return [
Cache::STATS_HITS => $info['total_hit_count'],
Cache::STATS_MISSES => $info['total_miss_count'],
Cache::STATS_UPTIME => $info['total_cache_uptime'],
Cache::STATS_MEMORY_USAGE => $meminfo['memory_total'],
Cache::STATS_MEMORY_AVAILABLE => $meminfo['memory_free'],
- );
+ ];
}
}
$this->checkAuthorization();
$info = xcache_info(XC_TYPE_VAR, 0);
- return array(
+ return [
Cache::STATS_HITS => $info['hits'],
Cache::STATS_MISSES => $info['misses'],
Cache::STATS_UPTIME => null,
Cache::STATS_MEMORY_USAGE => $info['size'],
Cache::STATS_MEMORY_AVAILABLE => $info['avail'],
- );
+ ];
}
}
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-
-<phpunit
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
- backupGlobals="false"
- colors="true"
- bootstrap="vendor/autoload.php"
->
- <php>
- <ini name="error_reporting" value="-1" />
- </php>
-
- <testsuites>
- <testsuite name="Doctrine Cache Test Suite">
- <directory>./tests/Doctrine/</directory>
- </testsuite>
- </testsuites>
-
- <filter>
- <whitelist>
- <directory>./lib/Doctrine/</directory>
- </whitelist>
- </filter>
-</phpunit>
+++ /dev/null
-<?php
-
-namespace Doctrine\Tests\Common\Cache;
-
-use Doctrine\Common\Cache\ApcCache;
-
-/**
- * @requires extension apc
- */
-class ApcCacheTest extends CacheTest
-{
- protected function setUp()
- {
- if (!ini_get('apc.enable_cli')) {
- $this->markTestSkipped('APC must be enabled for the CLI with the ini setting apc.enable_cli=1');
- }
- }
-
- protected function _getCacheDriver()
- {
- return new ApcCache();
- }
-
- public function testLifetime()
- {
- $this->markTestSkipped('The APC cache TTL is not working in a single process/request. See https://bugs.php.net/bug.php?id=58084');
- }
-}
+++ /dev/null
-<?php
-
-namespace Doctrine\Tests\Common\Cache;
-
-use Doctrine\Common\Cache\ApcuCache;
-
-/**
- * @requires extension apcu
- */
-class ApcuCacheTest extends CacheTest
-{
- protected function setUp()
- {
- if (!ini_get('apc.enable_cli')) {
- $this->markTestSkipped('APC must be enabled for the CLI with the ini setting apc.enable_cli=1');
- }
- }
-
- protected function _getCacheDriver()
- {
- return new ApcuCache();
- }
-
- public function testLifetime()
- {
- $this->markTestSkipped('The APC cache TTL is not working in a single process/request. See https://bugs.php.net/bug.php?id=58084');
- }
-}
+++ /dev/null
-<?php
-
-namespace Doctrine\Tests\Common\Cache;
-
-use Doctrine\Common\Cache\ArrayCache;
-use Doctrine\Common\Cache\Cache;
-
-class ArrayCacheTest extends CacheTest
-{
- protected function _getCacheDriver()
- {
- return new ArrayCache();
- }
-
- public function testGetStats()
- {
- $cache = $this->_getCacheDriver();
- $cache->fetch('test1');
- $cache->fetch('test2');
- $cache->fetch('test3');
-
- $cache->save('test1', 123);
- $cache->save('test2', 123);
-
- $cache->fetch('test1');
- $cache->fetch('test2');
- $cache->fetch('test3');
-
- $stats = $cache->getStats();
- $this->assertEquals(2, $stats[Cache::STATS_HITS]);
- $this->assertEquals(5, $stats[Cache::STATS_MISSES]); // +1 for internal call to DoctrineNamespaceCacheKey
- $this->assertNotNull($stats[Cache::STATS_UPTIME]);
- $this->assertNull($stats[Cache::STATS_MEMORY_USAGE]);
- $this->assertNull($stats[Cache::STATS_MEMORY_AVAILABLE]);
-
- $cache->delete('test1');
- $cache->delete('test2');
-
- $cache->fetch('test1');
- $cache->fetch('test2');
- $cache->fetch('test3');
-
- $stats = $cache->getStats();
- $this->assertEquals(2, $stats[Cache::STATS_HITS]);
- $this->assertEquals(8, $stats[Cache::STATS_MISSES]); // +1 for internal call to DoctrineNamespaceCacheKey
- }
-
- protected function isSharedStorage()
- {
- return false;
- }
-}
\ No newline at end of file
+++ /dev/null
-<?php
-
-namespace Doctrine\Tests\Common\Cache;
-
-use Doctrine\Common\Cache\FileCache;
-use RecursiveDirectoryIterator;
-use RecursiveIteratorIterator;
-
-abstract class BaseFileCacheTest extends CacheTest
-{
- protected $directory;
-
- protected function setUp()
- {
- do {
- $this->directory = sys_get_temp_dir() . '/doctrine_cache_'. uniqid();
- } while (file_exists($this->directory));
- }
-
- protected function tearDown()
- {
- if ( ! is_dir($this->directory)) {
- return;
- }
-
- $iterator = new RecursiveDirectoryIterator($this->directory);
-
- foreach (new RecursiveIteratorIterator($iterator, RecursiveIteratorIterator::CHILD_FIRST) as $file) {
- if ($file->isFile()) {
- @unlink($file->getRealPath());
- } elseif ($file->isDir()) {
- @rmdir($file->getRealPath());
- }
- }
-
- @rmdir($this->directory);
- }
-
- public function testFlushAllRemovesBalancingDirectories()
- {
- $cache = $this->_getCacheDriver();
-
- $this->assertTrue($cache->save('key1', 1));
- $this->assertTrue($cache->save('key2', 2));
- $this->assertTrue($cache->flushAll());
-
- $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::CHILD_FIRST);
-
- $this->assertCount(0, $iterator);
- }
-
- protected function isSharedStorage()
- {
- return false;
- }
-
- public function getPathLengthsToTest()
- {
- // Windows officially supports 260 bytes including null terminator
- // 258 bytes available to use due to php bug #70943
- // Windows officially supports 260 bytes including null terminator
- // 259 characters is too large due to PHP bug (https://bugs.php.net/bug.php?id=70943)
- // 260 characters is too large - null terminator is included in allowable length
- return array(
- array(257, false),
- array(258, false),
- array(259, true),
- array(260, true)
- );
- }
-
- private static function getBasePathForWindowsPathLengthTests($pathLength)
- {
- return FileCacheTest::getBasePathForWindowsPathLengthTests($pathLength);
- }
-
- /**
- * @param int $length
- * @param string $basePath
- *
- * @return array
- */
- private static function getKeyAndPathFittingLength($length, $basePath)
- {
- $baseDirLength = strlen($basePath);
- $extensionLength = strlen('.doctrine.cache');
- $directoryLength = strlen(DIRECTORY_SEPARATOR . 'aa' . DIRECTORY_SEPARATOR);
- $namespaceAndBracketLength = strlen(bin2hex("[][1]"));
- $keyLength = $length
- - ($baseDirLength
- + $extensionLength
- + $directoryLength
- + $namespaceAndBracketLength);
-
- $key = str_repeat('a', floor($keyLength / 2));
- $namespacedKey = '[' . $key . '][1]';
-
- $keyHash = hash('sha256', $namespacedKey);
-
- $keyPath = $basePath
- . DIRECTORY_SEPARATOR
- . substr($keyHash, 0, 2)
- . DIRECTORY_SEPARATOR
- . bin2hex($namespacedKey)
- . '.doctrine.cache';
-
- $hashedKeyPath = $basePath
- . DIRECTORY_SEPARATOR
- . substr($keyHash, 0, 2)
- . DIRECTORY_SEPARATOR
- . '_' . $keyHash
- . '.doctrine.cache';
-
- return array($key, $keyPath, $hashedKeyPath);
- }
-
- /**
- * @dataProvider getPathLengthsToTest
- *
- * @param int $length
- * @param bool $pathShouldBeHashed
- */
- public function testWindowsPathLengthLimitIsCorrectlyHandled($length, $pathShouldBeHashed)
- {
- $this->directory = self::getBasePathForWindowsPathLengthTests($length);
-
- list($key, $keyPath, $hashedKeyPath) = self::getKeyAndPathFittingLength($length, $this->directory);
-
- $this->assertEquals($length, strlen($keyPath), 'Unhashed path should be of correct length.');
-
- $cacheClass = get_class($this->_getCacheDriver());
- /* @var $cache \Doctrine\Common\Cache\FileCache */
- $cache = new $cacheClass($this->directory, '.doctrine.cache');
-
- // Trick it into thinking this is windows.
- $reflClass = new \ReflectionClass(FileCache::class);
- $reflProp = $reflClass->getProperty('isRunningOnWindows');
- $reflProp->setAccessible(true);
- $reflProp->setValue($cache, true);
- $reflProp->setAccessible(false);
-
- $value = uniqid('value', true);
-
- $cache->save($key, $value);
- $this->assertEquals($value, $cache->fetch($key));
-
- if ($pathShouldBeHashed) {
- $this->assertFileExists($hashedKeyPath, 'Path generated for key should be hashed.');
- unlink($hashedKeyPath);
- } else {
- $this->assertFileExists($keyPath, 'Path generated for key should not be hashed.');
- unlink($keyPath);
- }
- }
-}
+++ /dev/null
-<?php
-
-namespace Doctrine\Tests\Common\Cache;
-
-class CacheProviderTest extends \Doctrine\Tests\DoctrineTestCase
-{
- public function testFetchMultiWillFilterNonRequestedKeys()
- {
- /* @var $cache \Doctrine\Common\Cache\CacheProvider|\PHPUnit_Framework_MockObject_MockObject */
- $cache = $this->getMockForAbstractClass(
- 'Doctrine\Common\Cache\CacheProvider',
- array(),
- '',
- true,
- true,
- true,
- array('doFetchMultiple')
- );
-
- $cache
- ->expects($this->once())
- ->method('doFetchMultiple')
- ->will($this->returnValue(array(
- '[foo][1]' => 'bar',
- '[bar][1]' => 'baz',
- '[baz][1]' => 'tab',
- )));
-
- $this->assertEquals(
- array('foo' => 'bar', 'bar' => 'baz'),
- $cache->fetchMultiple(array('foo', 'bar'))
- );
- }
-
- public function testFailedDeleteAllDoesNotChangeNamespaceVersion()
- {
- /* @var $cache \Doctrine\Common\Cache\CacheProvider|\PHPUnit_Framework_MockObject_MockObject */
- $cache = $this->getMockForAbstractClass(
- 'Doctrine\Common\Cache\CacheProvider',
- array(),
- '',
- true,
- true,
- true,
- array('doFetch', 'doSave', 'doContains')
- );
-
- $cache
- ->expects($this->once())
- ->method('doFetch')
- ->with('DoctrineNamespaceCacheKey[]')
- ->will($this->returnValue(false));
-
- // doSave is only called once from deleteAll as we do not need to persist the default version in getNamespaceVersion()
- $cache
- ->expects($this->once())
- ->method('doSave')
- ->with('DoctrineNamespaceCacheKey[]')
- ->will($this->returnValue(false));
-
- // After a failed deleteAll() the local namespace version is not increased (still 1). Otherwise all data written afterwards
- // would be lost outside the current instance.
- $cache
- ->expects($this->once())
- ->method('doContains')
- ->with('[key][1]')
- ->will($this->returnValue(true));
-
- $this->assertFalse($cache->deleteAll(), 'deleteAll() returns false when saving the namespace version fails');
- $cache->contains('key');
- }
-
- public function testSaveMultipleNoFail()
- {
- /* @var $cache \Doctrine\Common\Cache\CacheProvider|\PHPUnit_Framework_MockObject_MockObject */
- $cache = $this->getMockForAbstractClass(
- 'Doctrine\Common\Cache\CacheProvider',
- array(),
- '',
- true,
- true,
- true,
- array('doSave')
- );
-
- $cache
- ->expects($this->at(1))
- ->method('doSave')
- ->with('[kerr][1]', 'verr', 0)
- ->will($this->returnValue(false));
-
- $cache
- ->expects($this->at(2))
- ->method('doSave')
- ->with('[kok][1]', 'vok', 0)
- ->will($this->returnValue(true));
-
- $cache->saveMultiple(array(
- 'kerr' => 'verr',
- 'kok' => 'vok',
- ));
- }
-}
+++ /dev/null
-<?php
-
-namespace Doctrine\Tests\Common\Cache;
-
-use Doctrine\Common\Cache\Cache;
-use ArrayObject;
-
-abstract class CacheTest extends \Doctrine\Tests\DoctrineTestCase
-{
- /**
- * @dataProvider provideDataToCache
- */
- public function testSetContainsFetchDelete($value)
- {
- $cache = $this->_getCacheDriver();
-
- // Test saving a value, checking if it exists, and fetching it back
- $this->assertTrue($cache->save('key', $value));
- $this->assertTrue($cache->contains('key'));
- if (is_object($value)) {
- $this->assertEquals($value, $cache->fetch('key'), 'Objects retrieved from the cache must be equal but not necessarily the same reference');
- } else {
- $this->assertSame($value, $cache->fetch('key'), 'Scalar and array data retrieved from the cache must be the same as the original, e.g. same type');
- }
-
- // Test deleting a value
- $this->assertTrue($cache->delete('key'));
- $this->assertFalse($cache->contains('key'));
- $this->assertFalse($cache->fetch('key'));
- }
-
- /**
- * @dataProvider provideDataToCache
- */
- public function testUpdateExistingEntry($value)
- {
- $cache = $this->_getCacheDriver();
-
- $this->assertTrue($cache->save('key', 'old-value'));
- $this->assertTrue($cache->contains('key'));
-
- $this->assertTrue($cache->save('key', $value));
- $this->assertTrue($cache->contains('key'));
- if (is_object($value)) {
- $this->assertEquals($value, $cache->fetch('key'), 'Objects retrieved from the cache must be equal but not necessarily the same reference');
- } else {
- $this->assertSame($value, $cache->fetch('key'), 'Scalar and array data retrieved from the cache must be the same as the original, e.g. same type');
- }
- }
-
- public function testCacheKeyIsCaseSensitive()
- {
- $cache = $this->_getCacheDriver();
-
- $this->assertTrue($cache->save('key', 'value'));
- $this->assertTrue($cache->contains('key'));
- $this->assertSame('value', $cache->fetch('key'));
-
- $this->assertFalse($cache->contains('KEY'));
- $this->assertFalse($cache->fetch('KEY'));
-
- $cache->delete('KEY');
- $this->assertTrue($cache->contains('key', 'Deleting cache item with different case must not affect other cache item'));
- }
-
- public function testFetchMultiple()
- {
- $cache = $this->_getCacheDriver();
- $values = $this->provideDataToCache();
- $saved = array();
-
- foreach ($values as $key => $value) {
- $cache->save($key, $value[0]);
-
- $saved[$key] = $value[0];
- }
-
- $keys = array_keys($saved);
-
- $this->assertEquals(
- $saved,
- $cache->fetchMultiple($keys),
- 'Testing fetchMultiple with different data types'
- );
- $this->assertEquals(
- array_slice($saved, 0, 1),
- $cache->fetchMultiple(array_slice($keys, 0, 1)),
- 'Testing fetchMultiple with a single key'
- );
-
- $keysWithNonExisting = array();
- $keysWithNonExisting[] = 'non_existing1';
- $keysWithNonExisting[] = $keys[0];
- $keysWithNonExisting[] = 'non_existing2';
- $keysWithNonExisting[] = $keys[1];
- $keysWithNonExisting[] = 'non_existing3';
-
- $this->assertEquals(
- array_slice($saved, 0, 2),
- $cache->fetchMultiple($keysWithNonExisting),
- 'Testing fetchMultiple with a subset of keys and mixed with non-existing ones'
- );
- }
-
- public function testFetchMultipleWithNoKeys()
- {
- $cache = $this->_getCacheDriver();
-
- $this->assertSame(array(), $cache->fetchMultiple(array()));
- }
-
- public function testSaveMultiple()
- {
- $cache = $this->_getCacheDriver();
- $cache->deleteAll();
-
- $data = array_map(function ($value) {
- return $value[0];
- }, $this->provideDataToCache());
-
- $this->assertTrue($cache->saveMultiple($data));
-
- $keys = array_keys($data);
-
- $this->assertEquals($data, $cache->fetchMultiple($keys));
- }
-
- public function provideDataToCache()
- {
- $obj = new \stdClass();
- $obj->foo = 'bar';
- $obj2 = new \stdClass();
- $obj2->bar = 'foo';
- $obj2->obj = $obj;
- $obj->obj2 = $obj2;
-
- return array(
- 'array' => array(array('one', 2, 3.01)),
- 'string' => array('value'),
- 'string_invalid_utf8' => array("\xc3\x28"),
- 'string_null_byte' => array('with'."\0".'null char'),
- 'integer' => array(1),
- 'float' => array(1.5),
- 'object' => array(new ArrayObject(array('one', 2, 3.01))),
- 'object_recursive' => array($obj),
- 'true' => array(true),
- // the following are considered FALSE in boolean context, but caches should still recognize their existence
- 'null' => array(null),
- 'false' => array(false),
- 'array_empty' => array(array()),
- 'string_zero' => array('0'),
- 'integer_zero' => array(0),
- 'float_zero' => array(0.0),
- 'string_empty' => array(''),
- );
- }
-
- public function testDeleteIsSuccessfulWhenKeyDoesNotExist()
- {
- $cache = $this->_getCacheDriver();
-
- $cache->delete('key');
- $this->assertFalse($cache->contains('key'));
- $this->assertTrue($cache->delete('key'));
- }
-
- public function testDeleteAll()
- {
- $cache = $this->_getCacheDriver();
-
- $this->assertTrue($cache->save('key1', 1));
- $this->assertTrue($cache->save('key2', 2));
- $this->assertTrue($cache->deleteAll());
- $this->assertFalse($cache->contains('key1'));
- $this->assertFalse($cache->contains('key2'));
- }
-
- /**
- * @dataProvider provideCacheIds
- */
- public function testCanHandleSpecialCacheIds($id)
- {
- $cache = $this->_getCacheDriver();
-
- $this->assertTrue($cache->save($id, 'value'));
- $this->assertTrue($cache->contains($id));
- $this->assertEquals('value', $cache->fetch($id));
-
- $this->assertTrue($cache->delete($id));
- $this->assertFalse($cache->contains($id));
- $this->assertFalse($cache->fetch($id));
- }
-
- public function testNoCacheIdCollisions()
- {
- $cache = $this->_getCacheDriver();
-
- $ids = $this->provideCacheIds();
-
- // fill cache with each id having a different value
- foreach ($ids as $index => $id) {
- $cache->save($id[0], $index);
- }
-
- // then check value of each cache id
- foreach ($ids as $index => $id) {
- $value = $cache->fetch($id[0]);
- $this->assertNotFalse($value, sprintf('Failed to retrieve data for cache id "%s".', $id[0]));
- if ($index !== $value) {
- $this->fail(sprintf('Cache id "%s" collides with id "%s".', $id[0], $ids[$value][0]));
- }
- }
- }
-
- /**
- * Returns cache ids with special characters that should still work.
- *
- * For example, the characters :\/<>"*?| are not valid in Windows filenames. So they must be encoded properly.
- * Each cache id should be considered different from the others.
- *
- * @return array
- */
- public function provideCacheIds()
- {
- return array(
- array(':'),
- array('\\'),
- array('/'),
- array('<'),
- array('>'),
- array('"'),
- array('*'),
- array('?'),
- array('|'),
- array('['),
- array(']'),
- array('ä'),
- array('a'),
- array('é'),
- array('e'),
- array('.'), // directory traversal
- array('..'), // directory traversal
- array('-'),
- array('_'),
- array('$'),
- array('%'),
- array(' '),
- array("\0"),
- array(''),
- array(str_repeat('a', 300)), // long key
- array(str_repeat('a', 113)),
- );
- }
-
- public function testLifetime()
- {
- $cache = $this->_getCacheDriver();
- $cache->save('expire', 'value', 1);
- $this->assertTrue($cache->contains('expire'), 'Data should not be expired yet');
- // @TODO should more TTL-based tests pop up, so then we should mock the `time` API instead
- sleep(2);
- $this->assertFalse($cache->contains('expire'), 'Data should be expired');
- }
-
- public function testNoExpire()
- {
- $cache = $this->_getCacheDriver();
- $cache->save('noexpire', 'value', 0);
- // @TODO should more TTL-based tests pop up, so then we should mock the `time` API instead
- sleep(1);
- $this->assertTrue($cache->contains('noexpire'), 'Data with lifetime of zero should not expire');
- }
-
- public function testLongLifetime()
- {
- $cache = $this->_getCacheDriver();
- $cache->save('longlifetime', 'value', 30 * 24 * 3600 + 1);
- $this->assertTrue($cache->contains('longlifetime'), 'Data with lifetime > 30 days should be accepted');
- }
-
- public function testDeleteAllAndNamespaceVersioningBetweenCaches()
- {
- if ( ! $this->isSharedStorage()) {
- $this->markTestSkipped('The cache storage needs to be shared.');
- }
-
- $cache1 = $this->_getCacheDriver();
- $cache2 = $this->_getCacheDriver();
-
- $this->assertTrue($cache1->save('key1', 1));
- $this->assertTrue($cache2->save('key2', 2));
-
- /* Both providers are initialized with the same namespace version, so
- * they can see entries set by each other.
- */
- $this->assertTrue($cache1->contains('key1'));
- $this->assertTrue($cache1->contains('key2'));
- $this->assertTrue($cache2->contains('key1'));
- $this->assertTrue($cache2->contains('key2'));
-
- /* Deleting all entries through one provider will only increment the
- * namespace version on that object (and in the cache itself, which new
- * instances will use to initialize). The second provider will retain
- * its original version and still see stale data.
- */
- $this->assertTrue($cache1->deleteAll());
- $this->assertFalse($cache1->contains('key1'));
- $this->assertFalse($cache1->contains('key2'));
- $this->assertTrue($cache2->contains('key1'));
- $this->assertTrue($cache2->contains('key2'));
-
- /* A new cache provider should not see the deleted entries, since its
- * namespace version will be initialized.
- */
- $cache3 = $this->_getCacheDriver();
- $this->assertFalse($cache3->contains('key1'));
- $this->assertFalse($cache3->contains('key2'));
- }
-
- public function testFlushAll()
- {
- $cache = $this->_getCacheDriver();
-
- $this->assertTrue($cache->save('key1', 1));
- $this->assertTrue($cache->save('key2', 2));
- $this->assertTrue($cache->flushAll());
- $this->assertFalse($cache->contains('key1'));
- $this->assertFalse($cache->contains('key2'));
- }
-
- public function testFlushAllAndNamespaceVersioningBetweenCaches()
- {
- if ( ! $this->isSharedStorage()) {
- $this->markTestSkipped('The cache storage needs to be shared.');
- }
-
- $cache1 = $this->_getCacheDriver();
- $cache2 = $this->_getCacheDriver();
-
- /* Deleting all elements from the first provider should increment its
- * namespace version before saving the first entry.
- */
- $cache1->deleteAll();
- $this->assertTrue($cache1->save('key1', 1));
-
- /* The second provider will be initialized with the same namespace
- * version upon its first save operation.
- */
- $this->assertTrue($cache2->save('key2', 2));
-
- /* Both providers have the same namespace version and can see entries
- * set by each other.
- */
- $this->assertTrue($cache1->contains('key1'));
- $this->assertTrue($cache1->contains('key2'));
- $this->assertTrue($cache2->contains('key1'));
- $this->assertTrue($cache2->contains('key2'));
-
- /* Flushing all entries through one cache will remove all entries from
- * the cache but leave their namespace version as-is.
- */
- $this->assertTrue($cache1->flushAll());
- $this->assertFalse($cache1->contains('key1'));
- $this->assertFalse($cache1->contains('key2'));
- $this->assertFalse($cache2->contains('key1'));
- $this->assertFalse($cache2->contains('key2'));
-
- /* Inserting a new entry will use the same, incremented namespace
- * version, and it will be visible to both providers.
- */
- $this->assertTrue($cache1->save('key1', 1));
- $this->assertTrue($cache1->contains('key1'));
- $this->assertTrue($cache2->contains('key1'));
-
- /* A new cache provider will be initialized with the original namespace
- * version and not share any visibility with the first two providers.
- */
- $cache3 = $this->_getCacheDriver();
- $this->assertFalse($cache3->contains('key1'));
- $this->assertFalse($cache3->contains('key2'));
- $this->assertTrue($cache3->save('key3', 3));
- $this->assertTrue($cache3->contains('key3'));
- }
-
- public function testNamespace()
- {
- $cache = $this->_getCacheDriver();
-
- $cache->setNamespace('ns1_');
-
- $this->assertTrue($cache->save('key1', 1));
- $this->assertTrue($cache->contains('key1'));
-
- $cache->setNamespace('ns2_');
-
- $this->assertFalse($cache->contains('key1'));
- }
-
- public function testDeleteAllNamespace()
- {
- $cache = $this->_getCacheDriver();
-
- $cache->setNamespace('ns1');
- $this->assertFalse($cache->contains('key1'));
- $cache->save('key1', 'test');
- $this->assertTrue($cache->contains('key1'));
-
- $cache->setNamespace('ns2');
- $this->assertFalse($cache->contains('key1'));
- $cache->save('key1', 'test');
- $this->assertTrue($cache->contains('key1'));
-
- $cache->setNamespace('ns1');
- $this->assertTrue($cache->contains('key1'));
- $cache->deleteAll();
- $this->assertFalse($cache->contains('key1'));
-
- $cache->setNamespace('ns2');
- $this->assertTrue($cache->contains('key1'));
- $cache->deleteAll();
- $this->assertFalse($cache->contains('key1'));
- }
-
- /**
- * @group DCOM-43
- */
- public function testGetStats()
- {
- $cache = $this->_getCacheDriver();
- $stats = $cache->getStats();
-
- $this->assertArrayHasKey(Cache::STATS_HITS, $stats);
- $this->assertArrayHasKey(Cache::STATS_MISSES, $stats);
- $this->assertArrayHasKey(Cache::STATS_UPTIME, $stats);
- $this->assertArrayHasKey(Cache::STATS_MEMORY_USAGE, $stats);
- $this->assertArrayHasKey(Cache::STATS_MEMORY_AVAILABLE, $stats);
- }
-
- public function testSaveReturnsTrueWithAndWithoutTTlSet()
- {
- $cache = $this->_getCacheDriver();
- $cache->deleteAll();
- $this->assertTrue($cache->save('without_ttl', 'without_ttl'));
- $this->assertTrue($cache->save('with_ttl', 'with_ttl', 3600));
- }
-
- public function testValueThatIsFalseBooleanIsProperlyRetrieved()
- {
- $cache = $this->_getCacheDriver();
- $cache->deleteAll();
-
- $this->assertTrue($cache->save('key1', false));
- $this->assertTrue($cache->contains('key1'));
- $this->assertFalse($cache->fetch('key1'));
- }
-
- /**
- * Return whether multiple cache providers share the same storage.
- *
- * This is used for skipping certain tests for shared storage behavior.
- *
- * @return bool
- */
- protected function isSharedStorage()
- {
- return true;
- }
-
- /**
- * @return \Doctrine\Common\Cache\CacheProvider
- */
- abstract protected function _getCacheDriver();
-}
+++ /dev/null
-<?php
-
-namespace Doctrine\Tests\Common\Cache;
-
-use Doctrine\Common\Cache\ApcCache;
-use Doctrine\Common\Cache\ArrayCache;
-use Doctrine\Common\Cache\ChainCache;
-
-class ChainCacheTest extends CacheTest
-{
- protected function _getCacheDriver()
- {
- return new ChainCache(array(new ArrayCache()));
- }
-
- public function testLifetime()
- {
- $this->markTestSkipped('The ChainCache test uses ArrayCache which does not implement TTL currently.');
- }
-
- public function testGetStats()
- {
- $cache = $this->_getCacheDriver();
- $stats = $cache->getStats();
-
- $this->assertInternalType('array', $stats);
- }
-
- public function testOnlyFetchFirstOne()
- {
- $cache1 = new ArrayCache();
- $cache2 = $this->getMockForAbstractClass('Doctrine\Common\Cache\CacheProvider');
-
- $cache2->expects($this->never())->method('doFetch');
-
- $chainCache = new ChainCache(array($cache1, $cache2));
- $chainCache->save('id', 'bar');
-
- $this->assertEquals('bar', $chainCache->fetch('id'));
- }
-
- public function testFetchPropagateToFastestCache()
- {
- $cache1 = new ArrayCache();
- $cache2 = new ArrayCache();
-
- $cache2->save('bar', 'value');
-
- $chainCache = new ChainCache(array($cache1, $cache2));
-
- $this->assertFalse($cache1->contains('bar'));
-
- $result = $chainCache->fetch('bar');
-
- $this->assertEquals('value', $result);
- $this->assertTrue($cache2->contains('bar'));
- }
-
- public function testNamespaceIsPropagatedToAllProviders()
- {
- $cache1 = new ArrayCache();
- $cache2 = new ArrayCache();
-
- $chainCache = new ChainCache(array($cache1, $cache2));
- $chainCache->setNamespace('bar');
-
- $this->assertEquals('bar', $cache1->getNamespace());
- $this->assertEquals('bar', $cache2->getNamespace());
- }
-
- public function testDeleteToAllProviders()
- {
- $cache1 = $this->getMockForAbstractClass('Doctrine\Common\Cache\CacheProvider');
- $cache2 = $this->getMockForAbstractClass('Doctrine\Common\Cache\CacheProvider');
-
- $cache1->expects($this->once())->method('doDelete');
- $cache2->expects($this->once())->method('doDelete');
-
- $chainCache = new ChainCache(array($cache1, $cache2));
- $chainCache->delete('bar');
- }
-
- public function testFlushToAllProviders()
- {
- $cache1 = $this->getMockForAbstractClass('Doctrine\Common\Cache\CacheProvider');
- $cache2 = $this->getMockForAbstractClass('Doctrine\Common\Cache\CacheProvider');
-
- $cache1->expects($this->once())->method('doFlush');
- $cache2->expects($this->once())->method('doFlush');
-
- $chainCache = new ChainCache(array($cache1, $cache2));
- $chainCache->flushAll();
- }
-
- protected function isSharedStorage()
- {
- return false;
- }
-}
\ No newline at end of file
+++ /dev/null
-<?php
-
-namespace Doctrine\Tests\Common\Cache;
-
-use Couchbase;
-use Doctrine\Common\Cache\CouchbaseCache;
-
-/**
- * @requires extension couchbase
- */
-class CouchbaseCacheTest extends CacheTest
-{
- private $couchbase;
-
- protected function setUp()
- {
- try {
- $this->couchbase = new Couchbase('127.0.0.1', 'Administrator', 'password', 'default');
- } catch(Exception $ex) {
- $this->markTestSkipped('Could not instantiate the Couchbase cache because of: ' . $ex);
- }
- }
-
- protected function _getCacheDriver()
- {
- $driver = new CouchbaseCache();
- $driver->setCouchbase($this->couchbase);
- return $driver;
- }
-}
\ No newline at end of file
+++ /dev/null
-<?php
-
-namespace Doctrine\Tests\Common\Cache;
-
-use Doctrine\Common\Cache\Cache;
-
-/**
- * @group DCOM-101
- */
-class FileCacheTest extends \Doctrine\Tests\DoctrineTestCase
-{
- /**
- * @var \Doctrine\Common\Cache\FileCache
- */
- private $driver;
-
- protected function setUp()
- {
- $this->driver = $this->getMock(
- 'Doctrine\Common\Cache\FileCache',
- array('doFetch', 'doContains', 'doSave'),
- array(), '', false
- );
- }
-
- public function testFilenameShouldCreateThePathWithOneSubDirectory()
- {
- $cache = $this->driver;
- $method = new \ReflectionMethod($cache, 'getFilename');
- $key = 'item-key';
- $expectedDir = array(
- '84',
- );
- $expectedDir = implode(DIRECTORY_SEPARATOR, $expectedDir);
-
- $method->setAccessible(true);
-
- $path = $method->invoke($cache, $key);
- $dirname = pathinfo($path, PATHINFO_DIRNAME);
-
- $this->assertEquals(DIRECTORY_SEPARATOR . $expectedDir, $dirname);
- }
-
- public function testFileExtensionCorrectlyEscaped()
- {
- $driver1 = $this->getMock(
- 'Doctrine\Common\Cache\FileCache',
- array('doFetch', 'doContains', 'doSave'),
- array(__DIR__, '.*')
- );
- $driver2 = $this->getMock(
- 'Doctrine\Common\Cache\FileCache',
- array('doFetch', 'doContains', 'doSave'),
- array(__DIR__, '.php')
- );
-
- $doGetStats = new \ReflectionMethod($driver1, 'doGetStats');
-
- $doGetStats->setAccessible(true);
-
- $stats1 = $doGetStats->invoke($driver1);
- $stats2 = $doGetStats->invoke($driver2);
-
- $this->assertSame(0, $stats1[Cache::STATS_MEMORY_USAGE]);
- $this->assertGreaterThan(0, $stats2[Cache::STATS_MEMORY_USAGE]);
- }
-
- /**
- * @group DCOM-266
- */
- public function testFileExtensionSlashCorrectlyEscaped()
- {
- $driver = $this->getMock(
- 'Doctrine\Common\Cache\FileCache',
- array('doFetch', 'doContains', 'doSave'),
- array(__DIR__ . '/../', DIRECTORY_SEPARATOR . basename(__FILE__))
- );
-
- $doGetStats = new \ReflectionMethod($driver, 'doGetStats');
-
- $doGetStats->setAccessible(true);
-
- $stats = $doGetStats->invoke($driver);
-
- $this->assertGreaterThan(0, $stats[Cache::STATS_MEMORY_USAGE]);
- }
-
- public function testNonIntUmaskThrowsInvalidArgumentException()
- {
- $this->setExpectedException('InvalidArgumentException');
-
- $this->getMock(
- 'Doctrine\Common\Cache\FileCache',
- array('doFetch', 'doContains', 'doSave'),
- array('', '', 'invalid')
- );
- }
-
- public function testGetDirectoryReturnsRealpathDirectoryString()
- {
- $directory = __DIR__ . '/../';
- $driver = $this->getMock(
- 'Doctrine\Common\Cache\FileCache',
- array('doFetch', 'doContains', 'doSave'),
- array($directory)
- );
-
- $doGetDirectory = new \ReflectionMethod($driver, 'getDirectory');
-
- $actualDirectory = $doGetDirectory->invoke($driver);
- $expectedDirectory = realpath($directory);
-
- $this->assertEquals($expectedDirectory, $actualDirectory);
- }
-
- public function testGetExtensionReturnsExtensionString()
- {
- $directory = __DIR__ . '/../';
- $extension = DIRECTORY_SEPARATOR . basename(__FILE__);
- $driver = $this->getMock(
- 'Doctrine\Common\Cache\FileCache',
- array('doFetch', 'doContains', 'doSave'),
- array($directory, $extension)
- );
-
- $doGetExtension = new \ReflectionMethod($driver, 'getExtension');
-
- $actualExtension = $doGetExtension->invoke($driver);
-
- $this->assertEquals($extension, $actualExtension);
- }
-
- const WIN_MAX_PATH_LEN = 258;
-
- public static function getBasePathForWindowsPathLengthTests($pathLength)
- {
- // Not using __DIR__ because it can get screwed up when xdebug debugger is attached.
- $basePath = realpath(sys_get_temp_dir()) . '/' . uniqid('doctrine-cache', true);
-
- /** @noinspection MkdirRaceConditionInspection */
- @mkdir($basePath);
-
- $basePath = realpath($basePath);
-
- // Test whether the desired path length is odd or even.
- $desiredPathLengthIsOdd = ($pathLength % 2) == 1;
-
- // If the cache key is not too long, the filecache codepath will add
- // a slash and bin2hex($key). The length of the added portion will be an odd number.
- // len(desired) = len(base path) + len(slash . bin2hex($key))
- // odd = even + odd
- // even = odd + odd
- $basePathLengthShouldBeOdd = !$desiredPathLengthIsOdd;
-
- $basePathLengthIsOdd = (strlen($basePath) % 2) == 1;
-
- // If the base path needs to be odd or even where it is not, we add an odd number of
- // characters as a pad. In this case, we're adding '\aa' (or '/aa' depending on platform)
- // This is all to make it so that the key we're testing would result in
- // a path that is exactly the length we want to test IF the path length limit
- // were not in place in FileCache.
- if ($basePathLengthIsOdd != $basePathLengthShouldBeOdd) {
- $basePath .= DIRECTORY_SEPARATOR . "aa";
- }
-
- return $basePath;
- }
-
- /**
- * @param int $length
- * @param string $basePath
- *
- * @return array
- */
- public static function getKeyAndPathFittingLength($length, $basePath)
- {
- $baseDirLength = strlen($basePath);
- $extensionLength = strlen('.doctrine.cache');
- $directoryLength = strlen(DIRECTORY_SEPARATOR . 'aa' . DIRECTORY_SEPARATOR);
- $keyLength = $length - ($baseDirLength + $extensionLength + $directoryLength); // - 1 because of slash
-
- $key = str_repeat('a', floor($keyLength / 2));
-
- $keyHash = hash('sha256', $key);
-
- $keyPath = $basePath
- . DIRECTORY_SEPARATOR
- . substr($keyHash, 0, 2)
- . DIRECTORY_SEPARATOR
- . bin2hex($key)
- . '.doctrine.cache';
-
- $hashedKeyPath = $basePath
- . DIRECTORY_SEPARATOR
- . substr($keyHash, 0, 2)
- . DIRECTORY_SEPARATOR
- . '_' . $keyHash
- . '.doctrine.cache';
-
- return array($key, $keyPath, $hashedKeyPath);
- }
-
- public function getPathLengthsToTest()
- {
- // Windows officially supports 260 bytes including null terminator
- // 259 characters is too large due to PHP bug (https://bugs.php.net/bug.php?id=70943)
- // 260 characters is too large - null terminator is included in allowable length
- return array(
- array(257, false),
- array(258, false),
- array(259, true),
- array(260, true)
- );
- }
-
- /**
- * @runInSeparateProcess
- * @dataProvider getPathLengthsToTest
- *
- * @covers \Doctrine\Common\Cache\FileCache::getFilename
- *
- * @param int $length
- * @param bool $pathShouldBeHashed
- */
- public function testWindowsPathLengthLimitationsAreCorrectlyRespected($length, $pathShouldBeHashed)
- {
- if (! defined('PHP_WINDOWS_VERSION_BUILD')) {
- define('PHP_WINDOWS_VERSION_BUILD', 'Yes, this is the "usual suspect", with the usual limitations');
- }
-
- $basePath = self::getBasePathForWindowsPathLengthTests($length);
-
- $fileCache = $this->getMockForAbstractClass(
- 'Doctrine\Common\Cache\FileCache',
- array($basePath, '.doctrine.cache')
- );
-
- list($key, $keyPath, $hashedKeyPath) = self::getKeyAndPathFittingLength($length, $basePath);
-
- $getFileName = new \ReflectionMethod($fileCache, 'getFilename');
-
- $getFileName->setAccessible(true);
-
- $this->assertEquals(
- $length,
- strlen($keyPath),
- sprintf('Path expected to be %d characters long is %d characters long', $length, strlen($keyPath))
- );
-
- if ($pathShouldBeHashed) {
- $keyPath = $hashedKeyPath;
- }
-
- if ($pathShouldBeHashed) {
- $this->assertSame(
- $hashedKeyPath,
- $getFileName->invoke($fileCache, $key),
- 'Keys should be hashed correctly if they are over the limit.'
- );
- } else {
- $this->assertSame(
- $keyPath,
- $getFileName->invoke($fileCache, $key),
- 'Keys below limit of the allowed length are used directly, unhashed'
- );
- }
- }
-}
+++ /dev/null
-<?php
-
-namespace Doctrine\Tests\Common\Cache;
-
-use Doctrine\Common\Cache\Cache;
-use Doctrine\Common\Cache\FilesystemCache;
-
-/**
- * @group DCOM-101
- */
-class FilesystemCacheTest extends BaseFileCacheTest
-{
- public function testGetStats()
- {
- $cache = $this->_getCacheDriver();
- $stats = $cache->getStats();
-
- $this->assertNull($stats[Cache::STATS_HITS]);
- $this->assertNull($stats[Cache::STATS_MISSES]);
- $this->assertNull($stats[Cache::STATS_UPTIME]);
- $this->assertEquals(0, $stats[Cache::STATS_MEMORY_USAGE]);
- $this->assertGreaterThan(0, $stats[Cache::STATS_MEMORY_AVAILABLE]);
- }
-
- public function testCacheInSharedDirectoryIsPerExtension()
- {
- $cache1 = new FilesystemCache($this->directory, '.foo');
- $cache2 = new FilesystemCache($this->directory, '.bar');
-
- $this->assertTrue($cache1->save('key1', 11));
- $this->assertTrue($cache1->save('key2', 12));
-
- $this->assertTrue($cache2->save('key1', 21));
- $this->assertTrue($cache2->save('key2', 22));
-
- $this->assertSame(11, $cache1->fetch('key1'), 'Cache value must not be influenced by a different cache in the same directory but different extension');
- $this->assertSame(12, $cache1->fetch('key2'));
- $this->assertTrue($cache1->flushAll());
- $this->assertFalse($cache1->fetch('key1'), 'flushAll() must delete all items with the current extension');
- $this->assertFalse($cache1->fetch('key2'));
-
- $this->assertSame(21, $cache2->fetch('key1'), 'flushAll() must not remove items with a different extension in a shared directory');
- $this->assertSame(22, $cache2->fetch('key2'));
- }
-
- public function testFlushAllWithNoExtension()
- {
- $cache = new FilesystemCache($this->directory, '');
-
- $this->assertTrue($cache->save('key1', 1));
- $this->assertTrue($cache->save('key2', 2));
- $this->assertTrue($cache->flushAll());
- $this->assertFalse($cache->contains('key1'));
- $this->assertFalse($cache->contains('key2'));
- }
-
- protected function _getCacheDriver()
- {
- return new FilesystemCache($this->directory);
- }
-}
+++ /dev/null
-<?php
-
-namespace Doctrine\Tests\Common\Cache;
-
-use Doctrine\Common\Cache\MemcacheCache;
-use Memcache;
-
-/**
- * @requires extension memcache
- */
-class MemcacheCacheTest extends CacheTest
-{
- private $memcache;
-
- protected function setUp()
- {
- $this->memcache = new Memcache();
-
- if (@$this->memcache->connect('localhost', 11211) === false) {
- unset($this->memcache);
- $this->markTestSkipped('Cannot connect to Memcache.');
- }
- }
-
- protected function tearDown()
- {
- if ($this->memcache instanceof Memcache) {
- $this->memcache->flush();
- }
- }
-
- /**
- * {@inheritdoc}
- *
- * Memcache does not support " " and null byte as key so we remove them from the tests.
- */
- public function provideCacheIds()
- {
- $ids = parent::provideCacheIds();
- unset($ids[21], $ids[22]);
-
- return $ids;
- }
-
- public function testGetMemcacheReturnsInstanceOfMemcache()
- {
- $this->assertInstanceOf('Memcache', $this->_getCacheDriver()->getMemcache());
- }
-
- /**
- * {@inheritDoc}
- */
- protected function _getCacheDriver()
- {
- $driver = new MemcacheCache();
- $driver->setMemcache($this->memcache);
- return $driver;
- }
-}
+++ /dev/null
-<?php
-
-namespace Doctrine\Tests\Common\Cache;
-
-use Doctrine\Common\Cache\MemcachedCache;
-use Memcached;
-
-/**
- * @requires extension memcached
- */
-class MemcachedCacheTest extends CacheTest
-{
- private $memcached;
-
- protected function setUp()
- {
- $this->memcached = new Memcached();
- $this->memcached->setOption(Memcached::OPT_COMPRESSION, false);
- $this->memcached->addServer('127.0.0.1', 11211);
-
- if (@fsockopen('127.0.0.1', 11211) === false) {
- unset($this->memcached);
- $this->markTestSkipped('Cannot connect to Memcached.');
- }
- }
-
- protected function tearDown()
- {
- if ($this->memcached instanceof Memcached) {
- $this->memcached->flush();
- }
- }
-
- /**
- * {@inheritdoc}
- *
- * Memcached does not support " ", null byte and very long keys so we remove them from the tests.
- */
- public function provideCacheIds()
- {
- $ids = parent::provideCacheIds();
- unset($ids[21], $ids[22], $ids[24]);
-
- return $ids;
- }
-
- public function testGetMemcachedReturnsInstanceOfMemcached()
- {
- $this->assertInstanceOf('Memcached', $this->_getCacheDriver()->getMemcached());
- }
-
- /**
- * {@inheritDoc}
- */
- protected function _getCacheDriver()
- {
- $driver = new MemcachedCache();
- $driver->setMemcached($this->memcached);
- return $driver;
- }
-}
+++ /dev/null
-<?php
-
-namespace Doctrine\Tests\Common\Cache;
-
-use Doctrine\Common\Cache\Cache;
-use Doctrine\Common\Cache\MongoDBCache;
-use MongoClient;
-use MongoCollection;
-
-/**
- * @requires extension mongo
- */
-class MongoDBCacheTest extends CacheTest
-{
- /**
- * @var MongoCollection
- */
- private $collection;
-
- protected function setUp()
- {
- if ( ! version_compare(phpversion('mongo'), '1.3.0', '>=')) {
- $this->markTestSkipped('Mongo >= 1.3.0 is required.');
- }
-
- $mongo = new MongoClient();
- $this->collection = $mongo->selectCollection('doctrine_common_cache', 'test');
- }
-
- protected function tearDown()
- {
- if ($this->collection instanceof MongoCollection) {
- $this->collection->drop();
- }
- }
-
- public function testGetStats()
- {
- $cache = $this->_getCacheDriver();
- $stats = $cache->getStats();
-
- $this->assertNull($stats[Cache::STATS_HITS]);
- $this->assertNull($stats[Cache::STATS_MISSES]);
- $this->assertGreaterThan(0, $stats[Cache::STATS_UPTIME]);
- $this->assertEquals(0, $stats[Cache::STATS_MEMORY_USAGE]);
- $this->assertNull($stats[Cache::STATS_MEMORY_AVAILABLE]);
- }
-
- /**
- * @group 108
- */
- public function testMongoCursorExceptionsDoNotBubbleUp()
- {
- /* @var $collection \MongoCollection|\PHPUnit_Framework_MockObject_MockObject */
- $collection = $this->getMock('MongoCollection', array(), array(), '', false);
-
- $collection->expects(self::once())->method('update')->willThrowException(new \MongoCursorException());
-
- $cache = new MongoDBCache($collection);
-
- self::assertFalse($cache->save('foo', 'bar'));
- }
-
- protected function _getCacheDriver()
- {
- return new MongoDBCache($this->collection);
- }
-}
+++ /dev/null
-<?php
-
-namespace Doctrine\Tests\Common\Cache;
-
-use Doctrine\Common\Cache\Cache;
-use Doctrine\Common\Cache\PhpFileCache;
-
-/**
- * @group DCOM-101
- */
-class PhpFileCacheTest extends BaseFileCacheTest
-{
- public function provideDataToCache()
- {
- $data = parent::provideDataToCache();
-
- unset($data['object'], $data['object_recursive']); // PhpFileCache only allows objects that implement __set_state() and fully support var_export()
-
- if (PHP_VERSION_ID < 70002) {
- unset($data['float_zero']); // var_export exports float(0) as int(0): https://bugs.php.net/bug.php?id=66179
- }
-
- return $data;
- }
-
- public function testImplementsSetState()
- {
- $cache = $this->_getCacheDriver();
-
- // Test save
- $cache->save('test_set_state', new SetStateClass(array(1,2,3)));
-
- //Test __set_state call
- $this->assertCount(0, SetStateClass::$values);
-
- // Test fetch
- $value = $cache->fetch('test_set_state');
- $this->assertInstanceOf('Doctrine\Tests\Common\Cache\SetStateClass', $value);
- $this->assertEquals(array(1,2,3), $value->getValue());
-
- //Test __set_state call
- $this->assertCount(1, SetStateClass::$values);
-
- // Test contains
- $this->assertTrue($cache->contains('test_set_state'));
- }
-
- public function testNotImplementsSetState()
- {
- $cache = $this->_getCacheDriver();
-
- $this->setExpectedException('InvalidArgumentException');
- $cache->save('test_not_set_state', new NotSetStateClass(array(1,2,3)));
- }
-
- public function testGetStats()
- {
- $cache = $this->_getCacheDriver();
- $stats = $cache->getStats();
-
- $this->assertNull($stats[Cache::STATS_HITS]);
- $this->assertNull($stats[Cache::STATS_MISSES]);
- $this->assertNull($stats[Cache::STATS_UPTIME]);
- $this->assertEquals(0, $stats[Cache::STATS_MEMORY_USAGE]);
- $this->assertGreaterThan(0, $stats[Cache::STATS_MEMORY_AVAILABLE]);
- }
-
- protected function _getCacheDriver()
- {
- return new PhpFileCache($this->directory);
- }
-}
-
-class NotSetStateClass
-{
- private $value;
-
- public function __construct($value)
- {
- $this->value = $value;
- }
-
- public function getValue()
- {
- return $this->value;
- }
-}
-
-class SetStateClass extends NotSetStateClass
-{
- public static $values = array();
-
- public static function __set_state($data)
- {
- self::$values = $data;
- return new self($data['value']);
- }
-}
+++ /dev/null
-<?php
-
-namespace Doctrine\Tests\Common\Cache;
-
-use Doctrine\Common\Cache\Cache;
-use Doctrine\Common\Cache\PredisCache;
-use Predis\Client;
-use Predis\Connection\ConnectionException;
-
-class PredisCacheTest extends CacheTest
-{
- private $client;
-
- protected function setUp()
- {
- if (!class_exists('Predis\Client')) {
- $this->markTestSkipped('Predis\Client is missing. Make sure to "composer install" to have all dev dependencies.');
- }
-
- $this->client = new Client();
-
- try {
- $this->client->connect();
- } catch (ConnectionException $e) {
- $this->markTestSkipped('Cannot connect to Redis because of: ' . $e);
- }
- }
-
- public function testHitMissesStatsAreProvided()
- {
- $cache = $this->_getCacheDriver();
- $stats = $cache->getStats();
-
- $this->assertNotNull($stats[Cache::STATS_HITS]);
- $this->assertNotNull($stats[Cache::STATS_MISSES]);
- }
-
- /**
- * @return PredisCache
- */
- protected function _getCacheDriver()
- {
- return new PredisCache($this->client);
- }
-
- /**
- * {@inheritDoc}
- *
- * @dataProvider provideDataToCache
- */
- public function testSetContainsFetchDelete($value)
- {
- if (array() === $value) {
- $this->markTestIncomplete(
- 'Predis currently doesn\'t support saving empty array values. '
- . 'See https://github.com/nrk/predis/issues/241'
- );
- }
-
- parent::testSetContainsFetchDelete($value);
- }
-
- /**
- * {@inheritDoc}
- *
- * @dataProvider provideDataToCache
- */
- public function testUpdateExistingEntry($value)
- {
- if (array() === $value) {
- $this->markTestIncomplete(
- 'Predis currently doesn\'t support saving empty array values. '
- . 'See https://github.com/nrk/predis/issues/241'
- );
- }
-
- parent::testUpdateExistingEntry($value);
- }
-
- public function testAllowsGenericPredisClient()
- {
- /* @var $predisClient \Predis\ClientInterface */
- $predisClient = $this->getMock('Predis\\ClientInterface');
-
- $this->assertInstanceOf('Doctrine\\Common\\Cache\\PredisCache', new PredisCache($predisClient));
- }
-}
+++ /dev/null
-<?php
-
-namespace Doctrine\Tests\Common\Cache;
-
-use Doctrine\Common\Cache\RedisCache;
-use Doctrine\Common\Cache\Cache;
-
-/**
- * @requires extension redis
- */
-class RedisCacheTest extends CacheTest
-{
- private $_redis;
-
- protected function setUp()
- {
- $this->_redis = new \Redis();
- $ok = @$this->_redis->connect('127.0.0.1');
- if (!$ok) {
- $this->markTestSkipped('Cannot connect to Redis.');
- }
- }
-
- public function testHitMissesStatsAreProvided()
- {
- $cache = $this->_getCacheDriver();
- $stats = $cache->getStats();
-
- $this->assertNotNull($stats[Cache::STATS_HITS]);
- $this->assertNotNull($stats[Cache::STATS_MISSES]);
- }
-
- public function testGetRedisReturnsInstanceOfRedis()
- {
- $this->assertInstanceOf('Redis', $this->_getCacheDriver()->getRedis());
- }
-
- public function testSerializerOptionWithOutIgbinaryExtension()
- {
- if (defined('Redis::SERIALIZER_IGBINARY') && extension_loaded('igbinary')) {
- $this->markTestSkipped('Extension igbinary is loaded.');
- }
-
- $this->assertEquals(
- \Redis::SERIALIZER_PHP,
- $this->_getCacheDriver()->getRedis()->getOption(\Redis::OPT_SERIALIZER)
- );
- }
-
- /**
- * {@inheritDoc}
- */
- protected function _getCacheDriver()
- {
- $driver = new RedisCache();
- $driver->setRedis($this->_redis);
- return $driver;
- }
-}
+++ /dev/null
-<?php
-
-namespace Doctrine\Tests\Common\Cache;
-
-use Riak\Bucket;
-use Riak\Connection;
-use Riak\Exception;
-use Doctrine\Common\Cache\RiakCache;
-
-/**
- * RiakCache test
- *
- * @group Riak
- * @requires extension riak
- */
-class RiakCacheTest extends CacheTest
-{
- /**
- * @var \Riak\Connection
- */
- private $connection;
-
- /**
- * @var \Riak\Bucket
- */
- private $bucket;
-
- protected function setUp()
- {
- try {
- $this->connection = new Connection('127.0.0.1', 8087);
- $this->bucket = new Bucket($this->connection, 'test');
- } catch (Exception\RiakException $e) {
- $this->markTestSkipped('Cannot connect to Riak.');
- }
- }
-
- /**
- * {@inheritdoc}
- */
- public function testGetStats()
- {
- $cache = $this->_getCacheDriver();
- $stats = $cache->getStats();
-
- $this->assertNull($stats);
- }
-
- /**
- * Retrieve RiakCache instance.
- *
- * @return \Doctrine\Common\Cache\RiakCache
- */
- protected function _getCacheDriver()
- {
- return new RiakCache($this->bucket);
- }
-}
+++ /dev/null
-<?php
-
-namespace Doctrine\Tests\Common\Cache;
-
-use Doctrine\Common\Cache\Cache;
-use Doctrine\Common\Cache\SQLite3Cache;
-use SQLite3;
-
-/**
- * @requires extension sqlite3
- */
-class SQLite3Test extends CacheTest
-{
- private $file;
- private $sqlite;
-
- protected function setUp()
- {
- $this->file = tempnam(null, 'doctrine-cache-test-');
- unlink($this->file);
- $this->sqlite = new SQLite3($this->file);
- }
-
- protected function tearDown()
- {
- $this->sqlite = null; // DB must be closed before
- unlink($this->file);
- }
-
- public function testGetStats()
- {
- $this->assertNull($this->_getCacheDriver()->getStats());
- }
-
- /**
- * {@inheritDoc}
- */
- protected function _getCacheDriver()
- {
- return new SQLite3Cache($this->sqlite, 'test_table');
- }
-}
+++ /dev/null
-<?php
-
-namespace Doctrine\Tests\Common\Cache;
-
-use Doctrine\Common\Cache\VoidCache;
-
-/**
- * @covers \Doctrine\Common\Cache\VoidCache
- */
-class VoidCacheTest extends \PHPUnit_Framework_TestCase
-{
- public function testShouldAlwaysReturnFalseOnContains()
- {
- $cache = new VoidCache();
-
- $this->assertFalse($cache->contains('foo'));
- $this->assertFalse($cache->contains('bar'));
- }
-
- public function testShouldAlwaysReturnFalseOnFetch()
- {
- $cache = new VoidCache();
-
- $this->assertFalse($cache->fetch('foo'));
- $this->assertFalse($cache->fetch('bar'));
- }
-
- public function testShouldAlwaysReturnTrueOnSaveButNotStoreAnything()
- {
- $cache = new VoidCache();
-
- $this->assertTrue($cache->save('foo', 'fooVal'));
-
- $this->assertFalse($cache->contains('foo'));
- $this->assertFalse($cache->fetch('foo'));
- }
-
- public function testShouldAlwaysReturnTrueOnDelete()
- {
- $cache = new VoidCache();
-
- $this->assertTrue($cache->delete('foo'));
- }
-
- public function testShouldAlwaysReturnNullOnGetStatus()
- {
- $cache = new VoidCache();
-
- $this->assertNull($cache->getStats());
- }
-
- public function testShouldAlwaysReturnTrueOnFlush()
- {
- $cache = new VoidCache();
-
- $this->assertTrue($cache->flushAll());
- }
-}
+++ /dev/null
-<?php
-
-namespace Doctrine\Tests\Common\Cache;
-
-use Doctrine\Common\Cache\WincacheCache;
-
-/**
- * @requires extension wincache
- */
-class WincacheCacheTest extends CacheTest
-{
- protected function _getCacheDriver()
- {
- return new WincacheCache();
- }
-}
\ No newline at end of file
+++ /dev/null
-<?php
-
-namespace Doctrine\Tests\Common\Cache;
-
-use Doctrine\Common\Cache\XcacheCache;
-
-/**
- * @requires extension xcache
- */
-class XcacheCacheTest extends CacheTest
-{
- protected function _getCacheDriver()
- {
- return new XcacheCache();
- }
-}
\ No newline at end of file
+++ /dev/null
-<?php
-
-namespace Doctrine\Tests\Common\Cache;
-
-use Doctrine\Common\Cache\ZendDataCache;
-
-/**
- * @requires function zend_shm_cache_fetch
- */
-class ZendDataCacheTest extends CacheTest
-{
- protected function setUp()
- {
- if ('apache2handler' !== php_sapi_name()) {
- $this->markTestSkipped('Zend Data Cache only works in apache2handler SAPI.');
- }
- }
-
- public function testGetStats()
- {
- $cache = $this->_getCacheDriver();
- $stats = $cache->getStats();
-
- $this->assertNull($stats);
- }
-
- protected function _getCacheDriver()
- {
- return new ZendDataCache();
- }
-}
\ No newline at end of file
+++ /dev/null
-<?php
-
-namespace Doctrine\Tests;
-
-/**
- * Base testcase class for all Doctrine testcases.
- */
-abstract class DoctrineTestCase extends \PHPUnit_Framework_TestCase
-{
-}
\ No newline at end of file
+++ /dev/null
-extension="mongo.so"
-extension="memcache.so"
-extension="memcached.so"
-extension="redis.so"
-
-apc.enabled=1
-apc.enable_cli=1
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-
-<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
- backupGlobals="false"
- colors="true"
- bootstrap="../../vendor/autoload.php"
->
- <php>
- <ini name="error_reporting" value="-1" />
- </php>
-
- <logging>
- <log type="coverage-clover" target="../../build/logs/clover.xml"/>
- </logging>
-
- <testsuites>
- <testsuite name="Doctrine Cache Test Suite">
- <directory>../Doctrine/</directory>
- </testsuite>
- </testsuites>
-
- <filter>
- <whitelist>
- <directory>../../lib/Doctrine/</directory>
- </whitelist>
- </filter>
-
- <groups>
- <exclude>
- <group>performance</group>
- </exclude>
- </groups>
-</phpunit>
{"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"}
],
"require": {
- "php": "^5.6 || ^7.0"
+ "php": "^7.1"
},
"require-dev": {
"phpunit/phpunit": "^5.7",
protected $collection;
/**
- * @var boolean
+ * @var bool
*/
protected $initialized = false;
*
* @param array $elements
*/
- public function __construct(array $elements = array())
+ public function __construct(array $elements = [])
{
$this->elements = $elements;
}
public function offsetSet($offset, $value)
{
if ( ! isset($offset)) {
- return $this->add($value);
+ $this->add($value);
+ return;
}
$this->set($offset, $value);
*/
public function offsetUnset($offset)
{
- return $this->remove($offset);
+ $this->remove($offset);
}
/**
*/
public function get($key)
{
- return isset($this->elements[$key]) ? $this->elements[$key] : null;
+ return $this->elements[$key] ?? null;
}
/**
/**
* {@inheritDoc}
+ *
+ * @return static
*/
public function map(Closure $func)
{
/**
* {@inheritDoc}
+ *
+ * @return static
*/
public function filter(Closure $p)
{
*/
public function partition(Closure $p)
{
- $matches = $noMatches = array();
+ $matches = $noMatches = [];
foreach ($this->elements as $key => $element) {
if ($p($key, $element)) {
}
}
- return array($this->createFrom($matches), $this->createFrom($noMatches));
+ return [$this->createFrom($matches), $this->createFrom($noMatches)];
}
/**
*/
public function clear()
{
- $this->elements = array();
+ $this->elements = [];
}
/**
*
* @param mixed $element The element to add.
*
- * @return boolean Always TRUE.
+ * @return bool Always TRUE.
*/
public function add($element);
*
* @param mixed $element The element to search for.
*
- * @return boolean TRUE if the collection contains the element, FALSE otherwise.
+ * @return bool TRUE if the collection contains the element, FALSE otherwise.
*/
public function contains($element);
/**
* Checks whether the collection is empty (contains no elements).
*
- * @return boolean TRUE if the collection is empty, FALSE otherwise.
+ * @return bool TRUE if the collection is empty, FALSE otherwise.
*/
public function isEmpty();
/**
* Removes the element at the specified index from the collection.
*
- * @param string|integer $key The kex/index of the element to remove.
+ * @param string|int $key The kex/index of the element to remove.
*
* @return mixed The removed element or NULL, if the collection did not contain the element.
*/
*
* @param mixed $element The element to remove.
*
- * @return boolean TRUE if this collection contained the specified element, FALSE otherwise.
+ * @return bool TRUE if this collection contained the specified element, FALSE otherwise.
*/
public function removeElement($element);
/**
* Checks whether the collection contains an element with the specified key/index.
*
- * @param string|integer $key The key/index to check for.
+ * @param string|int $key The key/index to check for.
*
- * @return boolean TRUE if the collection contains an element with the specified key/index,
- * FALSE otherwise.
+ * @return bool TRUE if the collection contains an element with the specified key/index,
+ * FALSE otherwise.
*/
public function containsKey($key);
/**
* Gets the element at the specified key/index.
*
- * @param string|integer $key The key/index of the element to retrieve.
+ * @param string|int $key The key/index of the element to retrieve.
*
* @return mixed
*/
/**
* Sets an element in the collection at the specified key/index.
*
- * @param string|integer $key The key/index of the element to set.
- * @param mixed $value The element to set.
+ * @param string|int $key The key/index of the element to set.
+ * @param mixed $value The element to set.
*
* @return void
*/
*
* @param Closure $p The predicate.
*
- * @return boolean TRUE if the predicate is TRUE for at least one element, FALSE otherwise.
+ * @return bool TRUE if the predicate is TRUE for at least one element, FALSE otherwise.
*/
public function exists(Closure $p);
*
* @param Closure $p The predicate.
*
- * @return boolean TRUE, if the predicate yields TRUE for all elements, FALSE otherwise.
+ * @return bool TRUE, if the predicate yields TRUE for all elements, FALSE otherwise.
*/
public function forAll(Closure $p);
*
* @param Closure $p The predicate on which to partition.
*
- * @return array An array with two elements. The first element contains the collection
- * of elements where the predicate returned TRUE, the second element
- * contains the collection of elements where the predicate returned FALSE.
+ * @return Collection[] An array with two elements. The first element contains the collection
+ * of elements where the predicate returned TRUE, the second element
+ * contains the collection of elements where the predicate returned FALSE.
*/
public function partition(Closure $p);
/**
* @var string[]
*/
- private $orderings = array();
+ private $orderings = [];
/**
* @var int|null
return $this->where($expression);
}
- $this->expression = new CompositeExpression(CompositeExpression::TYPE_AND, array(
- $this->expression, $expression
- ));
+ $this->expression = new CompositeExpression(
+ CompositeExpression::TYPE_AND,
+ [$this->expression, $expression]
+ );
return $this;
}
return $this->where($expression);
}
- $this->expression = new CompositeExpression(CompositeExpression::TYPE_OR, array(
- $this->expression, $expression
- ));
+ $this->expression = new CompositeExpression(
+ CompositeExpression::TYPE_OR,
+ [$this->expression, $expression]
+ );
return $this;
}
public function orderBy(array $orderings)
{
$this->orderings = array_map(
- function ($ordering) {
+ function (string $ordering) : string {
return strtoupper($ordering) === Criteria::ASC ? Criteria::ASC : Criteria::DESC;
},
$orderings
* directly or indirectly (through an accessor get*, is*, or a magic
* method, __get, __call).
*
- * @param object $object
+ * @param object|array $object
* @param string $field
*
* @return mixed
return $object[$field];
}
- $accessors = array('get', 'is');
+ $accessors = ['get', 'is'];
foreach ($accessors as $accessor) {
$accessor .= $field;
public static function sortByField($name, $orientation = 1, \Closure $next = null)
{
if ( ! $next) {
- $next = function() {
+ $next = function() : int {
return 0;
};
}
- return function ($a, $b) use ($name, $next, $orientation) {
+ return function ($a, $b) use ($name, $next, $orientation) : int {
$aValue = ClosureExpressionVisitor::getObjectFieldValue($a, $name);
$bValue = ClosureExpressionVisitor::getObjectFieldValue($b, $name);
switch ($comparison->getOperator()) {
case Comparison::EQ:
- return function ($object) use ($field, $value) {
+ return function ($object) use ($field, $value) : bool {
return ClosureExpressionVisitor::getObjectFieldValue($object, $field) === $value;
};
case Comparison::NEQ:
- return function ($object) use ($field, $value) {
+ return function ($object) use ($field, $value) : bool {
return ClosureExpressionVisitor::getObjectFieldValue($object, $field) !== $value;
};
case Comparison::LT:
- return function ($object) use ($field, $value) {
+ return function ($object) use ($field, $value) : bool {
return ClosureExpressionVisitor::getObjectFieldValue($object, $field) < $value;
};
case Comparison::LTE:
- return function ($object) use ($field, $value) {
+ return function ($object) use ($field, $value) : bool {
return ClosureExpressionVisitor::getObjectFieldValue($object, $field) <= $value;
};
case Comparison::GT:
- return function ($object) use ($field, $value) {
+ return function ($object) use ($field, $value) : bool {
return ClosureExpressionVisitor::getObjectFieldValue($object, $field) > $value;
};
case Comparison::GTE:
- return function ($object) use ($field, $value) {
+ return function ($object) use ($field, $value) : bool {
return ClosureExpressionVisitor::getObjectFieldValue($object, $field) >= $value;
};
case Comparison::IN:
- return function ($object) use ($field, $value) {
- return in_array(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value);
+ return function ($object) use ($field, $value) : bool {
+ return in_array(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value, true);
};
case Comparison::NIN:
- return function ($object) use ($field, $value) {
- return ! in_array(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value);
+ return function ($object) use ($field, $value) : bool {
+ return ! in_array(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value, true);
};
case Comparison::CONTAINS:
};
case Comparison::MEMBER_OF:
- return function ($object) use ($field, $value) {
+ return function ($object) use ($field, $value) : bool {
$fieldValues = ClosureExpressionVisitor::getObjectFieldValue($object, $field);
if (!is_array($fieldValues)) {
$fieldValues = iterator_to_array($fieldValues);
}
- return in_array($value, $fieldValues);
+ return in_array($value, $fieldValues, true);
};
case Comparison::STARTS_WITH:
- return function ($object) use ($field, $value) {
+ return function ($object) use ($field, $value) : bool {
return 0 === strpos(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value);
};
case Comparison::ENDS_WITH:
- return function ($object) use ($field, $value) {
+ return function ($object) use ($field, $value) : bool {
return $value === substr(ClosureExpressionVisitor::getObjectFieldValue($object, $field), -strlen($value));
};
*/
public function walkCompositeExpression(CompositeExpression $expr)
{
- $expressionList = array();
+ $expressionList = [];
foreach ($expr->getExpressionList() as $child) {
$expressionList[] = $this->dispatch($child);
*
* @return callable
*/
- private function andExpressions($expressions)
+ private function andExpressions(array $expressions) : callable
{
- return function ($object) use ($expressions) {
+ return function ($object) use ($expressions) : bool {
foreach ($expressions as $expression) {
if ( ! $expression($object)) {
return false;
}
}
+
return true;
};
}
*
* @return callable
*/
- private function orExpressions($expressions)
+ private function orExpressions(array $expressions) : callable
{
- return function ($object) use ($expressions) {
+ return function ($object) use ($expressions) : bool {
foreach ($expressions as $expression) {
if ($expression($object)) {
return true;
}
}
+
return false;
};
}
*/
class Comparison implements Expression
{
- const EQ = '=';
- const NEQ = '<>';
- const LT = '<';
- const LTE = '<=';
- const GT = '>';
- const GTE = '>=';
- const IS = '='; // no difference with EQ
- const IN = 'IN';
- const NIN = 'NIN';
- const CONTAINS = 'CONTAINS';
- const MEMBER_OF = 'MEMBER_OF';
+ const EQ = '=';
+ const NEQ = '<>';
+ const LT = '<';
+ const LTE = '<=';
+ const GT = '>';
+ const GTE = '>=';
+ const IS = '='; // no difference with EQ
+ const IN = 'IN';
+ const NIN = 'NIN';
+ const CONTAINS = 'CONTAINS';
+ const MEMBER_OF = 'MEMBER_OF';
const STARTS_WITH = 'STARTS_WITH';
- const ENDS_WITH = 'ENDS_WITH';
+ const ENDS_WITH = 'ENDS_WITH';
+
/**
* @var string
*/
/**
* @var Expression[]
*/
- private $expressions = array();
+ private $expressions = [];
/**
* @param string $type
* [Website](http://www.doctrine-project.org)
* [Documentation](http://docs.doctrine-project.org/projects/doctrine-common/en/latest/)
-* [Issue Tracker](http://www.doctrine-project.org/jira/browse/DCOM)
* [Downloads](http://github.com/doctrine/common/downloads)
{"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"}
],
"require": {
- "php": "~5.6|~7.0",
+ "php": "~7.1",
"doctrine/inflector": "1.*",
"doctrine/cache": "1.*",
"doctrine/collections": "1.*",
"doctrine/annotations": "1.*"
},
"require-dev": {
- "phpunit/phpunit": "^5.4.6"
+ "phpunit/phpunit": "^5.7"
},
"autoload": {
"psr-4": {
"Doctrine\\Common\\": "lib/Doctrine/Common"
}
},
+ "autoload-dev": {
+ "psr-4": {
+ "Doctrine\\Tests\\": "tests/Doctrine/Tests"
+ }
+ },
"extra": {
"branch-alias": {
- "dev-master": "2.7.x-dev"
+ "dev-master": "2.8.x-dev"
}
}
}
*
* @param string $className The name of the class.
*
- * @return ClassLoader The <tt>ClassLoader</tt> for the class or NULL if no such <tt>ClassLoader</tt> exists.
+ * @return ClassLoader|null The <tt>ClassLoader</tt> for the class or NULL if no such <tt>ClassLoader</tt> exists.
*/
public static function getClassLoader($className)
{
* @param EventArgs|null $eventArgs The event arguments to pass to the event handlers/listeners.
* If not supplied, the single empty EventArgs instance is used.
*
- * @return boolean
+ * @return void
*/
public function dispatchEvent($eventName, EventArgs $eventArgs = null)
{
*/
public function hasListeners($event)
{
- return isset($this->_listeners[$event]) && $this->_listeners[$event];
+ return !empty($this->_listeners[$event]);
}
/**
$hash = spl_object_hash($listener);
foreach ((array) $events as $event) {
- // Check if actually have this listener associated
- if (isset($this->_listeners[$event][$hash])) {
- unset($this->_listeners[$event][$hash]);
- }
+ unset($this->_listeners[$event][$hash]);
}
}
public function getManagerNames();
/**
- * Gets the ObjectRepository for an persistent object.
+ * Gets the ObjectRepository for a persistent object.
*
* @param string $persistentObject The name of the persistent object.
* @param string $persistentManagerName The object manager name (null for the default one).
try {
if ($this->cacheDriver) {
- if (($cached = $this->cacheDriver->fetch($realClassName . $this->cacheSalt)) instanceof ClassMetadata) {
+ $cached = $this->cacheDriver->fetch($realClassName . $this->cacheSalt);
+ if ($cached instanceof ClassMetadata) {
$this->loadedMetadata[$realClassName] = $cached;
$this->wakeupReflection($cached, $this->getReflectionService());
* Loads the metadata of the class in question and all it's ancestors whose metadata
* is still not loaded.
*
- * Important: The class $name does not necesarily exist at this point here.
+ * Important: The class $name does not necessarily exist at this point here.
* Scenarios in a code-generation setup might have access to XML/YAML
* Mapping files without the actual PHP code existing here. That is why the
* {@see Doctrine\Common\Persistence\Mapping\ReflectionService} interface
/**
* {@inheritDoc}
*/
- public function __construct($locator, $fileExtension = null)
+ public function __construct($locator)
{
parent::__construct($locator, '.php');
}
*/
public function persist($object)
{
- return $this->wrapped->persist($object);
+ $this->wrapped->persist($object);
}
/**
*/
public function remove($object)
{
- return $this->wrapped->remove($object);
+ $this->wrapped->remove($object);
}
/**
*/
public function clear($objectName = null)
{
- return $this->wrapped->clear($objectName);
+ $this->wrapped->clear($objectName);
}
/**
*/
public function detach($object)
{
- return $this->wrapped->detach($object);
+ $this->wrapped->detach($object);
}
/**
*/
public function refresh($object)
{
- return $this->wrapped->refresh($object);
+ $this->wrapped->refresh($object);
}
/**
*/
public function flush()
{
- return $this->wrapped->flush();
+ $this->wrapped->flush();
}
/**
*/
public function initializeObject($obj)
{
- return $this->wrapped->initializeObject($obj);
+ $this->wrapped->initializeObject($obj);
}
/**
*/
const AUTOGENERATE_EVAL = 3;
+ private const AUTOGENERATE_MODES = [
+ self::AUTOGENERATE_NEVER,
+ self::AUTOGENERATE_ALWAYS,
+ self::AUTOGENERATE_FILE_NOT_EXISTS,
+ self::AUTOGENERATE_EVAL,
+ ];
+
/**
* @var \Doctrine\Common\Persistence\Mapping\ClassMetadataFactory
*/
private $proxyGenerator;
/**
- * @var bool Whether to automatically (re)generate proxy classes.
+ * @var int Whether to automatically (re)generate proxy classes.
*/
private $autoGenerate;
* @param \Doctrine\Common\Proxy\ProxyGenerator $proxyGenerator
* @param \Doctrine\Common\Persistence\Mapping\ClassMetadataFactory $metadataFactory
* @param bool|int $autoGenerate
+ *
+ * @throws \Doctrine\Common\Proxy\Exception\InvalidArgumentException When auto generate mode is not valid.
*/
public function __construct(ProxyGenerator $proxyGenerator, ClassMetadataFactory $metadataFactory, $autoGenerate)
{
$this->proxyGenerator = $proxyGenerator;
$this->metadataFactory = $metadataFactory;
- $this->autoGenerate = (int)$autoGenerate;
+
+ $this->autoGenerate = (int)$autoGenerate;
+
+ if ( ! in_array($this->autoGenerate, self::AUTOGENERATE_MODES, true)) {
+ throw InvalidArgumentException::invalidAutoGenerateMode($autoGenerate);
+ }
}
/**
return new self(sprintf('Invalid \$notFoundCallback given: must be a callable, "%s" given', $type));
}
+
+ /**
+ * @param string $className
+ *
+ * @return self
+ */
+ public static function classMustNotBeAbstract($className)
+ {
+ return new self(sprintf('Unable to create a proxy for an abstract class "%s".', $className));
+ }
+
+ /**
+ * @param string $className
+ *
+ * @return self
+ */
+ public static function classMustNotBeFinal($className)
+ {
+ return new self(sprintf('Unable to create a proxy for a final class "%s".', $className));
+ }
+
+ /**
+ * @param mixed $value
+ *
+ * @return self
+ */
+ public static function invalidAutoGenerateMode($value): self
+ {
+ return new self(sprintf('Invalid auto generate mode "%s" given.', $value));
+ }
}
* @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class Metadata for the original class.
* @param string|bool $fileName Filename (full path) for the generated class. If none is given, eval() is used.
*
+ * @throws InvalidArgumentException
* @throws UnexpectedValueException
*/
public function generateProxyClass(ClassMetadata $class, $fileName = false)
{
+ $this->verifyClassCanBeProxied($class);
+
preg_match_all('(<([a-zA-Z]+)>)', $this->proxyClassTemplate, $placeholderMatches);
$placeholderMatches = array_combine($placeholderMatches[0], $placeholderMatches[1]);
rename($tmpFileName, $fileName);
}
+ /**
+ * @param ClassMetadata $class
+ *
+ * @throws InvalidArgumentException
+ */
+ private function verifyClassCanBeProxied(ClassMetadata $class)
+ {
+ if ($class->getReflectionClass()->isFinal()) {
+ throw InvalidArgumentException::classMustNotBeFinal($class->getName());
+ }
+
+ if ($class->getReflectionClass()->isAbstract()) {
+ throw InvalidArgumentException::classMustNotBeAbstract($class->getName());
+ }
+ }
+
/**
* Generates the proxy short class name to be used in the template.
*
$methods .= '&';
}
- $methods .= $name . '(' . $this->buildParametersString($class, $method, $method->getParameters()) . ')';
+ $methods .= $name . '(' . $this->buildParametersString($method->getParameters()) . ')';
$methods .= $this->getMethodReturnType($method);
$methods .= "\n" . ' {' . "\n";
}
/**
- * @param ClassMetadata $class
- * @param \ReflectionMethod $method
* @param \ReflectionParameter[] $parameters
*
* @return string
*/
- private function buildParametersString(ClassMetadata $class, \ReflectionMethod $method, array $parameters)
+ private function buildParametersString(array $parameters)
{
$parameterDefinitions = [];
foreach ($parameters as $param) {
$parameterDefinition = '';
- if ($parameterType = $this->getParameterType($class, $method, $param)) {
+ if ($parameterType = $this->getParameterType($param)) {
$parameterDefinition .= $parameterType . ' ';
}
$parameterDefinition .= '&';
}
- if (method_exists($param, 'isVariadic') && $param->isVariadic()) {
+ if ($param->isVariadic()) {
$parameterDefinition .= '...';
}
- $parameters[] = '$' . $param->getName();
$parameterDefinition .= '$' . $param->getName();
if ($param->isDefaultValueAvailable()) {
*
* @return string|null
*/
- private function getParameterType(ClassMetadata $class, \ReflectionMethod $method, \ReflectionParameter $parameter)
+ private function getParameterType(\ReflectionParameter $parameter)
{
- if (method_exists($parameter, 'hasType')) {
- if ( ! $parameter->hasType()) {
- return '';
- }
-
- return $this->formatType($parameter->getType(), $parameter->getDeclaringFunction(), $parameter);
- }
-
- // For PHP 5.x, we need to pick the type hint in the old way (to be removed for PHP 7.0+)
- if ($parameter->isArray()) {
- return 'array';
- }
-
- if ($parameter->isCallable()) {
- return 'callable';
+ if ( ! $parameter->hasType()) {
+ return null;
}
- try {
- $parameterClass = $parameter->getClass();
-
- if ($parameterClass) {
- return '\\' . $parameterClass->getName();
- }
- } catch (\ReflectionException $previous) {
- throw UnexpectedValueException::invalidParameterTypeHint(
- $class->getName(),
- $method->getName(),
- $parameter->getName(),
- $previous
- );
- }
-
- return null;
+ return $this->formatType($parameter->getType(), $parameter->getDeclaringFunction(), $parameter);
}
/**
function (\ReflectionParameter $parameter) {
$name = '';
- if (method_exists($parameter, 'isVariadic') && $parameter->isVariadic()) {
+ if ($parameter->isVariadic()) {
$name .= '...';
}
}
/**
- * @Param \ReflectionMethod $method
+ * @param \ReflectionMethod $method
*
* @return string
*/
private function getMethodReturnType(\ReflectionMethod $method)
{
- if ( ! method_exists($method, 'hasReturnType') || ! $method->hasReturnType()) {
+ if ( ! $method->hasReturnType()) {
return '';
}
*/
private function shouldProxiedMethodReturn(\ReflectionMethod $method)
{
- if ( ! method_exists($method, 'hasReturnType') || ! $method->hasReturnType()) {
+ if ( ! $method->hasReturnType()) {
return true;
}
\ReflectionMethod $method,
\ReflectionParameter $parameter = null
) {
- $name = method_exists($type, 'getName') ? $type->getName() : (string) $type;
+ $name = (string) $type;
$nameLower = strtolower($name);
if ('self' === $nameLower) {
*/
protected $classAnnotationOptimize;
+ /**
+ * A ClassFinder object which finds the class.
+ *
+ * @var ClassFinderInterface
+ */
+ protected $finder;
+
/**
* Whether the parser has run.
*
/**
* The docComment of the class.
*
- * @var string
+ * @var mixed[]
*/
protected $docComment = [
'class' => '',
$last_token = false;
while ($token = $tokenParser->next(false)) {
- if (is_array($token)) {switch ($token[0]) {
+ switch ($token[0]) {
case T_USE:
$this->useStatements = array_merge($this->useStatements, $tokenParser->parseUseStatement());
break;
$docComment = $token[1];
break;
case T_CLASS:
- if ($last_token !== T_PAAMAYIM_NEKUDOTAYIM) {$this->docComment['class'] = $docComment;
- $docComment = '';}
+ if ($last_token !== T_PAAMAYIM_NEKUDOTAYIM) {
+ $this->docComment['class'] = $docComment;
+ $docComment = '';
+ }
break;
case T_VAR:
case T_PRIVATE:
if (!$fullySpecified) {
$this->parentClassName = '\\' . $this->namespace . '\\' . $this->parentClassName;
}
- break;}
+ break;
}
$last_token = $token[0];
*
* @param object $object
*
- * @return \ReflectionObject
+ * @return \ReflectionClass
*/
public static function newReflectionObject($object)
{
/**
* Fill the $return variable with class attributes
+ * Based on obj2array function from {@see http://php.net/manual/en/function.get-object-vars.php#47075}
*
* @param object $var
- * @param stdClass $return
+ * @param \stdClass $return
* @param int $maxDepth
*
* @return mixed
*/
private static function fillReturnWithClassAttributes($var, \stdClass $return, $maxDepth)
{
- $reflClass = ClassUtils::newReflectionObject($var);
- $parsedAttributes = array();
- do {
- $currentClassName = $reflClass->getName();
-
- foreach ($reflClass->getProperties() as $reflProperty) {
- $attributeKey = $reflProperty->isPrivate() ? $currentClassName . '#' : '';
- $attributeKey .= $reflProperty->getName();
-
- if (isset($parsedAttributes[$attributeKey])) {
- continue;
- }
-
- $parsedAttributes[$attributeKey] = true;
-
- $name =
- $reflProperty->getName()
- . ($return->__CLASS__ !== $currentClassName || $reflProperty->isPrivate() ? ':' . $currentClassName : '')
- . ($reflProperty->isPrivate() ? ':private' : '')
- . ($reflProperty->isProtected() ? ':protected' : '')
- ;
-
- $reflProperty->setAccessible(true);
- $return->$name = self::export($reflProperty->getValue($var), $maxDepth - 1);
+ $clone = (array) $var;
+
+ foreach (array_keys($clone) as $key) {
+ $aux = explode("\0", $key);
+ $name = end($aux);
+ if ($aux[0] === '') {
+ $name.= ':' . ($aux[1] === '*' ? 'protected' : $aux[1].':private');
}
- } while ($reflClass = $reflClass->getParentClass());
+ $return->$name = self::export($clone[$key], $maxDepth - 1);;
+ }
return $return;
}
/**
* Current Doctrine Version.
*/
- const VERSION = '2.7.3';
+ const VERSION = '2.8.1';
/**
* Compares a Doctrine version with the current one.
--- /dev/null
+parameters:
+ autoload_directories:
+ - %currentWorkingDirectory%/tests/Doctrine/Tests/Common/ClassLoaderTest
+ - %currentWorkingDirectory%/tests/Doctrine/Tests/Common/Proxy/generated
+ autoload_files:
+ - %currentWorkingDirectory%/tests/Doctrine/Tests/Common/Persistence/ObjectManagerDecoratorTest.php
+ - %currentWorkingDirectory%/tests/Doctrine/Tests/Common/Persistence/PersistentObjectTest.php
+ - %currentWorkingDirectory%/tests/Doctrine/Tests/Common/Persistence/Mapping/ClassMetadataFactoryTest.php
+ - %currentWorkingDirectory%/tests/Doctrine/Tests/Common/Persistence/Mapping/_files/annotation/TestClass.php
+ excludes_analyse:
+ - %currentWorkingDirectory%/lib/vendor/doctrine-build-common
+ - %currentWorkingDirectory%/tests/Doctrine/Tests/Common/Proxy/InvalidReturnTypeClass.php
+ - %currentWorkingDirectory%/tests/Doctrine/Tests/Common/Proxy/InvalidTypeHintClass.php
+ - %currentWorkingDirectory%/tests/Doctrine/Tests/Common/Persistence/Mapping/_files/TestEntity.php
+ ignoreErrors:
+ - '#Doctrine\\Common\\Reflection\\StaticReflection[a-zA-Z0-9_]+::__construct\(\) does not call parent constructor from Reflection[a-zA-Z0-9_]+#'
+ - '#Call to an undefined method Doctrine\\Tests\\Common\\Persistence\\TestObject#'
+ - '#Access to an undefined property Doctrine\\Common\\Proxy\\Proxy::\$publicField#'
+ - '#Access to an undefined property Doctrine\\Tests\\Common\\Proxy\\MagicGetByRefClass::\$nonExisting#'
+ - '#does not accept [\\a-zA-Z0-9_|]*PHPUnit_Framework_MockObject_MockObject#'
{"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"}
],
"require": {
- "php": "^7.0"
+ "php": "^7.1"
},
"require-dev": {
"phpunit/phpunit": "^6.2"
"psr-4": { "Doctrine\\Common\\Inflector\\": "lib/Doctrine/Common/Inflector" }
},
"autoload-dev": {
- "psr-4": { "Doctrine\\Tests\\Common\\Inflector\\": "tests/Doctrine/Common/Inflector" }
+ "psr-4": { "Doctrine\\Tests\\Common\\Inflector\\": "tests/Doctrine/Tests/Common/Inflector" }
},
"extra": {
"branch-alias": {
- "dev-master": "1.2.x-dev"
+ "dev-master": "1.3.x-dev"
}
}
}
/**
* Plural inflector rules.
*
- * @var array
+ * @var string[][]
*/
private static $plural = array(
'rules' => array(
'/(?:([^f])fe|([lr])f)$/i' => '\1\2ves',
'/sis$/i' => 'ses',
'/([ti])um$/i' => '\1a',
+ '/(c)riterion$/i' => '\1riteria',
'/(p)erson$/i' => '\1eople',
'/(m)an$/i' => '\1en',
'/(c)hild$/i' => '\1hildren',
'ganglion' => 'ganglions',
'genie' => 'genies',
'genus' => 'genera',
+ 'goose' => 'geese',
'graffito' => 'graffiti',
'hippopotamus' => 'hippopotami',
'hoof' => 'hoofs',
'tornado' => 'tornadoes',
'trilby' => 'trilbys',
'turf' => 'turfs',
+ 'valve' => 'valves',
'volcano' => 'volcanoes',
)
);
/**
* Singular inflector rules.
*
- * @var array
+ * @var string[][]
*/
private static $singular = array(
'rules' => array(
'/(hive)s$/i' => '\1',
'/(drive)s$/i' => '\1',
'/(dive)s$/i' => '\1',
+ '/(olive)s$/i' => '\1',
'/([^fo])ves$/i' => '\1fe',
'/(^analy)ses$/i' => '\1sis',
'/(analy|diagno|^ba|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\1\2sis',
+ '/(c)riteria$/i' => '\1riterion',
'/([ti])a$/i' => '\1um',
'/(p)eople$/i' => '\1\2erson',
'/(m)en$/i' => '\1an',
'.*pox',
'.*sheep',
'.*ss',
+ 'data',
'police',
'pants',
'clothes',
),
'irregular' => array(
- 'caches' => 'cache',
- 'criteria' => 'criterion',
- 'curves' => 'curve',
- 'emphases' => 'emphasis',
- 'foes' => 'foe',
- 'hoaxes' => 'hoax',
- 'media' => 'medium',
- 'neuroses' => 'neurosis',
- 'waves' => 'wave',
- 'oases' => 'oasis',
+ 'abuses' => 'abuse',
+ 'avalanches' => 'avalanche',
+ 'caches' => 'cache',
+ 'criteria' => 'criterion',
+ 'curves' => 'curve',
+ 'emphases' => 'emphasis',
+ 'foes' => 'foe',
+ 'geese' => 'goose',
+ 'graves' => 'grave',
+ 'hoaxes' => 'hoax',
+ 'media' => 'medium',
+ 'neuroses' => 'neurosis',
+ 'waves' => 'wave',
+ 'oases' => 'oasis',
+ 'valves' => 'valve',
)
);
* @var array
*/
private static $uninflected = array(
- 'Amoyese', 'bison', 'Borghese', 'bream', 'breeches', 'britches', 'buffalo', 'cantus',
- 'carp', 'chassis', 'clippers', 'cod', 'coitus', 'Congoese', 'contretemps', 'corps',
- 'debris', 'diabetes', 'djinn', 'eland', 'elk', 'equipment', 'Faroese', 'flounder',
- 'Foochowese', 'Furniture', 'gallows', 'Genevese', 'Genoese', 'Gilbertese', 'graffiti',
- 'headquarters', 'herpes', 'hijinks', 'Hottentotese', 'information', 'innings',
- 'jackanapes', 'Kiplingese', 'Kongoese', 'Lucchese', 'Luggage', 'mackerel', 'Maltese', '.*?media',
- 'mews', 'moose', 'mumps', 'Nankingese', 'news', 'nexus', 'Niasese',
- 'Pekingese', 'Piedmontese', 'pincers', 'Pistoiese', 'pliers', 'Portuguese',
- 'proceedings', 'rabies', 'rice', 'rhinoceros', 'salmon', 'Sarawakese', 'scissors',
- 'sea[- ]bass', 'series', 'Shavese', 'shears', 'siemens', 'species', 'staff', 'swine',
- 'testes', 'trousers', 'trout', 'tuna', 'Vermontese', 'Wenchowese', 'whiting',
- 'wildebeest', 'Yengeese'
+ '.*?media', 'Amoyese', 'audio', 'bison', 'Borghese', 'bream', 'breeches',
+ 'britches', 'buffalo', 'cantus', 'carp', 'chassis', 'clippers', 'cod', 'coitus', 'compensation', 'Congoese',
+ 'contretemps', 'coreopsis', 'corps', 'data', 'debris', 'deer', 'diabetes', 'djinn', 'education', 'eland',
+ 'elk', 'emoji', 'equipment', 'evidence', 'Faroese', 'feedback', 'fish', 'flounder', 'Foochowese',
+ 'Furniture', 'furniture', 'gallows', 'Genevese', 'Genoese', 'Gilbertese', 'gold',
+ 'headquarters', 'herpes', 'hijinks', 'Hottentotese', 'information', 'innings', 'jackanapes', 'jedi',
+ 'Kiplingese', 'knowledge', 'Kongoese', 'love', 'Lucchese', 'Luggage', 'mackerel', 'Maltese', 'metadata',
+ 'mews', 'moose', 'mumps', 'Nankingese', 'news', 'nexus', 'Niasese', 'nutrition', 'offspring',
+ 'Pekingese', 'Piedmontese', 'pincers', 'Pistoiese', 'plankton', 'pliers', 'pokemon', 'police', 'Portuguese',
+ 'proceedings', 'rabies', 'rain', 'rhinoceros', 'rice', 'salmon', 'Sarawakese', 'scissors', 'sea[- ]bass',
+ 'series', 'Shavese', 'shears', 'sheep', 'siemens', 'species', 'staff', 'swine', 'traffic',
+ 'trousers', 'trout', 'tuna', 'us', 'Vermontese', 'Wenchowese', 'wheat', 'whiting', 'wildebeest', 'Yengeese'
);
/**
/**
* Converts a word into the format for a Doctrine table name. Converts 'ModelName' to 'model_name'.
- *
- * @param string $word The word to tableize.
- *
- * @return string The tableized word.
*/
- public static function tableize($word)
+ public static function tableize(string $word) : string
{
return strtolower(preg_replace('~(?<=\\w)([A-Z])~', '_$1', $word));
}
/**
* Converts a word into the format for a Doctrine class name. Converts 'table_name' to 'TableName'.
- *
- * @param string $word The word to classify.
- *
- * @return string The classified word.
*/
- public static function classify($word)
+ public static function classify(string $word) : string
{
- return str_replace(' ', '', ucwords(strtr($word, '_-', ' ')));
+ return str_replace([' ', '_', '-'], '', ucwords($word, ' _-'));
}
/**
* Camelizes a word. This uses the classify() method and turns the first character to lowercase.
- *
- * @param string $word The word to camelize.
- *
- * @return string The camelized word.
*/
- public static function camelize($word)
+ public static function camelize(string $word) : string
{
return lcfirst(self::classify($word));
}
* Uppercases words with configurable delimeters between words.
*
* Takes a string and capitalizes all of the words, like PHP's built-in
- * ucwords function. This extends that behavior, however, by allowing the
+ * ucwords function. This extends that behavior, however, by allowing the
* word delimeters to be configured, rather than only separating on
* whitespace.
*
*
* @return string The string with all delimeter-separated words capitalized.
*/
- public static function ucwords($string, $delimiters = " \n\t\r\0\x0B-")
+ public static function ucwords(string $string, string $delimiters = " \n\t\r\0\x0B-") : string
{
- return preg_replace_callback(
- '/[^' . preg_quote($delimiters, '/') . ']+/',
- function($matches) {
- return ucfirst($matches[0]);
- },
- $string
- );
+ return ucwords($string, $delimiters);
}
/**
* Clears Inflectors inflected value caches, and resets the inflection
* rules to the initial values.
- *
- * @return void
*/
- public static function reset()
+ public static function reset() : void
{
if (empty(self::$initialState)) {
self::$initialState = get_class_vars('Inflector');
}
foreach (self::$initialState as $key => $val) {
- if ($key != 'initialState') {
+ if ($key !== 'initialState') {
self::${$key} = $val;
}
}
* ));
* }}}
*
- * @param string $type The type of inflection, either 'plural' or 'singular'
- * @param array $rules An array of rules to be added.
- * @param boolean $reset If true, will unset default inflections for all
- * new rules that are being defined in $rules.
+ * @param string $type The type of inflection, either 'plural' or 'singular'
+ * @param array|iterable $rules An array of rules to be added.
+ * @param boolean $reset If true, will unset default inflections for all
+ * new rules that are being defined in $rules.
*
* @return void
*/
- public static function rules($type, $rules, $reset = false)
+ public static function rules(string $type, iterable $rules, bool $reset = false) : void
{
foreach ($rules as $rule => $pattern) {
if ( ! is_array($pattern)) {
*
* @return string The word in plural form.
*/
- public static function pluralize($word)
+ public static function pluralize(string $word) : string
{
if (isset(self::$cache['pluralize'][$word])) {
return self::$cache['pluralize'][$word];
}
if (preg_match('/(.*)\\b(' . self::$plural['cacheIrregular'] . ')$/i', $word, $regs)) {
- self::$cache['pluralize'][$word] = $regs[1] . substr($word, 0, 1) . substr(self::$plural['merged']['irregular'][strtolower($regs[2])], 1);
+ self::$cache['pluralize'][$word] = $regs[1] . $word[0] . substr(self::$plural['merged']['irregular'][strtolower($regs[2])], 1);
return self::$cache['pluralize'][$word];
}
*
* @return string The word in singular form.
*/
- public static function singularize($word)
+ public static function singularize(string $word) : string
{
if (isset(self::$cache['singularize'][$word])) {
return self::$cache['singularize'][$word];
}
if (!isset(self::$singular['cacheUninflected']) || !isset(self::$singular['cacheIrregular'])) {
- self::$singular['cacheUninflected'] = '(?:' . join('|', self::$singular['merged']['uninflected']) . ')';
- self::$singular['cacheIrregular'] = '(?:' . join('|', array_keys(self::$singular['merged']['irregular'])) . ')';
+ self::$singular['cacheUninflected'] = '(?:' . implode('|', self::$singular['merged']['uninflected']) . ')';
+ self::$singular['cacheIrregular'] = '(?:' . implode('|', array_keys(self::$singular['merged']['irregular'])) . ')';
}
if (preg_match('/(.*)\\b(' . self::$singular['cacheIrregular'] . ')$/i', $word, $regs)) {
- self::$cache['singularize'][$word] = $regs[1] . substr($word, 0, 1) . substr(self::$singular['merged']['irregular'][strtolower($regs[2])], 1);
+ self::$cache['singularize'][$word] = $regs[1] . $word[0] . substr(self::$singular['merged']['irregular'][strtolower($regs[2])], 1);
return self::$cache['singularize'][$word];
}
-# CHANGELOG
+# Change Log
+
+## 6.3.2 - 2018-03-26
+
+* Fix: Release process
+
+
+## 6.3.1 - 2018-03-26
+
+* Bug fix: Parsing 0 epoch expiry times in cookies [#2014](https://github.com/guzzle/guzzle/pull/2014)
+* Improvement: Better ConnectException detection [#2012](https://github.com/guzzle/guzzle/pull/2012)
+* Bug fix: Malformed domain that contains a "/" [#1999](https://github.com/guzzle/guzzle/pull/1999)
+* Bug fix: Undefined offset when a cookie has no first key-value pair [#1998](https://github.com/guzzle/guzzle/pull/1998)
+* Improvement: Support PHPUnit 6 [#1953](https://github.com/guzzle/guzzle/pull/1953)
+* Bug fix: Support empty headers [#1915](https://github.com/guzzle/guzzle/pull/1915)
+* Bug fix: Ignore case during header modifications [#1916](https://github.com/guzzle/guzzle/pull/1916)
+
++ Minor code cleanups, documentation fixes and clarifications.
+
## 6.3.0 - 2017-06-22
-Copyright (c) 2011-2016 Michael Dowling, https://github.com/mtdowling <mtdowling@gmail.com>
+Copyright (c) 2011-2018 Michael Dowling, https://github.com/mtdowling <mtdowling@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Guzzle, PHP HTTP client
=======================
-[![Build Status](https://travis-ci.org/guzzle/guzzle.svg?branch=master)](https://travis-ci.org/guzzle/guzzle)
+[![Latest Version](https://img.shields.io/github/release/guzzle/guzzle.svg?style=flat-square)](https://github.com/guzzle/guzzle/releases)
+[![Build Status](https://img.shields.io/travis/guzzle/guzzle.svg?style=flat-square)](https://travis-ci.org/guzzle/guzzle)
+[![Total Downloads](https://img.shields.io/packagist/dt/guzzlehttp/guzzle.svg?style=flat-square)](https://packagist.org/packages/guzzlehttp/guzzle)
Guzzle is a PHP HTTP client that makes it easy to send HTTP requests and
trivial to integrate with web services.
},
"require-dev": {
"ext-curl": "*",
- "phpunit/phpunit": "^4.0 || ^5.0",
+ "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4",
"psr/log": "^1.0"
},
"autoload": {
},
"extra": {
"branch-alias": {
- "dev-master": "6.2-dev"
+ "dev-master": "6.3-dev"
}
}
}
*/
private function applyOptions(RequestInterface $request, array &$options)
{
- $modify = [];
+ $modify = [
+ 'set_headers' => [],
+ ];
+
+ if (isset($options['headers'])) {
+ $modify['set_headers'] = $options['headers'];
+ unset($options['headers']);
+ }
if (isset($options['form_params'])) {
if (isset($options['multipart'])) {
}
$options['body'] = http_build_query($options['form_params'], '', '&');
unset($options['form_params']);
+ // Ensure that we don't have the header in different case and set the new value.
+ $options['_conditional'] = Psr7\_caseless_remove(['Content-Type'], $options['_conditional']);
$options['_conditional']['Content-Type'] = 'application/x-www-form-urlencoded';
}
if (isset($options['json'])) {
$options['body'] = \GuzzleHttp\json_encode($options['json']);
unset($options['json']);
+ // Ensure that we don't have the header in different case and set the new value.
+ $options['_conditional'] = Psr7\_caseless_remove(['Content-Type'], $options['_conditional']);
$options['_conditional']['Content-Type'] = 'application/json';
}
if (!empty($options['decode_content'])
&& $options['decode_content'] !== true
) {
+ // Ensure that we don't have the header in different case and set the new value.
+ $options['_conditional'] = Psr7\_caseless_remove(['Accept-Encoding'], $modify['set_headers']);
$modify['set_headers']['Accept-Encoding'] = $options['decode_content'];
}
- if (isset($options['headers'])) {
- if (isset($modify['set_headers'])) {
- $modify['set_headers'] = $options['headers'] + $modify['set_headers'];
- } else {
- $modify['set_headers'] = $options['headers'];
- }
- unset($options['headers']);
- }
-
if (isset($options['body'])) {
if (is_array($options['body'])) {
$this->invalidBody();
$type = isset($value[2]) ? strtolower($value[2]) : 'basic';
switch ($type) {
case 'basic':
+ // Ensure that we don't have the header in different case and set the new value.
+ $modify['set_headers'] = Psr7\_caseless_remove(['Authorization'], $modify['set_headers']);
$modify['set_headers']['Authorization'] = 'Basic '
. base64_encode("$value[0]:$value[1]");
break;
$request = Psr7\modify_request($request, $modify);
if ($request->getBody() instanceof Psr7\MultipartStream) {
// Use a multipart/form-data POST if a Content-Type is not set.
+ // Ensure that we don't have the header in different case and set the new value.
+ $options['_conditional'] = Psr7\_caseless_remove(['Content-Type'], $options['_conditional']);
$options['_conditional']['Content-Type'] = 'multipart/form-data; boundary='
. $request->getBody()->getBoundary();
}
*/
interface ClientInterface
{
- const VERSION = '6.2.1';
+ const VERSION = '6.3.2';
/**
* Send an HTTP request.
public function getCookieByName($name)
{
// don't allow a null name
- if($name === null) {
+ if ($name === null) {
return null;
}
- foreach($this->cookies as $cookie) {
- if($cookie->getName() !== null && strcasecmp($cookie->getName(), $name) === 0) {
+ foreach ($this->cookies as $cookie) {
+ if ($cookie->getName() !== null && strcasecmp($cookie->getName(), $name) === 0) {
return $cookie;
}
}
/**
* Create a new SessionCookieJar object
*
- * @param string $sessionKey Session key name to store the cookie
+ * @param string $sessionKey Session key name to store the cookie
* data in session
* @param bool $storeSessionCookies Set to true to store session cookies
* in the cookie jar.
$data = self::$defaults;
// Explode the cookie string using a series of semicolons
$pieces = array_filter(array_map('trim', explode(';', $cookie)));
- // The name of the cookie (first kvp) must include an equal sign.
- if (empty($pieces) || !strpos($pieces[0], '=')) {
+ // The name of the cookie (first kvp) must exist and include an equal sign.
+ if (empty($pieces[0]) || !strpos($pieces[0], '=')) {
return new self($data);
}
// Add the cookie pieces into the parsed data array
foreach ($pieces as $part) {
-
$cookieParts = explode('=', $part, 2);
$key = trim($cookieParts[0]);
$value = isset($cookieParts[1])
return false;
}
- return (bool) preg_match('/\.' . preg_quote($cookieDomain) . '$/', $domain);
+ return (bool) preg_match('/\.' . preg_quote($cookieDomain, '/') . '$/', $domain);
}
/**
*/
public function isExpired()
{
- return $this->getExpires() && time() > $this->getExpires();
+ return $this->getExpires() !== null && time() > $this->getExpires();
}
/**
<?php
namespace GuzzleHttp\Exception;
+/**
+ * @method string getMessage()
+ * @method \Throwable|null getPrevious()
+ * @method mixed getCode()
+ * @method string getFile()
+ * @method int getLine()
+ * @method array getTrace()
+ * @method string getTraceAsString()
+ */
interface GuzzleException {}
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Promise\FulfilledPromise;
-use GuzzleHttp\Promise\RejectedPromise;
use GuzzleHttp\Psr7;
use GuzzleHttp\Psr7\LazyOpenStream;
use GuzzleHttp\TransferStats;
{
foreach ($conf['_headers'] as $name => $values) {
foreach ($values as $value) {
- $conf[CURLOPT_HTTPHEADER][] = "$name: $value";
+ $value = (string) $value;
+ if ($value === '') {
+ // cURL requires a special format for empty headers.
+ // See https://github.com/guzzle/guzzle/issues/1882 for more details.
+ $conf[CURLOPT_HTTPHEADER][] = "$name;";
+ } else {
+ $conf[CURLOPT_HTTPHEADER][] = "$name: $value";
+ }
}
}
if (isset($options['force_ip_resolve'])) {
if ('v4' === $options['force_ip_resolve']) {
$conf[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V4;
- } else if ('v6' === $options['force_ip_resolve']) {
+ } elseif ('v6' === $options['force_ip_resolve']) {
$conf[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V6;
}
}
$promise = new Promise(
[$this, 'execute'],
- function () use ($id) { return $this->cancel($id); }
+ function () use ($id) {
+ return $this->cancel($id);
+ }
);
$this->addRequest(['easy' => $easy, 'deferred' => $promise]);
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Promise\FulfilledPromise;
-use GuzzleHttp\Promise\RejectedPromise;
use GuzzleHttp\Promise\PromiseInterface;
use GuzzleHttp\Psr7;
use GuzzleHttp\TransferStats;
if (strpos($message, 'getaddrinfo') // DNS lookup failed
|| strpos($message, 'Connection refused')
|| strpos($message, "couldn't connect to host") // error on HHVM
+ || strpos($message, "connection attempt failed")
) {
$e = new ConnectException($e->getMessage(), $request, $e);
}
$status = $parts[1];
$reason = isset($parts[2]) ? $parts[2] : null;
$headers = \GuzzleHttp\headers_from_lines($hdrs);
- list ($stream, $headers) = $this->checkDecode($options, $headers, $stream);
+ list($stream, $headers) = $this->checkDecode($options, $headers, $stream);
$stream = Psr7\stream_for($stream);
$sink = $stream;
}
$params = [];
- $context = $this->getDefaultContext($request, $options);
+ $context = $this->getDefaultContext($request);
if (isset($options['on_headers']) && !is_callable($options['on_headers'])) {
throw new \InvalidArgumentException('on_headers must be callable');
&& isset($options['auth'][2])
&& 'ntlm' == $options['auth'][2]
) {
-
throw new \InvalidArgumentException('Microsoft NTLM authentication only supported with curl handler');
}
* Creates a default handler stack that can be used by clients.
*
* The returned handler will wrap the provided handler or use the most
- * appropriate default handler for you system. The returned HandlerStack has
+ * appropriate default handler for your system. The returned HandlerStack has
* support for cookies, redirects, HTTP error exceptions, and preparing a body
* before sending.
*
* - {host}: Host of the request
* - {method}: Method of the request
* - {uri}: URI of the request
- * - {host}: Host of the request
* - {version}: Protocol version
* - {target}: Request target of the request (path + query + fragment)
* - {hostname}: Hostname of the machine that sent the request
return preg_replace_callback(
'/{\s*([A-Za-z_\-\.0-9]+)\s*}/',
function (array $matches) use ($request, $response, $error, &$cache) {
-
if (isset($cache[$matches[1]])) {
return $cache[$matches[1]];
}
$cookieJar = $options['cookies'];
$request = $cookieJar->withCookieHeader($request);
return $handler($request, $options)
- ->then(function ($response) use ($cookieJar, $request) {
- $cookieJar->extractCookies($request, $response);
- return $response;
- }
+ ->then(
+ function ($response) use ($cookieJar, $request) {
+ $cookieJar->extractCookies($request, $response);
+ return $response;
+ }
);
};
};
$useQuery = self::$operatorHash[$parsed['operator']]['query'];
foreach ($parsed['values'] as $value) {
-
if (!isset($this->variables[$value['value']])) {
continue;
}
$expanded = '';
if (is_array($variable)) {
-
$isAssoc = $this->isAssoc($variable);
$kvp = [];
foreach ($variable as $key => $var) {
-
if ($isAssoc) {
$key = rawurlencode($key);
$isNestedArray = is_array($var);
}
$expanded = implode(',', $kvp);
}
-
} else {
if ($value['modifier'] === ':') {
$variable = substr($variable, 0, $value['position']);
$data = \json_decode($json, $assoc, $depth, $options);
if (JSON_ERROR_NONE !== json_last_error()) {
throw new \InvalidArgumentException(
- 'json_decode error: ' . json_last_error_msg());
+ 'json_decode error: ' . json_last_error_msg()
+ );
}
return $data;
$json = \json_encode($value, $options, $depth);
if (JSON_ERROR_NONE !== json_last_error()) {
throw new \InvalidArgumentException(
- 'json_encode error: ' . json_last_error_msg());
+ 'json_encode error: ' . json_last_error_msg()
+ );
}
return $json;
env:
- TWIG_EXT=no
- - TWIG_EXT=yes
before_install:
# turn off XDebug
- php: 5.3
dist: precise
env: TWIG_EXT=no
- exclude:
- - php: 7.0
+ - php: 5.4
env: TWIG_EXT=yes
- - php: 7.1
+ - php: 5.5
env: TWIG_EXT=yes
- - php: 7.2
- env: TWIG_EXT=yes
- - php: nightly
+ - php: 5.6
env: TWIG_EXT=yes
+* 1.35.3 (2018-03-20)
+
+ * fixed block names unicity
+ * fixed counting children of SimpleXMLElement objects
+ * added missing else clause to avoid infinite loops
+ * fixed .. (range operator) in sandbox policy
+
+* 1.35.2 (2018-03-03)
+
+ * fixed a regression in the way the profiler is registered in templates
+
* 1.35.1 (2018-03-02)
* added an exception when using "===" instead of "same as"
"php": ">=5.3.3"
},
"require-dev": {
- "symfony/phpunit-bridge": "~3.3@dev",
- "symfony/debug": "~2.7",
+ "symfony/phpunit-bridge": "^3.3",
+ "symfony/debug": "^2.7",
"psr/container": "^1.0"
},
"autoload": {
#ifndef PHP_TWIG_H
#define PHP_TWIG_H
-#define PHP_TWIG_VERSION "1.35.1"
+#define PHP_TWIG_VERSION "1.35.3"
#include "php.h"
*/
class Twig_Environment
{
- const VERSION = '1.35.1';
- const VERSION_ID = 13501;
+ const VERSION = '1.35.3';
+ const VERSION_ID = 13503;
const MAJOR_VERSION = 1;
const MINOR_VERSION = 35;
- const RELEASE_VERSION = 1;
+ const RELEASE_VERSION = 3;
const EXTRA_VERSION = '';
protected $charset;
// For BC
if (is_string($this->originalCache)) {
$r = new ReflectionMethod($this, 'writeCacheFile');
- if ($r->getDeclaringClass()->getName() !== __CLASS__) {
+ if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
@trigger_error('The Twig_Environment::writeCacheFile method is deprecated since version 1.22 and will be removed in Twig 2.0.', E_USER_DEPRECATED);
$this->bcWriteCacheFile = true;
}
$r = new ReflectionMethod($this, 'getCacheFilename');
- if ($r->getDeclaringClass()->getName() !== __CLASS__) {
+ if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
@trigger_error('The Twig_Environment::getCacheFilename method is deprecated since version 1.22 and will be removed in Twig 2.0.', E_USER_DEPRECATED);
$this->bcGetCacheFilename = true;
break;
}
+ // no break
default:
if ($token->test(Twig_Token::PUNCTUATION_TYPE, '[')) {
$node = $this->parseArrayExpression();
} elseif ($token->test(Twig_Token::PUNCTUATION_TYPE, '{')) {
$node = $this->parseHashExpression();
- } elseif ($token->test(Twig_Token::OPERATOR_TYPE, '=') && ($this->parser->getStream()->look(-1)->getValue() === '==' || $this->parser->getStream()->look(-1)->getValue() === '!=')) {
+ } elseif ($token->test(Twig_Token::OPERATOR_TYPE, '=') && ('==' === $this->parser->getStream()->look(-1)->getValue() || '!=' === $this->parser->getStream()->look(-1)->getValue())) {
throw new Twig_Error_Syntax(sprintf('Unexpected operator of value "%s". Did you try to use "===" or "!==" for strict comparison? Use "is same as(value)" instead.', $token->getValue()), $token->getLine(), $this->parser->getStream()->getSourceContext());
} else {
throw new Twig_Error_Syntax(sprintf('Unexpected token "%s" of value "%s".', Twig_Token::typeToEnglish($token->getType()), $token->getValue()), $token->getLine(), $this->parser->getStream()->getSourceContext());
{
while (true) {
$token = $this->parser->getCurrentToken();
- if ($token->getType() == Twig_Token::PUNCTUATION_TYPE) {
+ if (Twig_Token::PUNCTUATION_TYPE == $token->getType()) {
if ('.' == $token->getValue() || '[' == $token->getValue()) {
$node = $this->parseSubscriptExpression($node);
} elseif ('|' == $token->getValue()) {
$lineno = $token->getLine();
$arguments = new Twig_Node_Expression_Array(array(), $lineno);
$type = Twig_Template::ANY_CALL;
- if ($token->getValue() == '.') {
+ if ('.' == $token->getValue()) {
$token = $stream->next();
if (
- $token->getType() == Twig_Token::NAME_TYPE
+ Twig_Token::NAME_TYPE == $token->getType()
||
- $token->getType() == Twig_Token::NUMBER_TYPE
+ Twig_Token::NUMBER_TYPE == $token->getType()
||
- ($token->getType() == Twig_Token::OPERATOR_TYPE && preg_match(Twig_Lexer::REGEX_NAME, $token->getValue()))
+ (Twig_Token::OPERATOR_TYPE == $token->getType() && preg_match(Twig_Lexer::REGEX_NAME, $token->getValue()))
) {
$arg = new Twig_Node_Expression_Constant($token->getValue(), $lineno);
if ($start >= 0 && $length >= 0 && $item instanceof Iterator) {
try {
- return iterator_to_array(new LimitIterator($item, $start, $length === null ? -1 : $length), $preserveKeys);
+ return iterator_to_array(new LimitIterator($item, $start, null === $length ? -1 : $length), $preserveKeys);
} catch (OutOfBoundsException $exception) {
return array();
}
* The following replaces characters undefined in HTML with the
* hex entity for the Unicode replacement character.
*/
- if (($ord <= 0x1f && $chr != "\t" && $chr != "\n" && $chr != "\r") || ($ord >= 0x7f && $ord <= 0x9f)) {
+ if (($ord <= 0x1f && "\t" != $chr && "\n" != $chr && "\r" != $chr) || ($ord >= 0x7f && $ord <= 0x9f)) {
return '�';
}
* Check if the current character to escape has a name entity we should
* replace it with while grabbing the hex value of the character.
*/
- if (strlen($chr) == 1) {
+ if (1 == strlen($chr)) {
$hex = strtoupper(substr('00'.bin2hex($chr), -2));
} else {
$chr = twig_convert_encoding($chr, 'UTF-16BE', 'UTF-8');
return mb_strlen($thing, $env->getCharset());
}
+ if ($thing instanceof \SimpleXMLElement) {
+ return count($thing);
+ }
+
if (is_object($thing) && method_exists($thing, '__toString') && !$thing instanceof \Countable) {
return mb_strlen((string) $thing, $env->getCharset());
}
if ($thing instanceof \Countable || is_array($thing)) {
return count($thing);
}
-
+
if ($thing instanceof \IteratorAggregate) {
return iterator_count($thing);
}
return strlen($thing);
}
+ if ($thing instanceof \SimpleXMLElement) {
+ return count($thing);
+ }
+
if (is_object($thing) && method_exists($thing, '__toString') && !$thing instanceof \Countable) {
return strlen((string) $thing);
}
if ($thing instanceof \Countable || is_array($thing)) {
return count($thing);
}
-
+
if ($thing instanceof \IteratorAggregate) {
return iterator_count($thing);
}
$this->moveCursor($match[0]);
if ($this->cursor >= $this->end) {
- throw new Twig_Error_Syntax(sprintf('Unclosed "%s".', $this->state === self::STATE_BLOCK ? 'block' : 'variable'), $this->currentVarBlockLine, $this->source);
+ throw new Twig_Error_Syntax(sprintf('Unclosed "%s".', self::STATE_BLOCK === $this->state ? 'block' : 'variable'), $this->currentVarBlockLine, $this->source);
}
}
$this->moveCursor($match[0]);
} elseif (preg_match(self::REGEX_DQ_STRING_DELIM, $this->code, $match, null, $this->cursor)) {
list($expect, $lineno) = array_pop($this->brackets);
- if ($this->code[$this->cursor] != '"') {
+ if ('"' != $this->code[$this->cursor]) {
throw new Twig_Error_Syntax(sprintf('Unclosed "%s".', $expect), $lineno, $this->source);
}
$this->popState();
++$this->cursor;
+ } else {
+ // unlexable
+ throw new Twig_Error_Syntax(sprintf('Unexpected character "%s".', $this->code[$this->cursor]), $this->lineno, $this->source);
}
}
{
return strspn($file, '/\\', 0, 1)
|| (strlen($file) > 3 && ctype_alpha($file[0])
- && substr($file, 1, 1) === ':'
+ && ':' === substr($file, 1, 1)
&& strspn($file, '/\\', 2, 1)
)
|| null !== parse_url($file, PHP_URL_SCHEME)
if (PHP_VERSION_ID < 50400 && self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) && !$env->isStrictVariables() && !$env->hasExtension('Twig_Extension_Sandbox')) {
if ($this->inABody) {
if (!$node instanceof Twig_Node_Expression) {
- if (get_class($node) !== 'Twig_Node') {
+ if ('Twig_Node' !== get_class($node)) {
array_unshift($this->prependedNodes, array());
}
} else {
if ($node instanceof Twig_Node_Body) {
$this->inABody = false;
} elseif ($this->inABody) {
- if (!$expression && get_class($node) !== 'Twig_Node' && $prependedNodes = array_shift($this->prependedNodes)) {
+ if (!$expression && 'Twig_Node' !== get_class($node) && $prependedNodes = array_shift($this->prependedNodes)) {
$nodes = array();
foreach (array_unique($prependedNodes) as $name) {
$nodes[] = new Twig_Node_SetTemp($name, $node->getTemplateLine());
$this->functions[$node->getAttribute('name')] = $node;
}
+ // the .. operator is equivalent to the range() function
+ if ($node instanceof Twig_Node_Expression_Binary_Range && !isset($this->functions['range'])) {
+ $this->functions['range'] = $node;
+ }
+
// wrap print to check __toString() calls
if ($node instanceof Twig_Node_Print) {
return new Twig_Node_SandboxedPrint($node->getNode('expr'), $node->getTemplateLine(), $node->getNodeTag());
public function getVarName()
{
- return sprintf('__internal_%s', hash('sha256', __METHOD__.$this->varNameSalt++));
+ return sprintf('__internal_%s', hash('sha256', __METHOD__.$this->stream->getSourceContext()->getCode().$this->varNameSalt++));
}
/**
$this->stream->next();
$token = $this->getCurrentToken();
- if ($token->getType() !== Twig_Token::NAME_TYPE) {
+ if (Twig_Token::NAME_TYPE !== $token->getType()) {
throw new Twig_Error_Syntax('A block must start with a tag name.', $token->getLine(), $this->stream->getSourceContext());
}
private function getVarName()
{
- return sprintf('__internal_%s', hash('sha256', __METHOD__));
+ return sprintf('__internal_%s', hash('sha256', $this->extensionName));
}
public function getPriority()
}
@trigger_error($message, E_USER_DEPRECATED);
- return $ret === '' ? '' : new Twig_Markup($ret, $this->env->getCharset());
+ return '' === $ret ? '' : new Twig_Markup($ret, $this->env->getCharset());
}
return $ret;
$stream->expect(Twig_Token::BLOCK_END_TYPE);
$body = $this->parser->subparse(array($this, 'decideForFork'));
- if ($stream->next()->getValue() == 'else') {
+ if ('else' == $stream->next()->getValue()) {
$stream->expect(Twig_Token::BLOCK_END_TYPE);
$else = $this->parser->subparse(array($this, 'decideForEnd'), true);
} else {
}
}
- public function addTokenParserBroker(Twig_TokenParserBroker $broker)
+ public function addTokenParserBroker(self $broker)
{
$this->brokers[] = $broker;
}
- public function removeTokenParserBroker(Twig_TokenParserBroker $broker)
+ public function removeTokenParserBroker(self $broker)
{
if (false !== $pos = array_search($broker, $this->brokers)) {
unset($this->brokers[$pos]);
*/
public function isEOF()
{
- return $this->tokens[$this->current]->getType() === Twig_Token::EOF_TYPE;
+ return Twig_Token::EOF_TYPE === $this->tokens[$this->current]->getType();
}
/**
'1_layout' => '{% block content %}{% endblock %}',
'1_child' => "{% extends \"1_layout\" %}\n{% block content %}\n{{ \"a\"|json_encode }}\n{% endblock %}",
'1_include' => '{{ include("1_basic1", sandboxed=true) }}',
+ '1_range_operator' => '{{ (1..2)[0] }}',
);
}
}
}
+ public function testSandboxUnallowedRangeOperator()
+ {
+ $twig = $this->getEnvironment(true, array(), self::$templates);
+ try {
+ $twig->loadTemplate('1_range_operator')->render(self::$params);
+ $this->fail('Sandbox throws a SecurityError exception if the unallowed range operator is called');
+ } catch (Twig_Sandbox_SecurityError $e) {
+ $this->assertInstanceOf('Twig_Sandbox_SecurityNotAllowedFunctionError', $e, 'Exception should be an instance of Twig_Sandbox_SecurityNotAllowedFunctionError');
+ $this->assertEquals('range', $e->getFunctionName(), 'Exception should be raised on the "range" function');
+ }
+ }
+
public function testSandboxAllowMethodFoo()
{
$twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array('FooObject' => 'foo'));
$this->assertEquals('bar', $twig->loadTemplate('1_basic7')->render(self::$params), 'Sandbox allow some functions');
}
+ public function testSandboxAllowRangeOperator()
+ {
+ $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array(), array(), array('range'));
+ $this->assertEquals('1', $twig->loadTemplate('1_range_operator')->render(self::$params), 'Sandbox allow the range operator');
+ }
+
public function testSandboxAllowFunctionsCaseInsensitive()
{
foreach (array('getfoobar', 'getFoobar', 'getFooBar') as $name) {
} catch (Throwable $e) {
} catch (Exception $e) {
}
- if ($e === null) {
+ if (null === $e) {
$this->fail('An exception should be thrown for this test to be valid.');
}
{{ null|length }}
{{ magic|length }}
{{ non_countable|length }}
+{{ simple_xml_element|length }}
--DATA--
return array(
'array' => array(1, 4),
'null' => null,
'magic' => new MagicCallStub(), /* used to assert we do *not* call __call */
'non_countable' => new \StdClass(),
+ 'simple_xml_element' => new \SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><doc><elem/><elem/></doc>'),
);
--EXPECT--
2
0
1
1
+2
--- /dev/null
+--TEST--
+Block names are unique per template
+--TEMPLATE--
+{% extends 'layout' %}
+{% block content -%}
+ {% filter title -%}
+ second
+ {% endfilter %}
+{% endblock %}
+--TEMPLATE(layout)--
+{% filter title -%}
+ first
+{% endfilter %}
+{% block content %}{% endblock %}
+--DATA--
+return array();
+--EXPECT--
+First
+Second
}
/**
- * This class is used in tests for the length filter
+ * This class is used in tests for the length filter.
*/
class IteratorAggregateStub implements \IteratorAggregate
{
/**
* The current system version.
*/
- const VERSION = '8.4.5';
+ const VERSION = '8.4.6';
/**
* Core API compatibility.
use Drupal\Core\Http\TrustedHostsRequestFactory;
use Drupal\Core\Installer\InstallerRedirectTrait;
use Drupal\Core\Language\Language;
+use Drupal\Core\Security\RequestSanitizer;
use Drupal\Core\Site\Settings;
use Drupal\Core\Test\TestDatabase;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
* {@inheritdoc}
*/
public function preHandle(Request $request) {
+ // Sanitize the request.
+ $request = RequestSanitizer::sanitize(
+ $request,
+ (array) Settings::get(RequestSanitizer::SANITIZE_WHITELIST, []),
+ (bool) Settings::get(RequestSanitizer::SANITIZE_LOG, FALSE)
+ );
$this->loadLegacyIncludes();
--- /dev/null
+<?php
+
+namespace Drupal\Core\Security;
+
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Sanitizes user input.
+ */
+class RequestSanitizer {
+
+ /**
+ * Request attribute to mark the request as sanitized.
+ */
+ const SANITIZED = '_drupal_request_sanitized';
+
+ /**
+ * The name of the setting that configures the whitelist.
+ */
+ const SANITIZE_WHITELIST = 'sanitize_input_whitelist';
+
+ /**
+ * The name of the setting that determines if sanitized keys are logged.
+ */
+ const SANITIZE_LOG = 'sanitize_input_logging';
+
+ /**
+ * Strips dangerous keys from user input.
+ *
+ * @param \Symfony\Component\HttpFoundation\Request $request
+ * The incoming request to sanitize.
+ * @param string[] $whitelist
+ * An array of keys to whitelist as safe. See default.settings.php.
+ * @param bool $log_sanitized_keys
+ * (optional) Set to TRUE to log an keys that are sanitized.
+ *
+ * @return \Symfony\Component\HttpFoundation\Request
+ * The sanitized request.
+ */
+ public static function sanitize(Request $request, $whitelist, $log_sanitized_keys = FALSE) {
+ if (!$request->attributes->get(self::SANITIZED, FALSE)) {
+ // Process query string parameters.
+ $get_sanitized_keys = [];
+ $request->query->replace(static::stripDangerousValues($request->query->all(), $whitelist, $get_sanitized_keys));
+ if ($log_sanitized_keys && !empty($get_sanitized_keys)) {
+ trigger_error(sprintf('Potentially unsafe keys removed from query string parameters (GET): %s', implode(', ', $get_sanitized_keys)));
+ }
+
+ // Request body parameters.
+ $post_sanitized_keys = [];
+ $request->request->replace(static::stripDangerousValues($request->request->all(), $whitelist, $post_sanitized_keys));
+ if ($log_sanitized_keys && !empty($post_sanitized_keys)) {
+ trigger_error(sprintf('Potentially unsafe keys removed from request body parameters (POST): %s', implode(', ', $post_sanitized_keys)));
+ }
+
+ // Cookie parameters.
+ $cookie_sanitized_keys = [];
+ $request->cookies->replace(static::stripDangerousValues($request->cookies->all(), $whitelist, $cookie_sanitized_keys));
+ if ($log_sanitized_keys && !empty($cookie_sanitized_keys)) {
+ trigger_error(sprintf('Potentially unsafe keys removed from cookie parameters: %s', implode(', ', $cookie_sanitized_keys)));
+ }
+
+ if (!empty($get_sanitized_keys) || !empty($post_sanitized_keys) || !empty($cookie_sanitized_keys)) {
+ $request->overrideGlobals();
+ }
+ $request->attributes->set(self::SANITIZED, TRUE);
+ }
+ return $request;
+ }
+
+ /**
+ * Strips dangerous keys from $input.
+ *
+ * @param mixed $input
+ * The input to sanitize.
+ * @param string[] $whitelist
+ * An array of keys to whitelist as safe.
+ * @param string[] $sanitized_keys
+ * An array of keys that have been removed.
+ *
+ * @return mixed
+ * The sanitized input.
+ */
+ protected static function stripDangerousValues($input, array $whitelist, array &$sanitized_keys) {
+ if (is_array($input)) {
+ foreach ($input as $key => $value) {
+ if ($key !== '' && $key[0] === '#' && !in_array($key, $whitelist, TRUE)) {
+ unset($input[$key]);
+ $sanitized_keys[] = $key;
+ }
+ else {
+ $input[$key] = static::stripDangerousValues($input[$key], $whitelist, $sanitized_keys);
+ }
+ }
+ }
+ return $input;
+ }
+
+}
$available = \Drupal::keyValueExpirable('update_available_releases')->getAll();
}
+ // Check for security releases that are covered under the same security
+ // advisories as the site's current release, and override the update status
+ // data so that those releases are not flagged as needed security updates.
+ // Any security releases beyond those specific releases will still be shown
+ // as required security updates.
+
+ // @todo This is a temporary fix to allow minor-version backports of security
+ // fixes to be shown as secure. It should not be included in the codebase of
+ // any release or branch other than such backports. Replace this with
+ // https://www.drupal.org/project/drupal/issues/2766491.
+ foreach (_update_equivalent_security_releases() as $equivalent_release) {
+ if (!empty($available['drupal']['releases'][$equivalent_release]['terms']['Release type'])) {
+ $security_release_key = array_search('Security update', $available['drupal']['releases'][$equivalent_release]['terms']['Release type']);
+ if ($security_release_key !== FALSE) {
+ unset($available['drupal']['releases'][$equivalent_release]['terms']['Release type'][$security_release_key]);
+ }
+ }
+ }
return $available;
}
+/**
+ * Identifies equivalent security releases with a hardcoded list.
+ *
+ * Generally, only the latest minor version of Drupal 8 is supported. However,
+ * when security fixes are backported to an old branch, and the site owner
+ * updates to the release containing the backported fix, they should not
+ * see "Security update required!" again if the only other security releases
+ * are releases for the same advisories.
+ *
+ * @return string[]
+ * A list of security release numbers that are equivalent to this release
+ * (i.e. covered by the same advisory), for backported security fixes only.
+ *
+ * @todo This is a temporary fix to allow minor-version backports of security
+ * fixes to be shown as secure. It should not be included in the codebase of
+ * any release or branch other than such backports. Replace this with
+ * https://www.drupal.org/project/drupal/issues/2766491.
+ */
+function _update_equivalent_security_releases() {
+ switch (\Drupal::VERSION) {
+ case '8.4.5':
+ return ['8.5.0-rc1'];
+ case '8.4.6':
+ return ['8.5.1'];
+ }
+
+ return [];
+}
+
/**
* Adds a task to the queue for fetching release history data for a project.
*