Issue #2366043 by dawehner: Upgrade to Symfony 2.6

8.0.x
Nathaniel Catchpole 2014-11-18 11:26:57 +00:00
parent 150cbcf61f
commit 3b945fe250
408 changed files with 11541 additions and 9127 deletions

View File

@ -6,16 +6,17 @@
"require": {
"php": ">=5.4.5",
"sdboyer/gliph": "0.1.*",
"symfony/class-loader": "2.5.*",
"symfony/css-selector": "2.5.*",
"symfony/dependency-injection": "2.5.*",
"symfony/event-dispatcher": "2.5.*",
"symfony/http-foundation": "2.5.*",
"symfony/http-kernel": "2.5.*",
"symfony/routing": "2.5.*",
"symfony/serializer": "2.5.*",
"symfony/validator": "2.5.*",
"symfony/yaml": "dev-master#499f7d7aa96747ad97940089bd7a1fb24ad8182a",
"symfony/class-loader": "2.6.0-beta1",
"symfony/css-selector": "2.6.0-beta1",
"symfony/debug": "2.6.0-beta1",
"symfony/dependency-injection": "2.6.0-beta1",
"symfony/event-dispatcher": "2.6.0-beta1",
"symfony/http-foundation": "2.6.0-beta1",
"symfony/http-kernel": "2.6.0-beta1",
"symfony/routing": "2.6.0-beta1",
"symfony/serializer": "2.6.0-beta1",
"symfony/validator": "2.6.0-beta1",
"symfony/yaml": "2.6.0-beta1",
"twig/twig": "1.16.*",
"doctrine/common": "dev-master#a45d110f71c323e29f41eb0696fa230e3fa1b1b5",
"doctrine/annotations": "1.2.*",

250
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "2bd5814ec010b13a6997359736b58695",
"hash": "34a4f5a56891e51217c305b97e8cc675",
"packages": [
{
"name": "doctrine/annotations",
@ -518,16 +518,16 @@
},
{
"name": "egulias/email-validator",
"version": "1.2.2",
"version": "1.2.5",
"source": {
"type": "git",
"url": "https://github.com/egulias/EmailValidator.git",
"reference": "39b451bb2bb0655d83d82a38a0bba7189298cfc5"
"reference": "518f80a0ff7c1a35780e7702f4262c8c6f2b807f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/egulias/EmailValidator/zipball/39b451bb2bb0655d83d82a38a0bba7189298cfc5",
"reference": "39b451bb2bb0655d83d82a38a0bba7189298cfc5",
"url": "https://api.github.com/repos/egulias/EmailValidator/zipball/518f80a0ff7c1a35780e7702f4262c8c6f2b807f",
"reference": "518f80a0ff7c1a35780e7702f4262c8c6f2b807f",
"shasum": ""
},
"require": {
@ -540,7 +540,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
"dev-master": "1.3.x-dev"
}
},
"autoload": {
@ -564,7 +564,7 @@
"validation",
"validator"
],
"time": "2014-09-01 22:35:48"
"time": "2014-11-06 08:59:44"
},
{
"name": "guzzlehttp/guzzle",
@ -626,16 +626,16 @@
},
{
"name": "guzzlehttp/ringphp",
"version": "1.0.0",
"version": "1.0.3",
"source": {
"type": "git",
"url": "https://github.com/guzzle/RingPHP.git",
"reference": "9e44b565d726d9614cd970319e6eea70ee15bff3"
"reference": "e7c28f96c5ac12ab0e63412cfc15989756fcb964"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/RingPHP/zipball/9e44b565d726d9614cd970319e6eea70ee15bff3",
"reference": "9e44b565d726d9614cd970319e6eea70ee15bff3",
"url": "https://api.github.com/repos/guzzle/RingPHP/zipball/e7c28f96c5ac12ab0e63412cfc15989756fcb964",
"reference": "e7c28f96c5ac12ab0e63412cfc15989756fcb964",
"shasum": ""
},
"require": {
@ -672,7 +672,7 @@
"homepage": "https://github.com/mtdowling"
}
],
"time": "2014-10-13 00:59:38"
"time": "2014-11-04 07:01:14"
},
{
"name": "guzzlehttp/streams",
@ -1386,16 +1386,16 @@
},
{
"name": "sebastian/environment",
"version": "1.1.0",
"version": "1.2.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/environment.git",
"reference": "6288ebbf6fa3ed2b2ff2d69c356fbaaf4f0971a7"
"reference": "0d9bf79554d2a999da194a60416c15cf461eb67d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6288ebbf6fa3ed2b2ff2d69c356fbaaf4f0971a7",
"reference": "6288ebbf6fa3ed2b2ff2d69c356fbaaf4f0971a7",
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/0d9bf79554d2a999da194a60416c15cf461eb67d",
"reference": "0d9bf79554d2a999da194a60416c15cf461eb67d",
"shasum": ""
},
"require": {
@ -1407,7 +1407,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.1.x-dev"
"dev-master": "1.2.x-dev"
}
},
"autoload": {
@ -1432,7 +1432,7 @@
"environment",
"hhvm"
],
"time": "2014-10-07 09:23:16"
"time": "2014-10-22 06:38:05"
},
{
"name": "sebastian/exporter",
@ -1643,17 +1643,17 @@
},
{
"name": "symfony/class-loader",
"version": "v2.5.5",
"version": "v2.6.0-BETA1",
"target-dir": "Symfony/Component/ClassLoader",
"source": {
"type": "git",
"url": "https://github.com/symfony/ClassLoader.git",
"reference": "432561f655123b003b32f370ca812fed9a9340c6"
"reference": "d1a16139ea522ec3cc20801b7e19cd3cafd12d8c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/ClassLoader/zipball/432561f655123b003b32f370ca812fed9a9340c6",
"reference": "432561f655123b003b32f370ca812fed9a9340c6",
"url": "https://api.github.com/repos/symfony/ClassLoader/zipball/d1a16139ea522ec3cc20801b7e19cd3cafd12d8c",
"reference": "d1a16139ea522ec3cc20801b7e19cd3cafd12d8c",
"shasum": ""
},
"require": {
@ -1665,7 +1665,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.5-dev"
"dev-master": "2.6-dev"
}
},
"autoload": {
@ -1689,21 +1689,21 @@
],
"description": "Symfony ClassLoader Component",
"homepage": "http://symfony.com",
"time": "2014-09-22 09:14:18"
"time": "2014-11-03 03:55:50"
},
{
"name": "symfony/css-selector",
"version": "v2.5.5",
"version": "v2.6.0-BETA1",
"target-dir": "Symfony/Component/CssSelector",
"source": {
"type": "git",
"url": "https://github.com/symfony/CssSelector.git",
"reference": "caf5ecc3face1f22884fb74b8edab65ac5ba9976"
"reference": "41953ad30ffc5cd710d106cf01eff79f6effa117"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/CssSelector/zipball/caf5ecc3face1f22884fb74b8edab65ac5ba9976",
"reference": "caf5ecc3face1f22884fb74b8edab65ac5ba9976",
"url": "https://api.github.com/repos/symfony/CssSelector/zipball/41953ad30ffc5cd710d106cf01eff79f6effa117",
"reference": "41953ad30ffc5cd710d106cf01eff79f6effa117",
"shasum": ""
},
"require": {
@ -1712,7 +1712,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.5-dev"
"dev-master": "2.6-dev"
}
},
"autoload": {
@ -1740,25 +1740,26 @@
],
"description": "Symfony CssSelector Component",
"homepage": "http://symfony.com",
"time": "2014-09-22 09:14:18"
"time": "2014-10-26 07:46:28"
},
{
"name": "symfony/debug",
"version": "v2.5.5",
"version": "v2.6.0-BETA1",
"target-dir": "Symfony/Component/Debug",
"source": {
"type": "git",
"url": "https://github.com/symfony/Debug.git",
"reference": "4a3dd4ef3fc0cee2fd9faaae12bd7af43afcf648"
"reference": "3548595c26175fdaca19cbec204668c22cda41f0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Debug/zipball/4a3dd4ef3fc0cee2fd9faaae12bd7af43afcf648",
"reference": "4a3dd4ef3fc0cee2fd9faaae12bd7af43afcf648",
"url": "https://api.github.com/repos/symfony/Debug/zipball/3548595c26175fdaca19cbec204668c22cda41f0",
"reference": "3548595c26175fdaca19cbec204668c22cda41f0",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
"php": ">=5.3.3",
"psr/log": "~1.0"
},
"require-dev": {
"symfony/http-foundation": "~2.1",
@ -1771,7 +1772,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.5-dev"
"dev-master": "2.6-dev"
}
},
"autoload": {
@ -1795,21 +1796,21 @@
],
"description": "Symfony Debug Component",
"homepage": "http://symfony.com",
"time": "2014-09-28 15:22:14"
"time": "2014-10-28 10:06:58"
},
{
"name": "symfony/dependency-injection",
"version": "v2.5.5",
"version": "v2.6.0-BETA1",
"target-dir": "Symfony/Component/DependencyInjection",
"source": {
"type": "git",
"url": "https://github.com/symfony/DependencyInjection.git",
"reference": "1f01a64c9047909e40700a14ee34e8c446300618"
"reference": "926500fe0b8a6562c4e8b8166a1cb664733804aa"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/DependencyInjection/zipball/1f01a64c9047909e40700a14ee34e8c446300618",
"reference": "1f01a64c9047909e40700a14ee34e8c446300618",
"url": "https://api.github.com/repos/symfony/DependencyInjection/zipball/926500fe0b8a6562c4e8b8166a1cb664733804aa",
"reference": "926500fe0b8a6562c4e8b8166a1cb664733804aa",
"shasum": ""
},
"require": {
@ -1828,7 +1829,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.5-dev"
"dev-master": "2.6-dev"
}
},
"autoload": {
@ -1852,21 +1853,21 @@
],
"description": "Symfony DependencyInjection Component",
"homepage": "http://symfony.com",
"time": "2014-09-27 08:35:39"
"time": "2014-11-03 03:55:50"
},
{
"name": "symfony/event-dispatcher",
"version": "v2.5.5",
"version": "v2.6.0-BETA1",
"target-dir": "Symfony/Component/EventDispatcher",
"source": {
"type": "git",
"url": "https://github.com/symfony/EventDispatcher.git",
"reference": "f6281337bf5f985f585d1db6a83adb05ce531f46"
"reference": "dcf345d5ed96bc6c3b4521c1989670d2c9e5014e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/f6281337bf5f985f585d1db6a83adb05ce531f46",
"reference": "f6281337bf5f985f585d1db6a83adb05ce531f46",
"url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/dcf345d5ed96bc6c3b4521c1989670d2c9e5014e",
"reference": "dcf345d5ed96bc6c3b4521c1989670d2c9e5014e",
"shasum": ""
},
"require": {
@ -1875,7 +1876,8 @@
"require-dev": {
"psr/log": "~1.0",
"symfony/config": "~2.0",
"symfony/dependency-injection": "~2.0,<2.6.0",
"symfony/dependency-injection": "~2.6",
"symfony/expression-language": "~2.6",
"symfony/stopwatch": "~2.2"
},
"suggest": {
@ -1885,7 +1887,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.5-dev"
"dev-master": "2.6-dev"
}
},
"autoload": {
@ -1909,21 +1911,21 @@
],
"description": "Symfony EventDispatcher Component",
"homepage": "http://symfony.com",
"time": "2014-09-28 15:56:11"
"time": "2014-11-03 03:55:50"
},
{
"name": "symfony/http-foundation",
"version": "v2.5.5",
"version": "v2.6.0-BETA1",
"target-dir": "Symfony/Component/HttpFoundation",
"source": {
"type": "git",
"url": "https://github.com/symfony/HttpFoundation.git",
"reference": "650e115af152d7a5e857d01c2cdb9a22809de9b4"
"reference": "4cd6c807598e560db7b3da50c4330fdb4808cfa1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/HttpFoundation/zipball/650e115af152d7a5e857d01c2cdb9a22809de9b4",
"reference": "650e115af152d7a5e857d01c2cdb9a22809de9b4",
"url": "https://api.github.com/repos/symfony/HttpFoundation/zipball/4cd6c807598e560db7b3da50c4330fdb4808cfa1",
"reference": "4cd6c807598e560db7b3da50c4330fdb4808cfa1",
"shasum": ""
},
"require": {
@ -1935,7 +1937,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.5-dev"
"dev-master": "2.6-dev"
}
},
"autoload": {
@ -1962,27 +1964,27 @@
],
"description": "Symfony HttpFoundation Component",
"homepage": "http://symfony.com",
"time": "2014-09-25 09:52:29"
"time": "2014-11-03 03:55:50"
},
{
"name": "symfony/http-kernel",
"version": "v2.5.5",
"version": "v2.6.0-BETA1",
"target-dir": "Symfony/Component/HttpKernel",
"source": {
"type": "git",
"url": "https://github.com/symfony/HttpKernel.git",
"reference": "6a3595611229def14d5e644f060cf372235532ec"
"reference": "7fa0bd9220cd529ee78d8565bbf8d5a854bd72d2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/HttpKernel/zipball/6a3595611229def14d5e644f060cf372235532ec",
"reference": "6a3595611229def14d5e644f060cf372235532ec",
"url": "https://api.github.com/repos/symfony/HttpKernel/zipball/7fa0bd9220cd529ee78d8565bbf8d5a854bd72d2",
"reference": "7fa0bd9220cd529ee78d8565bbf8d5a854bd72d2",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"psr/log": "~1.0",
"symfony/debug": "~2.5",
"symfony/debug": "~2.6",
"symfony/event-dispatcher": "~2.5",
"symfony/http-foundation": "~2.5"
},
@ -1997,7 +1999,9 @@
"symfony/process": "~2.0",
"symfony/routing": "~2.2",
"symfony/stopwatch": "~2.2",
"symfony/templating": "~2.2"
"symfony/templating": "~2.2",
"symfony/translation": "~2.0",
"symfony/var-dumper": "~2.6"
},
"suggest": {
"symfony/browser-kit": "",
@ -2005,12 +2009,13 @@
"symfony/config": "",
"symfony/console": "",
"symfony/dependency-injection": "",
"symfony/finder": ""
"symfony/finder": "",
"symfony/var-dumper": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.5-dev"
"dev-master": "2.6-dev"
}
},
"autoload": {
@ -2034,68 +2039,21 @@
],
"description": "Symfony HttpKernel Component",
"homepage": "http://symfony.com",
"time": "2014-09-28 17:33:53"
},
{
"name": "symfony/process",
"version": "v2.5.5",
"target-dir": "Symfony/Component/Process",
"source": {
"type": "git",
"url": "https://github.com/symfony/Process.git",
"reference": "8a1ec96c4e519cee0fb971ea48a1eb7369dda54b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Process/zipball/8a1ec96c4e519cee0fb971ea48a1eb7369dda54b",
"reference": "8a1ec96c4e519cee0fb971ea48a1eb7369dda54b",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.5-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\Process\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
},
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"description": "Symfony Process Component",
"homepage": "http://symfony.com",
"time": "2014-09-23 05:25:11"
"time": "2014-11-03 20:15:26"
},
{
"name": "symfony/routing",
"version": "v2.5.5",
"version": "v2.6.0-BETA1",
"target-dir": "Symfony/Component/Routing",
"source": {
"type": "git",
"url": "https://github.com/symfony/Routing.git",
"reference": "9bc38fe72e0eff61611e7cd4df3accbce20b1d36"
"reference": "f0bb6f818f9a7ece41c7dfe14e08b13c2de55b0c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Routing/zipball/9bc38fe72e0eff61611e7cd4df3accbce20b1d36",
"reference": "9bc38fe72e0eff61611e7cd4df3accbce20b1d36",
"url": "https://api.github.com/repos/symfony/Routing/zipball/f0bb6f818f9a7ece41c7dfe14e08b13c2de55b0c",
"reference": "f0bb6f818f9a7ece41c7dfe14e08b13c2de55b0c",
"shasum": ""
},
"require": {
@ -2118,7 +2076,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.5-dev"
"dev-master": "2.6-dev"
}
},
"autoload": {
@ -2148,21 +2106,21 @@
"uri",
"url"
],
"time": "2014-09-22 15:28:36"
"time": "2014-11-03 19:16:49"
},
{
"name": "symfony/serializer",
"version": "v2.5.5",
"version": "v2.6.0-BETA1",
"target-dir": "Symfony/Component/Serializer",
"source": {
"type": "git",
"url": "https://github.com/symfony/Serializer.git",
"reference": "a95c0471682778da2e02169fb2644d3b08d4470f"
"reference": "e96b7ac54b3d75a458f76eab11b7cd2d757f09f1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Serializer/zipball/a95c0471682778da2e02169fb2644d3b08d4470f",
"reference": "a95c0471682778da2e02169fb2644d3b08d4470f",
"url": "https://api.github.com/repos/symfony/Serializer/zipball/e96b7ac54b3d75a458f76eab11b7cd2d757f09f1",
"reference": "e96b7ac54b3d75a458f76eab11b7cd2d757f09f1",
"shasum": ""
},
"require": {
@ -2171,7 +2129,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.5-dev"
"dev-master": "2.6-dev"
}
},
"autoload": {
@ -2195,21 +2153,21 @@
],
"description": "Symfony Serializer Component",
"homepage": "http://symfony.com",
"time": "2014-09-22 09:14:18"
"time": "2014-11-03 03:55:50"
},
{
"name": "symfony/translation",
"version": "v2.5.5",
"version": "v2.5.6",
"target-dir": "Symfony/Component/Translation",
"source": {
"type": "git",
"url": "https://github.com/symfony/Translation.git",
"reference": "170c0d895616e1a6a35681ffb0b9e339f58ab928"
"reference": "362fe4da2cfe587f72d57aaa2f89e6b61c05dedf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Translation/zipball/170c0d895616e1a6a35681ffb0b9e339f58ab928",
"reference": "170c0d895616e1a6a35681ffb0b9e339f58ab928",
"url": "https://api.github.com/repos/symfony/Translation/zipball/362fe4da2cfe587f72d57aaa2f89e6b61c05dedf",
"reference": "362fe4da2cfe587f72d57aaa2f89e6b61c05dedf",
"shasum": ""
},
"require": {
@ -2251,21 +2209,21 @@
],
"description": "Symfony Translation Component",
"homepage": "http://symfony.com",
"time": "2014-09-23 05:25:11"
"time": "2014-10-01 05:50:18"
},
{
"name": "symfony/validator",
"version": "v2.5.5",
"version": "v2.6.0-BETA1",
"target-dir": "Symfony/Component/Validator",
"source": {
"type": "git",
"url": "https://github.com/symfony/Validator.git",
"reference": "64f61505843ca5e6c647244f5a4b6812c1279427"
"reference": "6e521cdbc963cef7daf9931a7bde48b56d67d10a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Validator/zipball/64f61505843ca5e6c647244f5a4b6812c1279427",
"reference": "64f61505843ca5e6c647244f5a4b6812c1279427",
"url": "https://api.github.com/repos/symfony/Validator/zipball/6e521cdbc963cef7daf9931a7bde48b56d67d10a",
"reference": "6e521cdbc963cef7daf9931a7bde48b56d67d10a",
"shasum": ""
},
"require": {
@ -2297,7 +2255,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.5-dev"
"dev-master": "2.6-dev"
}
},
"autoload": {
@ -2321,21 +2279,21 @@
],
"description": "Symfony Validator Component",
"homepage": "http://symfony.com",
"time": "2014-09-28 15:22:14"
"time": "2014-11-03 03:55:50"
},
{
"name": "symfony/yaml",
"version": "dev-master",
"version": "v2.6.0-BETA1",
"target-dir": "Symfony/Component/Yaml",
"source": {
"type": "git",
"url": "https://github.com/symfony/Yaml.git",
"reference": "499f7d7aa96747ad97940089bd7a1fb24ad8182a"
"reference": "9da3813f36985a4089f7e83c601a1034d125ff69"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Yaml/zipball/499f7d7aa96747ad97940089bd7a1fb24ad8182a",
"reference": "499f7d7aa96747ad97940089bd7a1fb24ad8182a",
"url": "https://api.github.com/repos/symfony/Yaml/zipball/9da3813f36985a4089f7e83c601a1034d125ff69",
"reference": "9da3813f36985a4089f7e83c601a1034d125ff69",
"shasum": ""
},
"require": {
@ -2368,7 +2326,7 @@
],
"description": "Symfony Yaml Component",
"homepage": "http://symfony.com",
"time": "2014-10-05 13:53:50"
"time": "2014-11-03 03:55:50"
},
{
"name": "twig/twig",
@ -2564,7 +2522,17 @@
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {
"symfony/yaml": 20,
"symfony/class-loader": 10,
"symfony/css-selector": 10,
"symfony/debug": 10,
"symfony/dependency-injection": 10,
"symfony/event-dispatcher": 10,
"symfony/http-foundation": 10,
"symfony/http-kernel": 10,
"symfony/routing": 10,
"symfony/serializer": 10,
"symfony/validator": 10,
"symfony/yaml": 10,
"doctrine/common": 20,
"phpunit/phpunit-mock-objects": 20
},

View File

@ -173,4 +173,30 @@ class CompiledRoute extends SymfonyCompiledRoute {
return $this->route->getRequirements();
}
/**
* {@inheritdoc}
*/
public function serialize() {
$data = unserialize(parent::serialize());
$data['fit'] = $this->fit;
$data['patternOutline'] = $this->patternOutline;
$data['numParts'] = $this->numParts;
return serialize($data);
}
/**
* {@inheritdoc}
*/
public function unserialize($serialized)
{
parent::unserialize($serialized);
$data = unserialize($serialized);
$this->fit = $data['fit'];
$this->patternOutline = $data['patternOutline'];
$this->numParts = $data['numParts'];
}
}

View File

@ -16,7 +16,6 @@ return array(
'Symfony\\Component\\Translation\\' => array($vendorDir . '/symfony/translation'),
'Symfony\\Component\\Serializer\\' => array($vendorDir . '/symfony/serializer'),
'Symfony\\Component\\Routing\\' => array($vendorDir . '/symfony/routing'),
'Symfony\\Component\\Process\\' => array($vendorDir . '/symfony/process'),
'Symfony\\Component\\HttpKernel\\' => array($vendorDir . '/symfony/http-kernel'),
'Symfony\\Component\\HttpFoundation\\' => array($vendorDir . '/symfony/http-foundation'),
'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'),

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,13 @@
EmailValidator [![Build Status](https://travis-ci.org/egulias/EmailValidator.png?branch=master)](https://travis-ci.org/egulias/EmailValidator) [![Coverage Status](https://coveralls.io/repos/egulias/EmailValidator/badge.png?branch=master)](https://coveralls.io/r/egulias/EmailValidator?branch=master) [![SensioLabsInsight](https://insight.sensiolabs.com/projects/b18d473e-bd5a-4d88-a7b2-7aeaee0ebd7b/small.png)](https://insight.sensiolabs.com/projects/b18d473e-bd5a-4d88-a7b2-7aeaee0ebd7b)[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/egulias/EmailValidator/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/egulias/EmailValidator/?branch=master)
EmailValidator [![Build Status](https://travis-ci.org/egulias/EmailValidator.png?branch=master)](https://travis-ci.org/egulias/EmailValidator) [![Coverage Status](https://coveralls.io/repos/egulias/EmailValidator/badge.png?branch=master)](https://coveralls.io/r/egulias/EmailValidator?branch=master) [![SensioLabsInsight](https://insight.sensiolabs.com/projects/22ba6692-9c02-42e5-a65d-1c5696bfffc6/small.png)](https://insight.sensiolabs.com/projects/22ba6692-9c02-42e5-a65d-1c5696bfffc6)[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/egulias/EmailValidator/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/egulias/EmailValidator/?branch=master)
=============================
##Installation##
Install via composer. Add to your current compooser.json ```require``` key: ```"egulias/email-validator":"1.0.x-dev" ```
Run the command below to install via Composer
```shell
composer require egulias/email-validator
```
##Usage##
@ -24,7 +29,7 @@ More advanced example (returns detailed diagnostic error codes):
```php
<?php
use egulias\EmailValidator\EmailValidator;
use Egulias\EmailValidator\EmailValidator;
$validator = new EmailValidator;
$email = 'dominic@sayers.cc';
@ -46,6 +51,6 @@ As this is a port from another library and work, here are other people related t
* Josepf Bielawski [@stloyd](http://github.com/stloyd): For its first re-work of Dominic's lib
* Dominic Sayers [@dominicsayers](http://github.com/dominicsayers): The original isemail function
##Licence##
Released under the MIT Licence attached with this code.
##License##
Released under the MIT License attached with this code.

View File

@ -10,7 +10,7 @@
],
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
"dev-master": "1.3.x-dev"
}
},
"require": {

View File

@ -1,9 +1,10 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file"
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "cca347a6b95164b1121e034b95f824ec",
"hash": "9e9dff0cc08c7292600453e681201e13",
"packages": [
{
"name": "doctrine/lexer",
@ -61,16 +62,16 @@
"packages-dev": [
{
"name": "guzzle/guzzle",
"version": "v3.9.1",
"version": "v3.9.2",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle3.git",
"reference": "92d9934f2fca1da15178c91239576ae26e505e60"
"reference": "54991459675c1a2924122afbb0e5609ade581155"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/guzzle3/zipball/92d9934f2fca1da15178c91239576ae26e505e60",
"reference": "92d9934f2fca1da15178c91239576ae26e505e60",
"url": "https://api.github.com/repos/guzzle/guzzle3/zipball/54991459675c1a2924122afbb0e5609ade581155",
"reference": "54991459675c1a2924122afbb0e5609ade581155",
"shasum": ""
},
"require": {
@ -114,7 +115,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.8-dev"
"dev-master": "3.9-dev"
}
},
"autoload": {
@ -149,7 +150,7 @@
"rest",
"web service"
],
"time": "2014-05-07 17:04:22"
"time": "2014-08-11 04:32:36"
},
{
"name": "psr/log",
@ -195,18 +196,18 @@
"source": {
"type": "git",
"url": "https://github.com/satooshi/php-coveralls.git",
"reference": "b7271847c84d160f5b0aae83e45c225e8ffc96f4"
"reference": "94389a0ebdb64857d6899b5e0254dffa99e5aa96"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/satooshi/php-coveralls/zipball/b7271847c84d160f5b0aae83e45c225e8ffc96f4",
"reference": "b7271847c84d160f5b0aae83e45c225e8ffc96f4",
"url": "https://api.github.com/repos/satooshi/php-coveralls/zipball/94389a0ebdb64857d6899b5e0254dffa99e5aa96",
"reference": "94389a0ebdb64857d6899b5e0254dffa99e5aa96",
"shasum": ""
},
"require": {
"ext-json": "*",
"ext-simplexml": "*",
"guzzle/guzzle": ">=3.0",
"guzzle/guzzle": ">=2.7",
"php": ">=5.3",
"psr/log": "1.0.0",
"symfony/config": ">=2.0",
@ -216,7 +217,7 @@
},
"require-dev": {
"apigen/apigen": "2.8.*@stable",
"pdepend/pdepend": "dev-master",
"pdepend/pdepend": "dev-master as 2.0.0",
"phpmd/phpmd": "dev-master",
"phpunit/php-invoker": ">=1.1.0,<1.2.0",
"phpunit/phpunit": "3.7.*@stable",
@ -262,21 +263,21 @@
"github",
"test"
],
"time": "2014-05-14 13:09:37"
"time": "2014-07-09 10:45:38"
},
{
"name": "symfony/config",
"version": "v2.4.5",
"version": "v2.5.4",
"target-dir": "Symfony/Component/Config",
"source": {
"type": "git",
"url": "https://github.com/symfony/Config.git",
"reference": "2effc67af6f21a0d267210b72d0b0b691d113528"
"reference": "080eabdc256c1d7a3a7cf6296271edb68eb1ab2b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Config/zipball/2effc67af6f21a0d267210b72d0b0b691d113528",
"reference": "2effc67af6f21a0d267210b72d0b0b691d113528",
"url": "https://api.github.com/repos/symfony/Config/zipball/080eabdc256c1d7a3a7cf6296271edb68eb1ab2b",
"reference": "080eabdc256c1d7a3a7cf6296271edb68eb1ab2b",
"shasum": ""
},
"require": {
@ -286,7 +287,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
"dev-master": "2.5-dev"
}
},
"autoload": {
@ -299,49 +300,49 @@
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
},
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"description": "Symfony Config Component",
"homepage": "http://symfony.com",
"time": "2014-04-22 08:11:06"
"time": "2014-08-31 03:22:04"
},
{
"name": "symfony/console",
"version": "v2.4.5",
"version": "v2.5.4",
"target-dir": "Symfony/Component/Console",
"source": {
"type": "git",
"url": "https://github.com/symfony/Console.git",
"reference": "24f723436e62598c9dddee2a8573d6992504dc5d"
"reference": "748beed2a1e73179c3f5154d33fe6ae100c1aeb1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Console/zipball/24f723436e62598c9dddee2a8573d6992504dc5d",
"reference": "24f723436e62598c9dddee2a8573d6992504dc5d",
"url": "https://api.github.com/repos/symfony/Console/zipball/748beed2a1e73179c3f5154d33fe6ae100c1aeb1",
"reference": "748beed2a1e73179c3f5154d33fe6ae100c1aeb1",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"psr/log": "~1.0",
"symfony/event-dispatcher": "~2.1"
},
"suggest": {
"psr/log": "For using the console logger",
"symfony/event-dispatcher": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
"dev-master": "2.5-dev"
}
},
"autoload": {
@ -354,41 +355,42 @@
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
},
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"description": "Symfony Console Component",
"homepage": "http://symfony.com",
"time": "2014-05-14 21:48:29"
"time": "2014-08-14 16:10:54"
},
{
"name": "symfony/event-dispatcher",
"version": "v2.4.5",
"version": "v2.5.4",
"target-dir": "Symfony/Component/EventDispatcher",
"source": {
"type": "git",
"url": "https://github.com/symfony/EventDispatcher.git",
"reference": "e539602e5455aa086c0e81e604745af7789e4d8a"
"reference": "8faf5cc7e80fde74a650a36e60d32ce3c3e0457b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/e539602e5455aa086c0e81e604745af7789e4d8a",
"reference": "e539602e5455aa086c0e81e604745af7789e4d8a",
"url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/8faf5cc7e80fde74a650a36e60d32ce3c3e0457b",
"reference": "8faf5cc7e80fde74a650a36e60d32ce3c3e0457b",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"symfony/dependency-injection": "~2.0"
"psr/log": "~1.0",
"symfony/config": "~2.0",
"symfony/dependency-injection": "~2.0",
"symfony/stopwatch": "~2.2"
},
"suggest": {
"symfony/dependency-injection": "",
@ -397,7 +399,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
"dev-master": "2.5-dev"
}
},
"autoload": {
@ -410,34 +412,32 @@
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
},
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"description": "Symfony EventDispatcher Component",
"homepage": "http://symfony.com",
"time": "2014-04-16 10:34:31"
"time": "2014-07-28 13:20:46"
},
{
"name": "symfony/filesystem",
"version": "v2.4.5",
"version": "v2.5.4",
"target-dir": "Symfony/Component/Filesystem",
"source": {
"type": "git",
"url": "https://github.com/symfony/Filesystem.git",
"reference": "a3af8294bcce4a7c1b2892363b0c9d8109affad4"
"reference": "a765efd199e02ff4001c115c318e219030be9364"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Filesystem/zipball/a3af8294bcce4a7c1b2892363b0c9d8109affad4",
"reference": "a3af8294bcce4a7c1b2892363b0c9d8109affad4",
"url": "https://api.github.com/repos/symfony/Filesystem/zipball/a765efd199e02ff4001c115c318e219030be9364",
"reference": "a765efd199e02ff4001c115c318e219030be9364",
"shasum": ""
},
"require": {
@ -446,7 +446,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
"dev-master": "2.5-dev"
}
},
"autoload": {
@ -459,34 +459,32 @@
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
},
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"description": "Symfony Filesystem Component",
"homepage": "http://symfony.com",
"time": "2014-04-16 10:34:31"
"time": "2014-09-03 09:00:14"
},
{
"name": "symfony/stopwatch",
"version": "v2.4.5",
"version": "v2.5.4",
"target-dir": "Symfony/Component/Stopwatch",
"source": {
"type": "git",
"url": "https://github.com/symfony/Stopwatch.git",
"reference": "343bcc0360f2c22f371884b8f6a9fee8d1aa431a"
"reference": "22ab4f76cdeefd38b00022a6be5709190a2fd046"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Stopwatch/zipball/343bcc0360f2c22f371884b8f6a9fee8d1aa431a",
"reference": "343bcc0360f2c22f371884b8f6a9fee8d1aa431a",
"url": "https://api.github.com/repos/symfony/Stopwatch/zipball/22ab4f76cdeefd38b00022a6be5709190a2fd046",
"reference": "22ab4f76cdeefd38b00022a6be5709190a2fd046",
"shasum": ""
},
"require": {
@ -495,7 +493,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
"dev-master": "2.5-dev"
}
},
"autoload": {
@ -508,34 +506,32 @@
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
},
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"description": "Symfony Stopwatch Component",
"homepage": "http://symfony.com",
"time": "2014-04-18 20:37:09"
"time": "2014-08-14 16:10:54"
},
{
"name": "symfony/yaml",
"version": "v2.4.5",
"version": "v2.5.4",
"target-dir": "Symfony/Component/Yaml",
"source": {
"type": "git",
"url": "https://github.com/symfony/Yaml.git",
"reference": "fd22bb88c3a6f73c898b39bec185a9e211b06265"
"reference": "01a7695bcfb013d0a15c6757e15aae120342986f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Yaml/zipball/fd22bb88c3a6f73c898b39bec185a9e211b06265",
"reference": "fd22bb88c3a6f73c898b39bec185a9e211b06265",
"url": "https://api.github.com/repos/symfony/Yaml/zipball/01a7695bcfb013d0a15c6757e15aae120342986f",
"reference": "01a7695bcfb013d0a15c6757e15aae120342986f",
"shasum": ""
},
"require": {
@ -544,7 +540,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
"dev-master": "2.5-dev"
}
},
"autoload": {
@ -557,20 +553,18 @@
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
},
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"description": "Symfony Yaml Component",
"homepage": "http://symfony.com",
"time": "2014-05-12 09:27:48"
"time": "2014-08-31 03:22:04"
}
],
"aliases": [
@ -580,6 +574,7 @@
"stability-flags": {
"satooshi/php-coveralls": 20
},
"prefer-stable": false,
"platform": {
"php": ">= 5.3.3"
},

View File

@ -31,10 +31,13 @@ class EmailLexer extends AbstractLexer
const S_SEMICOLON = 275;
const S_OPENQBRACKET = 276;
const S_CLOSEQBRACKET = 277;
const S_SLASH = 278;
const S_EMPTY = null;
const GENERIC = 300;
const CRLF = 301;
const INVALID = 302;
const ASCII_INVALID_FROM = 127;
const ASCII_INVALID_TO = 199;
/**
* US-ASCII visible characters not valid for atext (@link http://tools.ietf.org/html/rfc5322#section-3.2.3)
@ -52,6 +55,7 @@ class EmailLexer extends AbstractLexer
';' => self::S_SEMICOLON,
'@' => self::S_AT,
'\\' => self::S_BACKSLASH,
'/' => self::S_SLASH,
',' => self::S_COMMA,
'.' => self::S_DOT,
'"' => self::S_DQUOTE,
@ -67,14 +71,31 @@ class EmailLexer extends AbstractLexer
'>' => self::S_GREATERTHAN,
'{' => self::S_OPENQBRACKET,
'}' => self::S_CLOSEQBRACKET,
'' => self::S_EMPTY
'' => self::S_EMPTY,
'\0' => self::C_NUL,
);
protected $invalidASCII = array(226 => 1,);
protected $hasInvalidTokens = false;
protected $previous;
public function reset()
{
$this->hasInvalidTokens = false;
parent::reset();
}
public function hasInvalidTokens()
{
return $this->hasInvalidTokens;
}
/**
* @param $type
* @throws \UnexpectedValueException
* @return boolean
*/
public function find($type)
{
@ -100,7 +121,7 @@ class EmailLexer extends AbstractLexer
/**
* moveNext
*
* @return mixed
* @return boolean
*/
public function moveNext()
{
@ -112,7 +133,7 @@ class EmailLexer extends AbstractLexer
/**
* Lexical catchable patterns.
*
* @return array
* @return string[]
*/
protected function getCatchablePatterns()
{
@ -130,7 +151,7 @@ class EmailLexer extends AbstractLexer
/**
* Lexical non-catchable patterns.
*
* @return array
* @return string[]
*/
protected function getNonCatchablePatterns()
{
@ -146,14 +167,48 @@ class EmailLexer extends AbstractLexer
*/
protected function getType(&$value)
{
if ($this->isNullType($value)) {
return self::C_NUL;
}
if (isset($this->charValue[$value])) {
return $this->charValue[$value];
}
if (preg_match('/[\x10-\x1F]+/', $value)) {
if ($this->isInvalid($value)) {
$this->hasInvalidTokens = true;
return self::INVALID;
}
return self::GENERIC;
}
/**
* @param string $value
*/
protected function isNullType($value)
{
if ($value === "\0") {
return true;
}
return false;
}
/**
* @param string $value
*/
protected function isInvalid($value)
{
if (preg_match('/[\x10-\x1F]+/', $value)) {
return true;
}
if (isset($this->invalidASCII[ord($value)])) {
return true;
}
return false;
}
}

View File

@ -28,6 +28,9 @@ class EmailParser
$this->domainPartParser = new DomainPart($this->lexer);
}
/**
* @param string $str
*/
public function parse($str)
{
$this->lexer->setInput($str);
@ -36,6 +39,10 @@ class EmailParser
throw new \InvalidArgumentException('ERR_NOLOCALPART');
}
if ($this->lexer->hasInvalidTokens()) {
throw new \InvalidArgumentException('ERR_INVALID_ATEXT');
}
$this->localPartParser->parse($str);
$this->domainPartParser->parse($str);
@ -78,6 +85,10 @@ class EmailParser
return true;
}
/**
* @param string $localPart
* @param string $parsedDomainPart
*/
protected function addLongEmailWarning($localPart, $parsedDomainPart)
{
if (strlen($localPart . '@' . $parsedDomainPart) > self::EMAIL_MAX_LENGTH) {

View File

@ -2,8 +2,6 @@
namespace Egulias\EmailValidator;
use Egulias\EmailValidator\Parser\LocalPart;
/**
* EmailValidator
*
@ -97,7 +95,7 @@ class EmailValidator
return false;
}
return ($strict) ? (!$this->hasWarnings() && $dns) : true;
return !$strict || (!$this->hasWarnings() && $dns);
}
/**
@ -146,16 +144,9 @@ class EmailValidator
protected function checkDNS()
{
$checked = false;
if (!function_exists('dns_get_record') && (
in_array(self::DNSWARN_NO_RECORD, $this->warnings) &&
in_array(self::DNSWARN_NO_MX_RECORD, $this->warnings)
)) {
return $checked;
}
$checked = true;
$result = checkdnsrr(trim($this->parser->getParsedDomainPart()), 'MX');
$checked = true;
if (!$result) {
$this->warnings[] = self::DNSWARN_NO_RECORD;

View File

@ -23,6 +23,9 @@ class DomainPart extends Parser
if ($this->lexer->token['type'] === EmailLexer::S_EMPTY) {
throw new \InvalidArgumentException('ERR_NODOMAIN');
}
if ($this->lexer->token['type'] === EmailLexer::S_HYPHEN) {
throw new \InvalidArgumentException('ERR_DOMAINHYPHENEND');
}
if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) {
$this->warnings[] = EmailValidator::DEPREC_COMMENT;
@ -103,6 +106,10 @@ class DomainPart extends Parser
do {
$prev = $this->lexer->getPrevious();
if ($this->lexer->token['type'] === EmailLexer::S_SLASH) {
throw new \InvalidArgumentException('ERR_DOMAIN_CHAR_NOT_ALLOWED');
}
if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) {
$this->parseComments();
$this->lexer->moveNext();
@ -210,6 +217,9 @@ class DomainPart extends Parser
return $addressLiteral;
}
/**
* @param string $addressLiteral
*/
protected function checkIPV4Tag($addressLiteral)
{
$matchesIP = array();
@ -295,5 +305,4 @@ class DomainPart extends Parser
throw new \InvalidArgumentException('ERR_EXPECTING_ATEXT');
}
}
}

View File

@ -4,6 +4,7 @@ namespace Egulias\EmailValidator\Parser;
use Egulias\EmailValidator\EmailLexer;
use Egulias\EmailValidator\EmailValidator;
use \InvalidArgumentException;
class LocalPart extends Parser
@ -21,8 +22,7 @@ class LocalPart extends Parser
$closingQuote = $this->checkDQUOTE($closingQuote);
if ($closingQuote && $parseDQuote) {
$this->parseDoubleQuote();
$parseDQuote = false;
$parseDQuote = $this->parseDoubleQuote();
}
if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) {
@ -56,23 +56,51 @@ class LocalPart extends Parser
protected function parseDoubleQuote()
{
$special = array (
$parseAgain = true;
$special = array(
EmailLexer::S_CR => true,
EmailLexer::S_HTAB => true,
EmailLexer::S_LF => true
);
$invalid = array(
EmailLexer::C_NUL => true,
EmailLexer::S_HTAB => true,
EmailLexer::S_CR => true,
EmailLexer::S_LF => true
);
$setSpecialsWarning = true;
$this->lexer->moveNext();
while ($this->lexer->token['type'] !== EmailLexer::S_DQUOTE && $this->lexer->token) {
$parseAgain = false;
if (isset($special[$this->lexer->token['type']]) && $setSpecialsWarning) {
$this->warnings[] = EmailValidator::CFWS_FWS;
$setSpecialsWarning = false;
}
$this->lexer->moveNext();
}
}
$this->lexer->moveNext();
if (!$this->escaped() && isset($invalid[$this->lexer->token['type']])) {
throw new InvalidArgumentException("ERR_EXPECTED_ATEXT");
}
}
$prev = $this->lexer->getPrevious();
if ($prev['type'] === EmailLexer::S_BACKSLASH) {
if (!$this->checkDQUOTE(false)) {
throw new \InvalidArgumentException("ERR_UNCLOSED_DQUOTE");
}
}
if (!$this->lexer->isNextToken(EmailLexer::S_AT) && $prev['type'] !== EmailLexer::S_BACKSLASH) {
throw new \InvalidArgumentException("ERR_EXPECED_AT");
}
return $parseAgain;
}
protected function isInvalidToken($token, $closingQuote)
{

View File

@ -124,8 +124,7 @@ abstract class Parser
if ($previous['type'] === EmailLexer::S_BACKSLASH
&&
($this->lexer->token['type'] === EmailLexer::S_SP ||
$this->lexer->token['type'] === EmailLexer::S_HTAB)
$this->lexer->token['type'] !== EmailLexer::GENERIC
) {
return true;
}
@ -164,6 +163,7 @@ abstract class Parser
if ($this->lexer->isNextToken(EmailLexer::GENERIC) && $previous['type'] === EmailLexer::GENERIC) {
throw new \InvalidArgumentException('ERR_EXPECTING_ATEXT');
}
$this->warnings[] = EmailValidator::RFC5321_QUOTEDSTRING;
try {
$this->lexer->find(EmailLexer::S_DQUOTE);

View File

@ -44,6 +44,15 @@ class EmailLexerTests extends \PHPUnit_Framework_TestCase
$this->assertTrue($lexer->find(EmailLexer::S_HTAB));
}
public function testLexerHasInvalidTokens()
{
$lexer = new EmailLexer();
$lexer->setInput(chr(226));
$lexer->moveNext();
$lexer->moveNext();
$this->assertTrue($lexer->hasInvalidTokens());
}
public function getTokens()
{
return array(
@ -61,6 +70,7 @@ class EmailLexerTests extends \PHPUnit_Framework_TestCase
array("\"", EmailLexer::S_DQUOTE),
array("-", EmailLexer::S_HYPHEN),
array("\\", EmailLexer::S_BACKSLASH),
array("/", EmailLexer::S_SLASH),
array("(", EmailLexer::S_OPENPARENTHESIS),
array(")", EmailLexer::S_CLOSEPARENTHESIS),
array('<', EmailLexer::S_LOWERTHAN),
@ -74,7 +84,9 @@ class EmailLexerTests extends \PHPUnit_Framework_TestCase
array('{', EmailLexer::S_OPENQBRACKET),
array('}', EmailLexer::S_CLOSEQBRACKET),
array('', EmailLexer::S_EMPTY),
array(chr(31), EmailLexer::INVALID)
array(chr(31), EmailLexer::INVALID),
array(chr(226), EmailLexer::INVALID),
array(chr(0), EmailLexer::C_NUL)
);
}
}

View File

@ -43,6 +43,10 @@ class EmailValidatorTest extends \PHPUnit_Framework_TestCase
array('"user,name"@example.com'),
array('"user name"@example.com'),
array('"user@name"@example.com'),
array('"\a"@iana.org'),
array('"test\ test"@iana.org'),
array('""@iana.org'),
array('"\""@iana.org'),
);
}
@ -57,9 +61,10 @@ class EmailValidatorTest extends \PHPUnit_Framework_TestCase
public function getInvalidEmails()
{
return array(
array('example.@example.co.uk'),
array('example@example@example.co.uk'),
array('(fabien_potencier@example.fr)'),
array('(test_exampel@example.fr)'),
array('example(example)example@example.co.uk'),
array('.example@localhost'),
array('ex\ample@localhost'),
@ -72,6 +77,27 @@ class EmailValidatorTest extends \PHPUnit_Framework_TestCase
array('username@example,com'),
array('usern,ame@example.com'),
array('user[na]me@example.com'),
array('"""@iana.org'),
array('"\"@iana.org'),
array('"test"test@iana.org'),
array('"test""test"@iana.org'),
array('"test"."test"@iana.org'),
array('"test".test@iana.org'),
array('"test"' . chr(0) . '@iana.org'),
array('"test\"@iana.org'),
array(chr(226) . '@iana.org'),
array('test@' . chr(226) . '.org'),
array('\r\ntest@iana.org'),
array('\r\n test@iana.org'),
array('\r\n \r\ntest@iana.org'),
array('\r\n \r\ntest@iana.org'),
array('\r\n \r\n test@iana.org'),
array('test@iana.org \r\n'),
array('test@iana.org \r\n '),
array('test@iana.org \r\n \r\n'),
array('test@iana.org \r\n\r\n'),
array('test@iana.org \r\n\r\n '),
array('test@iana/icann.org'),
);
}
@ -124,6 +150,16 @@ class EmailValidatorTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($warnings, $this->validator->getWarnings());
}
/**
* @dataProvider getInvalidEmailsWithWarnings
*/
public function testInvalidEmailsWithDnsCheckAndStrictMode($warnings, $email)
{
$this->assertFalse($this->validator->isValid($email, true, true));
$this->assertEquals($warnings, $this->validator->getWarnings());
}
public function getInvalidEmailsWithWarnings()
{
return array(
@ -275,6 +311,17 @@ class EmailValidatorTest extends \PHPUnit_Framework_TestCase
'parttoolonglocalparttoolonglocalparttoolonglocalparttoolonglocalparttoolonglocalparttoolonglocalpart'.
'toolonglocalparttoolonglocalparttoolonglocalparttoolonglocalpar'
),
array(
array(
EmailValidator::DNSWARN_NO_RECORD,
),
'test@test'
),
);
}
public function testInvalidEmailsWithStrict()
{
$this->assertFalse($this->validator->isValid('"test"@test', false, true));
}
}

View File

@ -0,0 +1,22 @@
# CHANGELOG
## 1.0.3 - 2014-11-03
* Setting the `header` stream option as a string to be compatible with GAE.
* Header parsing now ensures that header order is maintained in the parsed
message.
## 1.0.2 - 2014-10-28
* Now correctly honoring a `version` option is supplied in a request.
See https://github.com/guzzle/RingPHP/pull/8
## 1.0.1 - 2014-10-26
* Fixed a header parsing issue with the `CurlHandler` and `CurlMultiHandler`
that caused cURL requests with multiple responses to merge repsonses together
(e.g., requests with digest authentication).
## 1.0.0 - 2014-10-12
* Initial release.

View File

@ -2,13 +2,45 @@
RingPHP
=======
Provides low level APIs used to power HTTP clients and servers through a
simple, PHP ``callable`` that accepts a request hash and returns a future
response hash. RingPHP supports both synchronous and asynchronous
workflows by utilizing both futures and `promises <https://github.com/reactphp/promise>`_.
Provides a simple API and specification that abstracts away the details of HTTP
into a single PHP function. RingPHP be used to power HTTP clients and servers
through a PHP function that accepts a request hash and returns a response hash
that is fulfilled using a `promise <https://github.com/reactphp/promise>`_,
allowing RingPHP to support both synchronous and asynchronous workflows.
By abstracting the implementation details of different HTTP clients and
servers, RingPHP allows you to utilize pluggable HTTP clients and servers
without tying your application to a specific implementation.
.. code-block:: php
<?php
require 'vendor/autoload.php';
use GuzzleHttp\Ring\Client\CurlHandler;
$handler = new CurlHandler();
$response = $handler([
'http_method' => 'GET',
'uri' => '/',
'headers' => [
'host' => ['www.google.com'],
'x-foo' => ['baz']
]
]);
$response->then(function (array $response) {
echo $response['status'];
});
$response->wait();
RingPHP is inspired by Clojure's `Ring <https://github.com/ring-clojure/ring>`_,
but has been modified to accommodate clients and servers for both blocking
and non-blocking requests.
which, in turn, was inspired by Python's WSGI and Ruby's Rack. RingPHP is
utilized as the handler layer in `Guzzle <http://guzzlephp.org>`_ 5.0+ to send
HTTP requests.
See http://guzzle-ring.readthedocs.org/ for the full online documentation.
Documentation
-------------
See http://ringphp.readthedocs.org/ for the full online documentation.

View File

@ -2,23 +2,49 @@
RingPHP
=======
Provides low level APIs used to power HTTP clients and servers through a
simple, PHP ``callable`` that accepts a request hash and returns a future
response hash. RingPHP supports both synchronous and asynchronous
workflows by utilizing both futures and `promises <https://github.com/reactphp/promise>`_.
Provides a simple API and specification that abstracts away the details of HTTP
into a single PHP function. RingPHP be used to power HTTP clients and servers
through a PHP function that accepts a request hash and returns a response hash
that is fulfilled using a `promise <https://github.com/reactphp/promise>`_,
allowing RingPHP to support both synchronous and asynchronous workflows.
RingPHP is inspired by Clojure's `Ring <https://github.com/ring-clojure/ring>`_,
but has been modified to accommodate clients and servers for both blocking
and non-blocking requests.
RingPHP is utilized as the handler layer in
`Guzzle <http://guzzlephp.org>`_ 5.0+ to send HTTP requests.
By abstracting the implementation details of different HTTP clients and
servers, RingPHP allows you to utilize pluggable HTTP clients and servers
without tying your application to a specific implementation.
.. toctree::
:maxdepth: 1
:maxdepth: 2
spec
futures
client_middleware
client_handlers
testing
spec
futures
client_middleware
client_handlers
testing
.. code-block:: php
<?php
require 'vendor/autoload.php';
use GuzzleHttp\Ring\Client\CurlHandler;
$handler = new CurlHandler();
$response = $handler([
'http_method' => 'GET',
'uri' => '/',
'headers' => [
'host' => ['www.google.com'],
'x-foo' => ['baz']
]
]);
$response->then(function (array $response) {
echo $response['status'];
});
$response->wait();
RingPHP is inspired by Clojure's `Ring <https://github.com/ring-clojure/ring>`_,
which, in turn, was inspired by Python's WSGI and Ruby's Rack. RingPHP is
utilized as the handler layer in `Guzzle <http://guzzlephp.org>`_ 5.0+ to send
HTTP requests.

View File

@ -8,7 +8,7 @@ middleware.
Handlers
--------
Handlers are implemented as a PHP ``callable`` that accept a request array and
Handlers are implemented as a PHP ``callable`` that accept a request array
and return a response array (``GuzzleHttp\Ring\Future\FutureArrayInterface``).
For example:

View File

@ -76,20 +76,17 @@ class CurlFactory
$response['effective_url'] = $response['transfer_stats']['url'];
}
if (isset($headers[0])) {
if (!empty($headers)) {
$startLine = explode(' ', array_shift($headers), 3);
// Trim out 100-Continue start-lines
if ($startLine[1] == '100') {
$startLine = explode(' ', array_shift($headers), 3);
}
$response['headers'] = Core::headersFromLines($headers);
$headerList = Core::headersFromLines($headers);
$response['headers'] = $headerList;
$response['status'] = isset($startLine[1]) ? (int) $startLine[1] : null;
$response['reason'] = isset($startLine[2]) ? $startLine[2] : null;
$response['body'] = $body;
Core::rewindBody($response);
}
return !empty($response['curl']['errno']) || !isset($startLine[1])
return !empty($response['curl']['errno']) || !isset($response['status'])
? self::createErrorResponse($handler, $request, $response)
: $response;
}
@ -159,6 +156,7 @@ class CurlFactory
private function getDefaultOptions(array $request, array &$headers)
{
$url = Core::url($request);
$startingResponse = false;
$options = [
'_headers' => $request['headers'],
@ -167,15 +165,24 @@ class CurlFactory
CURLOPT_RETURNTRANSFER => false,
CURLOPT_HEADER => false,
CURLOPT_CONNECTTIMEOUT => 150,
CURLOPT_HEADERFUNCTION => function ($ch, $h) use (&$headers) {
$length = strlen($h);
if ($value = trim($h)) {
$headers[] = trim($h);
CURLOPT_HEADERFUNCTION => function ($ch, $h) use (&$headers, &$startingResponse) {
$value = trim($h);
if ($value === '') {
$startingResponse = true;
} elseif ($startingResponse) {
$startingResponse = false;
$headers = [$value];
} else {
$headers[] = $value;
}
return $length;
return strlen($h);
},
];
if (isset($request['version'])) {
$options[CURLOPT_HTTP_VERSION] = $request['version'] == 1.1 ? CURL_HTTP_VERSION_1_1 : CURL_HTTP_VERSION_1_0;
}
if (defined('CURLOPT_PROTOCOLS')) {
$options[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS;
}

View File

@ -214,10 +214,10 @@ class StreamHandler
private function getDefaultOptions(array $request)
{
$headers = [];
$headers = "";
foreach ($request['headers'] as $name => $value) {
foreach ((array) $value as $val) {
$headers[] = "$name: $val";
$headers .= "$name: $val\r\n";
}
}
@ -225,7 +225,7 @@ class StreamHandler
'http' => [
'method' => $request['http_method'],
'header' => $headers,
'protocol_version' => '1.1',
'protocol_version' => isset($request['version']) ? $request['version'] : 1.1,
'ignore_errors' => true,
'follow_location' => 0,
],
@ -236,10 +236,12 @@ class StreamHandler
$context['http']['content'] = $body;
// Prevent the HTTP handler from adding a Content-Type header.
if (!Core::hasHeader($request, 'Content-Type')) {
$context['http']['header'][] .= "Content-Type:";
$context['http']['header'] .= "Content-Type:\r\n";
}
}
$context['http']['header'] = rtrim($context['http']['header']);
return $context;
}

View File

@ -41,24 +41,17 @@ class Core
*/
public static function headerLines($message, $header)
{
// Slight optimization for exact matches.
if (isset($message['headers'][$header])) {
return $message['headers'][$header];
}
$result = [];
// Check for message with no headers after the "fast" isset check.
if (!isset($message['headers'])) {
return [];
}
// Iterate and case-insensitively find the header by name.
foreach ($message['headers'] as $name => $value) {
if (!strcasecmp($name, $header)) {
return $value;
if (!empty($message['headers'])) {
foreach ($message['headers'] as $name => $value) {
if (!strcasecmp($name, $header)) {
$result = array_merge($result, $value);
}
}
}
return [];
return $result;
}
/**
@ -91,18 +84,17 @@ class Core
*/
public static function firstHeader($message, $header)
{
$match = self::headerLines($message, $header);
if (!isset($match[0])) {
return null;
if (!empty($message['headers'])) {
foreach ($message['headers'] as $name => $value) {
if (!strcasecmp($name, $header)) {
// Return the match itself if it is a single value.
$pos = strpos($value[0], ',');
return $pos ? substr($value[0], 0, $pos) : $value[0];
}
}
}
// Return the match itself if it is a single value.
if (!($pos = strpos($match[0], ','))) {
return $match[0];
}
return substr($match[0], 0, $pos);
return null;
}
/**
@ -115,7 +107,15 @@ class Core
*/
public static function hasHeader($message, $header)
{
return (bool) self::headerLines($message, $header);
if (!empty($message['headers'])) {
foreach ($message['headers'] as $name => $value) {
if (!strcasecmp($name, $header)) {
return true;
}
}
}
return false;
}
/**

View File

@ -28,9 +28,7 @@ trait BaseFutureTrait
private $isRealized = false;
/**
* @param PromiseInterface $promise Promise to shadow with the future. Only
* supply if the promise is not owned
* by the deferred value.
* @param PromiseInterface $promise Promise to shadow with the future.
* @param callable $wait Function that blocks until the deferred
* computation has been resolved. This
* function MUST resolve the deferred value

View File

@ -404,6 +404,19 @@ class CurlFactoryTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($content, Core::body($response));
}
public function testProtocolVersion()
{
Server::flush();
Server::enqueue([['status' => 200]]);
$a = new CurlMultiHandler();
$a([
'http_method' => 'GET',
'headers' => ['host' => [Server::$host]],
'version' => 1.0,
]);
$this->assertEquals(CURL_HTTP_VERSION_1_0, $_SERVER['_curl'][CURLOPT_HTTP_VERSION]);
}
/**
* @expectedException \InvalidArgumentException
*/
@ -570,7 +583,7 @@ class CurlFactoryTest extends \PHPUnit_Framework_TestCase
'http_method' => 'GET',
'headers' => [
'host' => [Server::$host],
'content-length' => 3,
'content-length' => [3],
],
'body' => 'foo',
]);
@ -709,6 +722,71 @@ class CurlFactoryTest extends \PHPUnit_Framework_TestCase
);
$this->assertInstanceOf('GuzzleHttp\Ring\Exception\ConnectException', $response['error']);
}
public function testParsesLastResponseOnly()
{
$response1 = [
'status' => 301,
'headers' => [
'Content-Length' => ['0'],
'Location' => ['/foo']
]
];
$response2 = [
'status' => 200,
'headers' => [
'Content-Length' => ['0'],
'Foo' => ['bar']
]
];
Server::flush();
Server::enqueue([$response1, $response2]);
$a = new CurlMultiHandler();
$response = $a([
'http_method' => 'GET',
'headers' => ['Host' => [Server::$host]],
'client' => [
'curl' => [
CURLOPT_FOLLOWLOCATION => true
]
]
])->wait();
$this->assertEquals(1, $response['transfer_stats']['redirect_count']);
$this->assertEquals('http://127.0.0.1:8125/foo', $response['effective_url']);
$this->assertEquals(['bar'], $response['headers']['Foo']);
$this->assertEquals(200, $response['status']);
$this->assertFalse(Core::hasHeader($response, 'Location'));
}
public function testMaintainsMultiHeaderOrder()
{
Server::flush();
Server::enqueue([
[
'status' => 200,
'headers' => [
'Content-Length' => ['0'],
'Foo' => ['a', 'b'],
'foo' => ['c', 'd'],
]
]
]);
$a = new CurlMultiHandler();
$response = $a([
'http_method' => 'GET',
'headers' => ['Host' => [Server::$host]]
])->wait();
$this->assertEquals(
['a', 'b', 'c', 'd'],
Core::headerLines($response, 'Foo')
);
}
}
}

View File

@ -202,6 +202,20 @@ class StreamHandlerTest extends \PHPUnit_Framework_TestCase
$this->assertSame($content, Core::body($response));
}
public function testProtocolVersion()
{
$this->queueRes();
$handler = new StreamHandler();
$handler([
'http_method' => 'GET',
'uri' => '/',
'headers' => ['host' => [Server::$host]],
'version' => 1.0,
]);
$this->assertEquals(1.0, Server::received()[0]['version']);
}
protected function getSendResult(array $opts)
{
$this->queueRes();

View File

@ -15,6 +15,22 @@ class CoreTest extends \PHPUnit_Framework_TestCase
$this->assertNull(Core::firstHeader([], 'Foo'));
}
public function testChecksIfHasHeader()
{
$message = [
'headers' => [
'Foo' => ['Bar', 'Baz'],
'foo' => ['hello'],
'bar' => ['1']
]
];
$this->assertTrue(Core::hasHeader($message, 'Foo'));
$this->assertTrue(Core::hasHeader($message, 'foo'));
$this->assertTrue(Core::hasHeader($message, 'FoO'));
$this->assertTrue(Core::hasHeader($message, 'bar'));
$this->assertFalse(Core::hasHeader($message, 'barr'));
}
public function testReturnsFirstHeaderWhenSimple()
{
$this->assertEquals('Bar', Core::firstHeader([
@ -37,6 +53,19 @@ class CoreTest extends \PHPUnit_Framework_TestCase
);
}
public function testExtractsCaseInsensitiveHeaderLines()
{
$this->assertEquals(
['a', 'b', 'c', 'd'],
Core::headerLines([
'headers' => [
'foo' => ['a', 'b'],
'Foo' => ['c', 'd']
]
], 'foo')
);
}
public function testExtractsHeaderLines()
{
$this->assertEquals(

View File

@ -23,7 +23,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "1.1.x-dev"
"dev-master": "1.2.x-dev"
}
}
}

View File

@ -68,4 +68,28 @@ class Console
return function_exists('posix_isatty') && @posix_isatty(STDOUT);
}
/**
* Returns the number of columns of the terminal.
*
* @return integer
*/
public function getNumberOfColumns()
{
// Windows terminals have a fixed size of 80
// but one column is used for the cursor.
if (DIRECTORY_SEPARATOR == '\\') {
return 79;
}
if (preg_match('#\d+ (\d+)#', shell_exec('stty size'), $match) === 1) {
return (int) $match[1];
}
if (preg_match('#columns = (\d+);#', shell_exec('stty'), $match) === 1) {
return (int) $match[1];
}
return 80;
}
}

View File

@ -62,6 +62,14 @@ class ConsoleTest extends PHPUnit_Framework_TestCase
*/
public function testCanDetectColorSupport()
{
$this->assertTrue($this->console->hasColorSupport());
$this->assertInternalType('boolean', $this->console->hasColorSupport());
}
/**
* @covers \SebastianBergmann\Environment\Console::hasColorSupport
*/
public function testCanDetectNumberOfColumns()
{
$this->assertInternalType('integer', $this->console->getNumberOfColumns());
}
}

View File

@ -37,8 +37,8 @@ namespace Symfony\Component\ClassLoader;
* // register classes with namespaces
* $loader->registerNamespaces(array(
* 'Symfony\Component' => __DIR__.'/component',
* 'Symfony' => __DIR__.'/framework',
* 'Sensio' => array(__DIR__.'/src', __DIR__.'/vendor'),
* 'Symfony' => __DIR__.'/framework',
* 'Sensio' => array(__DIR__.'/src', __DIR__.'/vendor'),
* ));
*
* // register a library using the PEAR naming convention

View File

@ -107,7 +107,7 @@ class ClassLoader
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
$this->useIncludePath = (bool) $useIncludePath;
}
/**

View File

@ -86,7 +86,7 @@ class ClassMapGenerator
private static function findClasses($path)
{
$contents = file_get_contents($path);
$tokens = token_get_all($contents);
$tokens = token_get_all($contents);
$classes = array();

View File

@ -9,51 +9,63 @@ standard or the PEAR naming convention.
First, register the autoloader:
require_once __DIR__.'/src/Symfony/Component/ClassLoader/UniversalClassLoader.php';
```php
require_once __DIR__.'/src/Symfony/Component/ClassLoader/UniversalClassLoader.php';
use Symfony\Component\ClassLoader\UniversalClassLoader;
use Symfony\Component\ClassLoader\UniversalClassLoader;
$loader = new UniversalClassLoader();
$loader->register();
$loader = new UniversalClassLoader();
$loader->register();
```
Then, register some namespaces with the `registerNamespace()` method:
$loader->registerNamespace('Symfony', __DIR__.'/src');
$loader->registerNamespace('Monolog', __DIR__.'/vendor/monolog/src');
```php
$loader->registerNamespace('Symfony', __DIR__.'/src');
$loader->registerNamespace('Monolog', __DIR__.'/vendor/monolog/src');
```
The `registerNamespace()` method takes a namespace prefix and a path where to
look for the classes as arguments.
You can also register a sub-namespaces:
$loader->registerNamespace('Doctrine\\Common', __DIR__.'/vendor/doctrine-common/lib');
```php
$loader->registerNamespace('Doctrine\\Common', __DIR__.'/vendor/doctrine-common/lib');
```
The order of registration is significant and the first registered namespace
takes precedence over later registered one.
You can also register more than one path for a given namespace:
$loader->registerNamespace('Symfony', array(__DIR__.'/src', __DIR__.'/symfony/src'));
```php
$loader->registerNamespace('Symfony', array(__DIR__.'/src', __DIR__.'/symfony/src'));
```
Alternatively, you can use the `registerNamespaces()` method to register more
than one namespace at once:
$loader->registerNamespaces(array(
'Symfony' => array(__DIR__.'/src', __DIR__.'/symfony/src'),
'Doctrine\\Common' => __DIR__.'/vendor/doctrine-common/lib',
'Doctrine' => __DIR__.'/vendor/doctrine/lib',
'Monolog' => __DIR__.'/vendor/monolog/src',
));
```php
$loader->registerNamespaces(array(
'Symfony' => array(__DIR__.'/src', __DIR__.'/symfony/src'),
'Doctrine\\Common' => __DIR__.'/vendor/doctrine-common/lib',
'Doctrine' => __DIR__.'/vendor/doctrine/lib',
'Monolog' => __DIR__.'/vendor/monolog/src',
));
```
For better performance, you can use the APC based version of the universal
class loader:
require_once __DIR__.'/src/Symfony/Component/ClassLoader/UniversalClassLoader.php';
require_once __DIR__.'/src/Symfony/Component/ClassLoader/ApcUniversalClassLoader.php';
```php
require_once __DIR__.'/src/Symfony/Component/ClassLoader/UniversalClassLoader.php';
require_once __DIR__.'/src/Symfony/Component/ClassLoader/ApcUniversalClassLoader.php';
use Symfony\Component\ClassLoader\ApcUniversalClassLoader;
use Symfony\Component\ClassLoader\ApcUniversalClassLoader;
$loader = new ApcUniversalClassLoader('apc.prefix.');
$loader = new ApcUniversalClassLoader('apc.prefix.');
```
Furthermore, the component provides tools to aggregate classes into a single
file, which is especially useful to improve performance on servers that do not

View File

@ -72,9 +72,9 @@ class ClassMapGeneratorTest extends \PHPUnit_Framework_TestCase
{
$data = array(
array(__DIR__.'/Fixtures/Namespaced', array(
'Namespaced\\Bar' => realpath(__DIR__).'/Fixtures/Namespaced/Bar.php',
'Namespaced\\Foo' => realpath(__DIR__).'/Fixtures/Namespaced/Foo.php',
'Namespaced\\Baz' => realpath(__DIR__).'/Fixtures/Namespaced/Baz.php',
'Namespaced\\Bar' => realpath(__DIR__).'/Fixtures/Namespaced/Bar.php',
'Namespaced\\Foo' => realpath(__DIR__).'/Fixtures/Namespaced/Foo.php',
'Namespaced\\Baz' => realpath(__DIR__).'/Fixtures/Namespaced/Baz.php',
'Namespaced\\WithComments' => realpath(__DIR__).'/Fixtures/Namespaced/WithComments.php',
),
),
@ -85,22 +85,22 @@ class ClassMapGeneratorTest extends \PHPUnit_Framework_TestCase
'NamespaceCollision\\C\\B\\Foo' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/C/B/Foo.php',
)),
array(__DIR__.'/Fixtures/Pearlike', array(
'Pearlike_Foo' => realpath(__DIR__).'/Fixtures/Pearlike/Foo.php',
'Pearlike_Bar' => realpath(__DIR__).'/Fixtures/Pearlike/Bar.php',
'Pearlike_Baz' => realpath(__DIR__).'/Fixtures/Pearlike/Baz.php',
'Pearlike_Foo' => realpath(__DIR__).'/Fixtures/Pearlike/Foo.php',
'Pearlike_Bar' => realpath(__DIR__).'/Fixtures/Pearlike/Bar.php',
'Pearlike_Baz' => realpath(__DIR__).'/Fixtures/Pearlike/Baz.php',
'Pearlike_WithComments' => realpath(__DIR__).'/Fixtures/Pearlike/WithComments.php',
)),
array(__DIR__.'/Fixtures/classmap', array(
'Foo\\Bar\\A' => realpath(__DIR__).'/Fixtures/classmap/sameNsMultipleClasses.php',
'Foo\\Bar\\B' => realpath(__DIR__).'/Fixtures/classmap/sameNsMultipleClasses.php',
'A' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php',
'Alpha\\A' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php',
'Alpha\\B' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php',
'Beta\\A' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php',
'Beta\\B' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php',
'Foo\\Bar\\A' => realpath(__DIR__).'/Fixtures/classmap/sameNsMultipleClasses.php',
'Foo\\Bar\\B' => realpath(__DIR__).'/Fixtures/classmap/sameNsMultipleClasses.php',
'A' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php',
'Alpha\\A' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php',
'Alpha\\B' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php',
'Beta\\A' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php',
'Beta\\B' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php',
'ClassMap\\SomeInterface' => realpath(__DIR__).'/Fixtures/classmap/SomeInterface.php',
'ClassMap\\SomeParent' => realpath(__DIR__).'/Fixtures/classmap/SomeParent.php',
'ClassMap\\SomeClass' => realpath(__DIR__).'/Fixtures/classmap/SomeClass.php',
'ClassMap\\SomeParent' => realpath(__DIR__).'/Fixtures/classmap/SomeParent.php',
'ClassMap\\SomeClass' => realpath(__DIR__).'/Fixtures/classmap/SomeClass.php',
)),
);

View File

@ -32,8 +32,8 @@ namespace Symfony\Component\ClassLoader;
* // register classes with namespaces
* $loader->registerNamespaces(array(
* 'Symfony\Component' => __DIR__.'/component',
* 'Symfony' => __DIR__.'/framework',
* 'Sensio' => array(__DIR__.'/src', __DIR__.'/vendor'),
* 'Symfony' => __DIR__.'/framework',
* 'Sensio' => array(__DIR__.'/src', __DIR__.'/vendor'),
* ));
*
* // register a library using the PEAR naming convention
@ -74,7 +74,7 @@ class UniversalClassLoader
*/
public function useIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
$this->useIncludePath = (bool) $useIncludePath;
}
/**

View File

@ -28,7 +28,7 @@
"target-dir": "Symfony/Component/ClassLoader",
"extra": {
"branch-alias": {
"dev-master": "2.5-dev"
"dev-master": "2.6-dev"
}
}
}

View File

@ -75,4 +75,28 @@ class Specificity
{
return $this->a * self::A_FACTOR + $this->b * self::B_FACTOR + $this->c * self::C_FACTOR;
}
/**
* Returns -1 if the object specificity is lower than the argument,
* 0 if they are equal, and 1 if the argument is lower
*
* @param Specificity $specificity
* @return int
*/
public function compareTo(Specificity $specificity)
{
if ($this->a !== $specificity->a) {
return $this->a > $specificity->a ? 1 : -1;
}
if ($this->b !== $specificity->b) {
return $this->b > $specificity->b ? 1 : -1;
}
if ($this->c !== $specificity->c) {
return $this->c > $specificity->c ? 1 : -1;
}
return 0;
}
}

View File

@ -21,13 +21,13 @@ namespace Symfony\Component\CssSelector\Parser;
*/
class Token
{
const TYPE_FILE_END = 'eof';
const TYPE_DELIMITER = 'delimiter';
const TYPE_FILE_END = 'eof';
const TYPE_DELIMITER = 'delimiter';
const TYPE_WHITESPACE = 'whitespace';
const TYPE_IDENTIFIER = 'identifier';
const TYPE_HASH = 'hash';
const TYPE_NUMBER = 'number';
const TYPE_STRING = 'string';
const TYPE_HASH = 'hash';
const TYPE_NUMBER = 'number';
const TYPE_STRING = 'string';
/**
* @var int

View File

@ -6,9 +6,11 @@ CssSelector converts CSS selectors to XPath expressions.
The component only goal is to convert CSS selectors to their XPath
equivalents:
use Symfony\Component\CssSelector\CssSelector;
```php
use Symfony\Component\CssSelector\CssSelector;
print CssSelector::toXPath('div.item > h4 > a');
print CssSelector::toXPath('div.item > h4 > a');
```
HTML and XML are different
--------------------------
@ -18,11 +20,13 @@ default. If you need to use this component with `XML` documents, you have to
disable this `HTML` extension. That's because, `HTML` tag & attribute names
are always lower-cased, but case-sensitive in `XML`:
// disable `HTML` extension:
CssSelector::disableHtmlExtension();
```php
// disable `HTML` extension:
CssSelector::disableHtmlExtension();
// re-enable `HTML` extension:
CssSelector::enableHtmlExtension();
// re-enable `HTML` extension:
CssSelector::enableHtmlExtension();
```
When the `HTML` extension is enabled, tag names are lower-cased, attribute
names are lower-cased, the following extra pseudo-classes are supported:

View File

@ -37,4 +37,26 @@ class SpecificityTest extends \PHPUnit_Framework_TestCase
array(new Specificity(4, 3, 2), 432),
);
}
/** @dataProvider getCompareTestData */
public function testCompareTo(Specificity $a, Specificity $b, $result)
{
$this->assertEquals($result, $a->compareTo($b));
}
public function getCompareTestData()
{
return array(
array(new Specificity(0, 0, 0), new Specificity(0, 0, 0), 0),
array(new Specificity(0, 0, 1), new Specificity(0, 0, 1), 0),
array(new Specificity(0, 0, 2), new Specificity(0, 0, 1), 1),
array(new Specificity(0, 0, 2), new Specificity(0, 0, 3), -1),
array(new Specificity(0, 4, 0), new Specificity(0, 4, 0), 0),
array(new Specificity(0, 6, 0), new Specificity(0, 5, 11), 1),
array(new Specificity(0, 7, 0), new Specificity(0, 8, 0), -1),
array(new Specificity(9, 0, 0), new Specificity(9, 0, 0), 0),
array(new Specificity(11, 0, 0), new Specificity(10, 11, 0), 1),
array(new Specificity(12, 11, 0), new Specificity(13, 0, 0), -1),
);
}
}

View File

@ -31,13 +31,13 @@ class AttributeMatchingExtension extends AbstractExtension
{
return array(
'exists' => array($this, 'translateExists'),
'=' => array($this, 'translateEquals'),
'~=' => array($this, 'translateIncludes'),
'|=' => array($this, 'translateDashMatch'),
'^=' => array($this, 'translatePrefixMatch'),
'$=' => array($this, 'translateSuffixMatch'),
'*=' => array($this, 'translateSubstringMatch'),
'!=' => array($this, 'translateDifferent'),
'=' => array($this, 'translateEquals'),
'~=' => array($this, 'translateIncludes'),
'|=' => array($this, 'translateDashMatch'),
'^=' => array($this, 'translatePrefixMatch'),
'$=' => array($this, 'translateSuffixMatch'),
'*=' => array($this, 'translateSubstringMatch'),
'!=' => array($this, 'translateDifferent'),
);
}

View File

@ -34,12 +34,12 @@ class FunctionExtension extends AbstractExtension
public function getFunctionTranslators()
{
return array(
'nth-child' => array($this, 'translateNthChild'),
'nth-last-child' => array($this, 'translateNthLastChild'),
'nth-of-type' => array($this, 'translateNthOfType'),
'nth-child' => array($this, 'translateNthChild'),
'nth-last-child' => array($this, 'translateNthLastChild'),
'nth-of-type' => array($this, 'translateNthOfType'),
'nth-last-of-type' => array($this, 'translateNthLastOfType'),
'contains' => array($this, 'translateContains'),
'lang' => array($this, 'translateLang'),
'contains' => array($this, 'translateContains'),
'lang' => array($this, 'translateLang'),
);
}

View File

@ -45,14 +45,14 @@ class HtmlExtension extends AbstractExtension
public function getPseudoClassTranslators()
{
return array(
'checked' => array($this, 'translateChecked'),
'link' => array($this, 'translateLink'),
'checked' => array($this, 'translateChecked'),
'link' => array($this, 'translateLink'),
'disabled' => array($this, 'translateDisabled'),
'enabled' => array($this, 'translateEnabled'),
'enabled' => array($this, 'translateEnabled'),
'selected' => array($this, 'translateSelected'),
'invalid' => array($this, 'translateInvalid'),
'hover' => array($this, 'translateHover'),
'visited' => array($this, 'translateVisited'),
'invalid' => array($this, 'translateInvalid'),
'hover' => array($this, 'translateHover'),
'visited' => array($this, 'translateVisited'),
);
}

View File

@ -25,8 +25,8 @@ use Symfony\Component\CssSelector\XPath\XPathExpr;
*/
class NodeExtension extends AbstractExtension
{
const ELEMENT_NAME_IN_LOWER_CASE = 1;
const ATTRIBUTE_NAME_IN_LOWER_CASE = 2;
const ELEMENT_NAME_IN_LOWER_CASE = 1;
const ATTRIBUTE_NAME_IN_LOWER_CASE = 2;
const ATTRIBUTE_VALUE_IN_LOWER_CASE = 4;
/**
@ -79,15 +79,15 @@ class NodeExtension extends AbstractExtension
public function getNodeTranslators()
{
return array(
'Selector' => array($this, 'translateSelector'),
'Selector' => array($this, 'translateSelector'),
'CombinedSelector' => array($this, 'translateCombinedSelector'),
'Negation' => array($this, 'translateNegation'),
'Function' => array($this, 'translateFunction'),
'Pseudo' => array($this, 'translatePseudo'),
'Attribute' => array($this, 'translateAttribute'),
'Class' => array($this, 'translateClass'),
'Hash' => array($this, 'translateHash'),
'Element' => array($this, 'translateElement'),
'Negation' => array($this, 'translateNegation'),
'Function' => array($this, 'translateFunction'),
'Pseudo' => array($this, 'translatePseudo'),
'Attribute' => array($this, 'translateAttribute'),
'Class' => array($this, 'translateClass'),
'Hash' => array($this, 'translateHash'),
'Element' => array($this, 'translateElement'),
);
}

View File

@ -30,14 +30,14 @@ class PseudoClassExtension extends AbstractExtension
public function getPseudoClassTranslators()
{
return array(
'root' => array($this, 'translateRoot'),
'first-child' => array($this, 'translateFirstChild'),
'last-child' => array($this, 'translateLastChild'),
'root' => array($this, 'translateRoot'),
'first-child' => array($this, 'translateFirstChild'),
'last-child' => array($this, 'translateLastChild'),
'first-of-type' => array($this, 'translateFirstOfType'),
'last-of-type' => array($this, 'translateLastOfType'),
'only-child' => array($this, 'translateOnlyChild'),
'only-of-type' => array($this, 'translateOnlyOfType'),
'empty' => array($this, 'translateEmpty'),
'last-of-type' => array($this, 'translateLastOfType'),
'only-child' => array($this, 'translateOnlyChild'),
'only-of-type' => array($this, 'translateOnlyOfType'),
'empty' => array($this, 'translateEmpty'),
);
}

View File

@ -123,17 +123,15 @@ class Translator implements TranslatorInterface
$selectors = $this->parseSelectors($cssExpr);
/** @var SelectorNode $selector */
foreach ($selectors as $selector) {
foreach ($selectors as $index => $selector) {
if (null !== $selector->getPseudoElement()) {
throw new ExpressionErrorException('Pseudo-elements are not supported.');
}
$selectors[$index] = $this->selectorToXPath($selector, $prefix);
}
$translator = $this;
return implode(' | ', array_map(function (SelectorNode $selector) use ($translator, $prefix) {
return $translator->selectorToXPath($selector, $prefix);
}, $selectors));
return implode(' | ', $selectors);
}
/**

View File

@ -29,7 +29,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "2.5-dev"
"dev-master": "2.6-dev"
}
}
}

View File

@ -1,6 +1,13 @@
CHANGELOG
=========
2.6.0
-----
* generalized ErrorHandler and ExceptionHandler,
with some new methods and others deprecated
* enhanced error messages for uncaught exceptions
2.5.0
-----

View File

@ -39,15 +39,23 @@ class Debug
static::$enabled = true;
error_reporting(-1);
if (null !== $errorReportingLevel) {
error_reporting($errorReportingLevel);
} else {
error_reporting(-1);
}
ErrorHandler::register($errorReportingLevel, $displayErrors);
if ('cli' !== php_sapi_name()) {
ini_set('display_errors', 0);
ExceptionHandler::register();
// CLI - display errors only if they're not already logged to STDERR
} elseif ($displayErrors && (!ini_get('log_errors') || ini_get('error_log'))) {
// CLI - display errors only if they're not already logged to STDERR
ini_set('display_errors', 1);
}
$handler = ErrorHandler::register();
if (!$displayErrors) {
$handler->throwAt(0, true);
}
DebugClassLoader::enable();
}

View File

@ -78,7 +78,8 @@ class DebugClassLoader
public static function enable()
{
// Ensures we don't hit https://bugs.php.net/42098
class_exists(__NAMESPACE__.'\ErrorHandler', true);
class_exists('Symfony\Component\Debug\ErrorHandler');
class_exists('Psr\Log\LogLevel');
if (!is_array($functions = spl_autoload_functions())) {
return;

View File

@ -22,188 +22,511 @@ use Symfony\Component\Debug\FatalErrorHandler\ClassNotFoundFatalErrorHandler;
use Symfony\Component\Debug\FatalErrorHandler\FatalErrorHandlerInterface;
/**
* ErrorHandler.
* A generic ErrorHandler for the PHP engine.
*
* Provides five bit fields that control how errors are handled:
* - thrownErrors: errors thrown as \ErrorException
* - loggedErrors: logged errors, when not @-silenced
* - scopedErrors: errors thrown or logged with their local context
* - tracedErrors: errors logged with their stack trace, only once for repeated errors
* - screamedErrors: never @-silenced errors
*
* Each error level can be logged by a dedicated PSR-3 logger object.
* Screaming only applies to logging.
* Throwing takes precedence over logging.
* Uncaught exceptions are logged as E_ERROR.
* E_DEPRECATED and E_USER_DEPRECATED levels never throw.
* E_RECOVERABLE_ERROR and E_USER_ERROR levels always throw.
* Non catchable errors that can be detected at shutdown time are logged when the scream bit field allows so.
* As errors have a performance cost, repeated errors are all logged, so that the developer
* can see them and weight them as more important to fix than others of the same level.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Konstantin Myakshin <koc-dp@yandex.ru>
* @author Nicolas Grekas <p@tchwork.com>
*/
class ErrorHandler
{
/**
* @deprecated since 2.6, to be removed in 3.0.
*/
const TYPE_DEPRECATION = -100;
private $levels = array(
E_WARNING => 'Warning',
E_NOTICE => 'Notice',
E_USER_ERROR => 'User Error',
E_USER_WARNING => 'User Warning',
E_USER_NOTICE => 'User Notice',
E_STRICT => 'Runtime Notice',
E_DEPRECATED => 'Deprecated',
E_USER_DEPRECATED => 'User Deprecated',
E_NOTICE => 'Notice',
E_USER_NOTICE => 'User Notice',
E_STRICT => 'Runtime Notice',
E_WARNING => 'Warning',
E_USER_WARNING => 'User Warning',
E_COMPILE_WARNING => 'Compile Warning',
E_CORE_WARNING => 'Core Warning',
E_USER_ERROR => 'User Error',
E_RECOVERABLE_ERROR => 'Catchable Fatal Error',
E_DEPRECATED => 'Deprecated',
E_USER_DEPRECATED => 'User Deprecated',
E_ERROR => 'Error',
E_CORE_ERROR => 'Core Error',
E_COMPILE_ERROR => 'Compile Error',
E_PARSE => 'Parse Error',
E_COMPILE_ERROR => 'Compile Error',
E_PARSE => 'Parse Error',
E_ERROR => 'Error',
E_CORE_ERROR => 'Core Error',
);
private $level;
private $loggers = array(
E_DEPRECATED => array(null, LogLevel::INFO),
E_USER_DEPRECATED => array(null, LogLevel::INFO),
E_NOTICE => array(null, LogLevel::NOTICE),
E_USER_NOTICE => array(null, LogLevel::NOTICE),
E_STRICT => array(null, LogLevel::NOTICE),
E_WARNING => array(null, LogLevel::WARNING),
E_USER_WARNING => array(null, LogLevel::WARNING),
E_COMPILE_WARNING => array(null, LogLevel::WARNING),
E_CORE_WARNING => array(null, LogLevel::WARNING),
E_USER_ERROR => array(null, LogLevel::ERROR),
E_RECOVERABLE_ERROR => array(null, LogLevel::ERROR),
E_COMPILE_ERROR => array(null, LogLevel::EMERGENCY),
E_PARSE => array(null, LogLevel::EMERGENCY),
E_ERROR => array(null, LogLevel::EMERGENCY),
E_CORE_ERROR => array(null, LogLevel::EMERGENCY),
);
private $reservedMemory;
private $thrownErrors = 0x1FFF; // E_ALL - E_DEPRECATED - E_USER_DEPRECATED
private $scopedErrors = 0x1FFF; // E_ALL - E_DEPRECATED - E_USER_DEPRECATED
private $tracedErrors = 0x77FB; // E_ALL - E_STRICT - E_PARSE
private $screamedErrors = 0x55; // E_ERROR + E_CORE_ERROR + E_COMPILE_ERROR + E_PARSE
private $loggedErrors = 0;
private $displayErrors;
private $loggedTraces = array();
private $isRecursive = 0;
private $exceptionHandler;
private static $reservedMemory;
private static $stackedErrors = array();
private static $stackedErrorLevels = array();
/**
* @var LoggerInterface[] Loggers for channels
* Same init value as thrownErrors
*
* @deprecated since 2.6, to be removed in 3.0.
*/
private static $loggers = array();
private static $stackedErrors = array();
private static $stackedErrorLevels = array();
private $displayErrors = 0x1FFF;
/**
* Registers the error handler.
*
* @param int $level The level at which the conversion to Exception is done (null to use the error_reporting() value and 0 to disable)
* @param bool $displayErrors Display errors (for dev environment) or just log them (production usage)
* @param self|null|int $handler The handler to register, or @deprecated (since 2.6, to be removed in 3.0) bit field of thrown levels
* @param bool $replace Whether to replace or not any existing handler
*
* @return ErrorHandler The registered error handler
* @return self The registered error handler
*/
public static function register($level = null, $displayErrors = true)
public static function register($handler = null, $replace = true)
{
$handler = new static();
$handler->setLevel($level);
$handler->setDisplayErrors($displayErrors);
if (null === self::$reservedMemory) {
self::$reservedMemory = str_repeat('x', 10240);
register_shutdown_function(__CLASS__.'::handleFatalError');
}
ini_set('display_errors', 0);
set_error_handler(array($handler, 'handle'));
register_shutdown_function(array($handler, 'handleFatal'));
$handler->reservedMemory = str_repeat('x', 10240);
$levels = -1;
if ($handlerIsNew = !$handler instanceof self) {
// @deprecated polymorphism, to be removed in 3.0
if (null !== $handler) {
$levels = $replace ? $handler : 0;
$replace = true;
}
$handler = new static();
}
$prev = set_error_handler(array($handler, 'handleError'), $handler->thrownErrors | $handler->loggedErrors);
if ($handlerIsNew && is_array($prev) && $prev[0] instanceof self) {
$handler = $prev[0];
$replace = false;
}
if ($replace || !$prev) {
$handler->setExceptionHandler(set_exception_handler(array($handler, 'handleException')));
} else {
restore_error_handler();
}
$handler->throwAt($levels & $handler->thrownErrors, true);
return $handler;
}
/**
* Sets the level at which the conversion to Exception is done.
* Sets a logger to non assigned errors levels.
*
* @param int|null $level The level (null to use the error_reporting() value and 0 to disable)
* @param LoggerInterface $logger A PSR-3 logger to put as default for the given levels
* @param array|int $levels An array map of E_* to LogLevel::* or an integer bit field of E_* constants
* @param bool $replace Whether to replace or not any existing logger
*/
public function setLevel($level)
public function setDefaultLogger(LoggerInterface $logger, $levels = null, $replace = false)
{
$this->level = null === $level ? error_reporting() : $level;
$loggers = array();
if (is_array($levels)) {
foreach ($levels as $type => $logLevel) {
if (empty($this->loggers[$type][0]) || $replace) {
$loggers[$type] = array($logger, $logLevel);
}
}
} else {
if (null === $levels) {
$levels = E_ALL | E_STRICT;
}
foreach ($this->loggers as $type => $log) {
if (($type & $levels) && (empty($log[0]) || $replace)) {
$log[0] = $logger;
$loggers[$type] = $log;
}
}
}
$this->setLoggers($loggers);
}
/**
* Sets the display_errors flag value.
* Sets a logger for each error level.
*
* @param int $displayErrors The display_errors flag value
* @param array $loggers Error levels to [LoggerInterface|null, LogLevel::*] map
*
* @return array The previous map
*
* @throws \InvalidArgumentException
*/
public function setDisplayErrors($displayErrors)
public function setLoggers(array $loggers)
{
$this->displayErrors = $displayErrors;
$prevLogged = $this->loggedErrors;
$prev = $this->loggers;
foreach ($loggers as $type => $log) {
if (!isset($prev[$type])) {
throw new \InvalidArgumentException('Unknown error type: '.$type);
}
if (!is_array($log)) {
$log = array($log);
} elseif (!array_key_exists(0, $log)) {
throw new \InvalidArgumentException('No logger provided');
}
if (null === $log[0]) {
$this->loggedErrors &= ~$type;
} elseif ($log[0] instanceof LoggerInterface) {
$this->loggedErrors |= $type;
} else {
throw new \InvalidArgumentException('Invalid logger provided');
}
$this->loggers[$type] = $log + $prev[$type];
}
$this->reRegister($prevLogged | $this->thrownErrors);
return $prev;
}
/**
* Sets a logger for the given channel.
* Sets a user exception handler.
*
* @param LoggerInterface $logger A logger interface
* @param string $channel The channel associated with the logger (deprecation, emergency or scream)
* @param callable $handler A handler that will be called on Exception
*
* @return callable|null The previous exception handler
*
* @throws \InvalidArgumentException
*/
public static function setLogger(LoggerInterface $logger, $channel = 'deprecation')
public function setExceptionHandler($handler)
{
self::$loggers[$channel] = $logger;
if (null !== $handler && !is_callable($handler)) {
throw new \LogicException('The exception handler must be a valid PHP callable.');
}
$prev = $this->exceptionHandler;
$this->exceptionHandler = $handler;
return $prev;
}
/**
* @throws \ErrorException When error_reporting returns error
* Sets the error levels that are to be thrown.
*
* @param int $levels A bit field of E_* constants for thrown errors
* @param bool $replace Replace or amend the previous value
*
* @return int The previous value
*/
public function handle($level, $message, $file = 'unknown', $line = 0, $context = array())
public function throwAt($levels, $replace = false)
{
if ($level & (E_USER_DEPRECATED | E_DEPRECATED)) {
if (isset(self::$loggers['deprecation'])) {
if (self::$stackedErrorLevels) {
self::$stackedErrors[] = func_get_args();
$prev = $this->thrownErrors;
$this->thrownErrors = ($levels | E_RECOVERABLE_ERROR | E_USER_ERROR) & ~E_USER_DEPRECATED & ~E_DEPRECATED;
if (!$replace) {
$this->thrownErrors |= $prev;
}
$this->reRegister($prev | $this->loggedErrors);
// $this->displayErrors is @deprecated since 2.6
$this->displayErrors = $this->thrownErrors;
return $prev;
}
/**
* Sets the error levels that are logged or thrown with their local scope.
*
* @param int $levels A bit field of E_* constants for scoped errors
* @param bool $replace Replace or amend the previous value
*
* @return int The previous value
*/
public function scopeAt($levels, $replace = false)
{
$prev = $this->scopedErrors;
$this->scopedErrors = (int) $levels;
if (!$replace) {
$this->scopedErrors |= $prev;
}
return $prev;
}
/**
* Sets the error levels that are logged with their stack trace.
*
* @param int $levels A bit field of E_* constants for traced errors
* @param bool $replace Replace or amend the previous value
*
* @return int The previous value
*/
public function traceAt($levels, $replace = false)
{
$prev = $this->tracedErrors;
$this->tracedErrors = (int) $levels;
if (!$replace) {
$this->tracedErrors |= $prev;
}
return $prev;
}
/**
* Sets the error levels where the @-operator is ignored.
*
* @param int $levels A bit field of E_* constants for screamed errors
* @param bool $replace Replace or amend the previous value
*
* @return int The previous value
*/
public function screamAt($levels, $replace = false)
{
$prev = $this->screamedErrors;
$this->screamedErrors = (int) $levels;
if (!$replace) {
$this->screamedErrors |= $prev;
}
return $prev;
}
/**
* Re-registers as a PHP error handler if levels changed.
*/
private function reRegister($prev)
{
if ($prev !== $this->thrownErrors | $this->loggedErrors) {
$handler = set_error_handler('var_dump', 0);
$handler = is_array($handler) ? $handler[0] : null;
restore_error_handler();
if ($handler === $this) {
restore_error_handler();
set_error_handler(array($this, 'handleError'), $this->thrownErrors | $this->loggedErrors);
}
}
}
/**
* Handles errors by filtering then logging them according to the configured bit fields.
*
* @param int $type One of the E_* constants
* @param string $file
* @param int $line
* @param array $context
*
* @return bool Returns false when no handling happens so that the PHP engine can handle the error itself.
*
* @throws \ErrorException When $this->thrownErrors requests so
*
* @internal
*/
public function handleError($type, $message, $file, $line, array $context)
{
$level = error_reporting() | E_RECOVERABLE_ERROR | E_USER_ERROR;
$log = $this->loggedErrors & $type;
$throw = $this->thrownErrors & $type & $level;
$type &= $level | $this->screamedErrors;
if ($type && ($log || $throw)) {
if (PHP_VERSION_ID < 50400 && isset($context['GLOBALS']) && ($this->scopedErrors & $type)) {
$e = $context; // Whatever the signature of the method,
unset($e['GLOBALS'], $context); // $context is always a reference in 5.3
$context = $e;
}
if ($throw) {
if (($this->scopedErrors & $type) && class_exists('Symfony\Component\Debug\Exception\ContextErrorException')) {
// Checking for class existence is a work around for https://bugs.php.net/42098
$throw = new ContextErrorException($this->levels[$type].': '.$message, 0, $type, $file, $line, $context);
} else {
if (version_compare(PHP_VERSION, '5.4', '<')) {
$stack = array_map(
function ($row) {
unset($row['args']);
$throw = new \ErrorException($this->levels[$type].': '.$message, 0, $type, $file, $line);
}
return $row;
},
array_slice(debug_backtrace(false), 0, 10)
);
} else {
$stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 10);
if (PHP_VERSION_ID <= 50407 && (PHP_VERSION_ID >= 50400 || PHP_VERSION_ID <= 50317)) {
// Exceptions thrown from error handlers are sometimes not caught by the exception
// handler and shutdown handlers are bypassed before 5.4.8/5.3.18.
// We temporarily re-enable display_errors to prevent any blank page related to this bug.
$throw->errorHandlerCanary = new ErrorHandlerCanary();
}
throw $throw;
}
// For duplicated errors, log the trace only once
$e = md5("{$type}/{$line}/{$file}\x00{$message}", true);
$trace = true;
if (!($this->tracedErrors & $type) || isset($this->loggedTraces[$e])) {
$trace = false;
} else {
$this->loggedTraces[$e] = 1;
}
$e = compact('type', 'file', 'line', 'level');
if ($type & $level) {
if ($this->scopedErrors & $type) {
$e['context'] = $context;
if ($trace) {
$e['stack'] = debug_backtrace(true); // Provide object
}
self::$loggers['deprecation']->warning($message, array('type' => self::TYPE_DEPRECATION, 'stack' => $stack));
} elseif ($trace) {
$e['stack'] = debug_backtrace(PHP_VERSION_ID >= 50306 ? DEBUG_BACKTRACE_IGNORE_ARGS : false);
}
return true;
}
} elseif ($this->displayErrors && error_reporting() & $level && $this->level & $level) {
if (PHP_VERSION_ID < 50400 && isset($context['GLOBALS']) && is_array($context)) {
$c = $context; // Whatever the signature of the method,
unset($c['GLOBALS'], $context); // $context is always a reference in 5.3
$context = $c;
}
$exception = sprintf('%s: %s in %s line %d', isset($this->levels[$level]) ? $this->levels[$level] : $level, $message, $file, $line);
if ($context && class_exists('Symfony\Component\Debug\Exception\ContextErrorException')) {
// Checking for class existence is a work around for https://bugs.php.net/42098
$exception = new ContextErrorException($exception, 0, $level, $file, $line, $context);
if ($this->isRecursive) {
$log = 0;
} elseif (self::$stackedErrorLevels) {
self::$stackedErrors[] = array($this->loggers[$type], $message, $e);
} else {
$exception = new \ErrorException($exception, 0, $level, $file, $line);
}
try {
$this->isRecursive = true;
$this->loggers[$type][0]->log($this->loggers[$type][1], $message, $e);
$this->isRecursive = false;
} catch (\Exception $e) {
$this->isRecursive = false;
if (PHP_VERSION_ID <= 50407 && (PHP_VERSION_ID >= 50400 || PHP_VERSION_ID <= 50317)) {
// Exceptions thrown from error handlers are sometimes not caught by the exception
// handler and shutdown handlers are bypassed before 5.4.8/5.3.18.
// We temporarily re-enable display_errors to prevent any blank page related to this bug.
$exception->errorHandlerCanary = new ErrorHandlerCanary();
}
throw $exception;
}
if (isset(self::$loggers['scream']) && !(error_reporting() & $level)) {
if (self::$stackedErrorLevels) {
self::$stackedErrors[] = func_get_args();
} else {
switch ($level) {
case E_USER_ERROR:
case E_RECOVERABLE_ERROR:
$logLevel = LogLevel::ERROR;
break;
case E_WARNING:
case E_USER_WARNING:
$logLevel = LogLevel::WARNING;
break;
default:
$logLevel = LogLevel::NOTICE;
break;
throw $e;
}
self::$loggers['scream']->log($logLevel, $message, array(
'type' => $level,
'file' => $file,
'line' => $line,
'scream' => error_reporting(),
));
}
}
return false;
return $type && $log;
}
/**
* Configure the error handler for delayed handling.
* Handles an exception by logging then forwarding it to an other handler.
*
* @param \Exception $exception An exception to handle
* @param array $error An array as returned by error_get_last()
*
* @internal
*/
public function handleException(\Exception $exception, array $error = null)
{
$level = error_reporting();
if ($this->loggedErrors & E_ERROR & ($level | $this->screamedErrors)) {
$e = array(
'type' => E_ERROR,
'file' => $exception->getFile(),
'line' => $exception->getLine(),
'level' => $level,
'stack' => $exception->getTrace(),
);
if ($exception instanceof FatalErrorException) {
$message = 'Fatal '.$exception->getMessage();
} elseif ($exception instanceof \ErrorException) {
$message = 'Uncaught '.$exception->getMessage();
if ($exception instanceof ContextErrorException) {
$e['context'] = $exception->getContext();
}
} else {
$message = 'Uncaught Exception: '.$exception->getMessage();
}
if ($this->loggedErrors & $e['type']) {
$this->loggers[$e['type']][0]->log($this->loggers[$e['type']][1], $message, $e);
}
}
if ($exception instanceof FatalErrorException && !$exception instanceof OutOfMemoryException && $error) {
foreach ($this->getFatalErrorHandlers() as $handler) {
if ($e = $handler->handleError($error, $exception)) {
$exception = $e;
break;
}
}
}
if (empty($this->exceptionHandler)) {
throw $exception; // Give back $exception to the native handler
}
try {
call_user_func($this->exceptionHandler, $exception);
} catch (\Exception $handlerException) {
$this->exceptionHandler = null;
$this->handleException($handlerException);
}
}
/**
* Shutdown registered function for handling PHP fatal errors.
*
* @param array $error An array as returned by error_get_last()
*
* @internal
*/
public static function handleFatalError(array $error = null)
{
self::$reservedMemory = '';
gc_collect_cycles();
$handler = set_error_handler('var_dump', 0);
$handler = is_array($handler) ? $handler[0] : null;
restore_error_handler();
if ($handler instanceof self) {
if (null === $error) {
$error = error_get_last();
}
try {
while (self::$stackedErrorLevels) {
static::unstackErrors();
}
} catch (\Exception $exception) {
// Handled below
}
if ($error && ($error['type'] & (E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR))) {
// Let's not throw anymore but keep logging
$handler->throwAt(0, true);
if (0 === strpos($error['message'], 'Allowed memory') || 0 === strpos($error['message'], 'Out of memory')) {
$exception = new OutOfMemoryException($handler->levels[$error['type']].': '.$error['message'], 0, $error['type'], $error['file'], $error['line'], 2, false);
} else {
$exception = new FatalErrorException($handler->levels[$error['type']].': '.$error['message'], 0, $error['type'], $error['file'], $error['line'], 2, true);
}
} elseif (!isset($exception)) {
return;
}
try {
$handler->handleException($exception, $error);
} catch (FatalErrorException $e) {
// Ignore this re-throw
}
}
}
/**
* Configures the error handler for delayed handling.
* Ensures also that non-catchable fatal errors are never silenced.
*
* As shown by http://bugs.php.net/42098 and http://bugs.php.net/60724
@ -219,7 +542,7 @@ class ErrorHandler
}
/**
* Unstacks stacked errors and forwards to the regular handler
* Unstacks stacked errors and forwards to the logger
*/
public static function unstackErrors()
{
@ -237,64 +560,12 @@ class ErrorHandler
$errors = self::$stackedErrors;
self::$stackedErrors = array();
$errorHandler = set_error_handler('var_dump');
restore_error_handler();
if ($errorHandler) {
foreach ($errors as $e) {
call_user_func_array($errorHandler, $e);
}
foreach ($errors as $e) {
$e[0][0]->log($e[0][1], $e[1], $e[2]);
}
}
}
public function handleFatal()
{
$this->reservedMemory = '';
gc_collect_cycles();
$error = error_get_last();
// get current exception handler
$exceptionHandler = set_exception_handler('var_dump');
restore_exception_handler();
try {
while (self::$stackedErrorLevels) {
static::unstackErrors();
}
} catch (\Exception $exception) {
if ($exceptionHandler) {
call_user_func($exceptionHandler, $exception);
return;
}
if ($this->displayErrors) {
ini_set('display_errors', 1);
}
throw $exception;
}
if (!$error || !$this->level || !($error['type'] & (E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR | E_PARSE))) {
return;
}
if (isset(self::$loggers['emergency'])) {
$fatal = array(
'type' => $error['type'],
'file' => $error['file'],
'line' => $error['line'],
);
self::$loggers['emergency']->emergency($error['message'], $fatal);
}
if ($this->displayErrors && $exceptionHandler) {
$this->handleFatalError($exceptionHandler, $error);
}
}
/**
* Gets the fatal error handlers.
*
@ -311,33 +582,81 @@ class ErrorHandler
);
}
private function handleFatalError($exceptionHandler, array $error)
/**
* Sets the level at which the conversion to Exception is done.
*
* @param int|null $level The level (null to use the error_reporting() value and 0 to disable)
*
* @deprecated since 2.6, to be removed in 3.0. Use throwAt() instead.
*/
public function setLevel($level)
{
// Let PHP handle any further error
set_error_handler('var_dump', 0);
ini_set('display_errors', 1);
$level = null === $level ? error_reporting() : $level;
$this->throwAt($level, true);
}
$level = isset($this->levels[$error['type']]) ? $this->levels[$error['type']] : $error['type'];
$message = sprintf('%s: %s in %s line %d', $level, $error['message'], $error['file'], $error['line']);
if (0 === strpos($error['message'], 'Allowed memory') || 0 === strpos($error['message'], 'Out of memory')) {
$exception = new OutOfMemoryException($message, 0, $error['type'], $error['file'], $error['line'], 3, false);
/**
* Sets the display_errors flag value.
*
* @param int $displayErrors The display_errors flag value
*
* @deprecated since 2.6, to be removed in 3.0. Use throwAt() instead.
*/
public function setDisplayErrors($displayErrors)
{
if ($displayErrors) {
$this->throwAt($this->displayErrors, true);
} else {
$exception = new FatalErrorException($message, 0, $error['type'], $error['file'], $error['line'], 3, true);
foreach ($this->getFatalErrorHandlers() as $handler) {
if ($e = $handler->handleError($error, $exception)) {
$exception = $e;
break;
}
}
$displayErrors = $this->displayErrors;
$this->throwAt(0, true);
$this->displayErrors = $displayErrors;
}
}
try {
call_user_func($exceptionHandler, $exception);
} catch (\Exception $e) {
// The handler failed. Let PHP handle that now.
throw $exception;
/**
* Sets a logger for the given channel.
*
* @param LoggerInterface $logger A logger interface
* @param string $channel The channel associated with the logger (deprecation, emergency or scream)
*
* @deprecated since 2.6, to be removed in 3.0. Use setLoggers() or setDefaultLogger() instead.
*/
public static function setLogger(LoggerInterface $logger, $channel = 'deprecation')
{
$handler = set_error_handler('var_dump', 0);
$handler = is_array($handler) ? $handler[0] : null;
restore_error_handler();
if (!$handler instanceof self) {
return;
}
if ('deprecation' === $channel) {
$handler->setDefaultLogger($logger, E_DEPRECATED | E_USER_DEPRECATED, true);
$handler->screamAt(E_DEPRECATED | E_USER_DEPRECATED);
} elseif ('scream' === $channel) {
$handler->setDefaultLogger($logger, E_ALL | E_STRICT, false);
$handler->screamAt(E_ALL | E_STRICT);
} elseif ('emergency' === $channel) {
$handler->setDefaultLogger($logger, E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR, true);
$handler->screamAt(E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR);
}
}
/**
* @deprecated since 2.6, to be removed in 3.0. Use handleError() instead.
*/
public function handle($level, $message, $file = 'unknown', $line = 0, $context = array())
{
return $this->handleError($level, $message, $file, $line, (array) $context);
}
/**
* Handles PHP fatal errors.
*
* @deprecated since 2.6, to be removed in 3.0. Use handleFatalError() instead.
*/
public function handleFatal()
{
static::handleFatalError();
}
}

View File

@ -66,8 +66,8 @@ class FlattenException
foreach (array_merge(array($this), $this->getAllPrevious()) as $exception) {
$exceptions[] = array(
'message' => $exception->getMessage(),
'class' => $exception->getClass(),
'trace' => $exception->getTrace(),
'class' => $exception->getClass(),
'trace' => $exception->getTrace(),
);
}
@ -179,14 +179,14 @@ class FlattenException
{
$this->trace = array();
$this->trace[] = array(
'namespace' => '',
'namespace' => '',
'short_class' => '',
'class' => '',
'type' => '',
'function' => '',
'file' => $file,
'line' => $line,
'args' => array(),
'class' => '',
'type' => '',
'function' => '',
'file' => $file,
'line' => $line,
'args' => array(),
);
foreach ($trace as $entry) {
$class = '';
@ -198,14 +198,14 @@ class FlattenException
}
$this->trace[] = array(
'namespace' => $namespace,
'namespace' => $namespace,
'short_class' => $class,
'class' => isset($entry['class']) ? $entry['class'] : '',
'type' => isset($entry['type']) ? $entry['type'] : '',
'function' => isset($entry['function']) ? $entry['function'] : null,
'file' => isset($entry['file']) ? $entry['file'] : null,
'line' => isset($entry['line']) ? $entry['line'] : null,
'args' => isset($entry['args']) ? $this->flattenArgs($entry['args']) : array(),
'class' => isset($entry['class']) ? $entry['class'] : '',
'type' => isset($entry['type']) ? $entry['type'] : '',
'function' => isset($entry['function']) ? $entry['function'] : null,
'file' => isset($entry['file']) ? $entry['file'] : null,
'line' => isset($entry['line']) ? $entry['line'] : null,
'args' => isset($entry['args']) ? $this->flattenArgs($entry['args']) : array(),
);
}
}

View File

@ -34,29 +34,33 @@ if (!defined('ENT_SUBSTITUTE')) {
class ExceptionHandler
{
private $debug;
private $charset;
private $handler;
private $caughtBuffer;
private $caughtLength;
private $fileLinkFormat;
public function __construct($debug = true, $charset = 'UTF-8')
public function __construct($debug = true, $fileLinkFormat = null)
{
$this->debug = $debug;
$this->charset = $charset;
$this->fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format');
}
/**
* Registers the exception handler.
*
* @param bool $debug
* @param bool $debug
*
* @return ExceptionHandler The registered exception handler
*/
public static function register($debug = true)
public static function register($debug = true, $fileLinkFormat = null)
{
$handler = new static($debug);
$handler = new static($debug, $fileLinkFormat = null);
set_exception_handler(array($handler, 'handle'));
$prev = set_exception_handler(array($handler, 'handle'));
if (is_array($prev) && $prev[0] instanceof ErrorHandler) {
restore_exception_handler();
$prev[0]->setExceptionHandler(array($handler, 'handle'));
}
return $handler;
}
@ -79,6 +83,21 @@ class ExceptionHandler
return $old;
}
/**
* Sets the format for links to source files.
*
* @param string $format The format for links to source files
*
* @return string The previous file link format.
*/
public function setFileLinkFormat($format)
{
$old = $this->fileLinkFormat;
$this->fileLinkFormat = $format;
return $old;
}
/**
* Sends a response for the given Exception.
*
@ -205,29 +224,26 @@ class ExceptionHandler
$total = $count + 1;
foreach ($exception->toArray() as $position => $e) {
$ind = $count - $position + 1;
$class = $this->abbrClass($e['class']);
$message = nl2br($e['message']);
$class = $this->formatClass($e['class']);
$message = nl2br(self::utf8Htmlize($e['message']));
$content .= sprintf(<<<EOF
<div class="block_exception clear_fix">
<h2><span>%d/%d</span> %s: %s</h2>
</div>
<h2 class="block_exception clear_fix">
<span class="exception_counter">%d/%d</span>
<span class="exception_title">%s%s:</span>
<span class="exception_message">%s</span>
</h2>
<div class="block">
<ol class="traces list_exception">
EOF
, $ind, $total, $class, $message);
, $ind, $total, $class, $this->formatPath($e['trace'][0]['file'], $e['trace'][0]['line']), $message);
foreach ($e['trace'] as $trace) {
$content .= ' <li>';
if ($trace['function']) {
$content .= sprintf('at %s%s%s(%s)', $this->abbrClass($trace['class']), $trace['type'], $trace['function'], $this->formatArgs($trace['args']));
$content .= sprintf('at %s%s%s(%s)', $this->formatClass($trace['class']), $trace['type'], $trace['function'], $this->formatArgs($trace['args']));
}
if (isset($trace['file']) && isset($trace['line'])) {
if ($linkFormat = ini_get('xdebug.file_link_format')) {
$link = str_replace(array('%f', '%l'), array($trace['file'], $trace['line']), $linkFormat);
$content .= sprintf(' in <a href="%s" title="Go to source">%s line %s</a>', $link, $trace['file'], $trace['line']);
} else {
$content .= sprintf(' in %s line %s', $trace['file'], $trace['line']);
}
$content .= $this->formatPath($trace['file'], $trace['line']);
}
$content .= "</li>\n";
}
@ -272,12 +288,14 @@ EOF;
.sf-reset abbr { border-bottom: 1px dotted #000; cursor: help; }
.sf-reset p { font-size:14px; line-height:20px; color:#868686; padding-bottom:20px }
.sf-reset strong { font-weight:bold; }
.sf-reset a { color:#6c6159; }
.sf-reset a { color:#6c6159; cursor: default; }
.sf-reset a img { border:none; }
.sf-reset a:hover { text-decoration:underline; }
.sf-reset em { font-style:italic; }
.sf-reset h1, .sf-reset h2 { font: 20px Georgia, "Times New Roman", Times, serif }
.sf-reset h2 span { background-color: #fff; color: #333; padding: 6px; float: left; margin-right: 10px; }
.sf-reset .exception_counter { background-color: #fff; color: #333; padding: 6px; float: left; margin-right: 10px; float: left; display: block; }
.sf-reset .exception_title { margin-left: 3em; margin-bottom: 0.7em; display: block; }
.sf-reset .exception_message { margin-left: 3em; display: block; }
.sf-reset .traces li { font-size:12px; padding: 2px 4px; list-style-type:decimal; margin-left:20px; }
.sf-reset .block { background-color:#FFFFFF; padding:10px 28px; margin-bottom:20px;
-webkit-border-bottom-right-radius: 16px;
@ -303,8 +321,8 @@ EOF;
overflow: hidden;
word-wrap: break-word;
}
.sf-reset li a { background:none; color:#868686; text-decoration:none; }
.sf-reset li a:hover { background:none; color:#313131; text-decoration:underline; }
.sf-reset a { background:none; color:#868686; text-decoration:none; }
.sf-reset a:hover { background:none; color:#313131; text-decoration:underline; }
.sf-reset ol { padding: 10px 0; }
.sf-reset h1 { background-color:#FFFFFF; padding: 15px 28px; margin-bottom: 20px;
-webkit-border-radius: 10px;
@ -321,7 +339,7 @@ EOF;
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta charset="UTF-8" />
<meta name="robots" content="noindex,nofollow" />
<style>
/* Copyright (c) 2010, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.com/yui/license.html */
@ -340,13 +358,27 @@ EOF;
EOF;
}
private function abbrClass($class)
private function formatClass($class)
{
$parts = explode('\\', $class);
return sprintf("<abbr title=\"%s\">%s</abbr>", $class, array_pop($parts));
}
private function formatPath($path, $line)
{
$path = self::utf8Htmlize($path);
$file = preg_match('#[^/\\\\]*$#', $path, $file) ? $file[0] : $path;
if ($linkFormat = $this->fileLinkFormat) {
$link = str_replace(array('%f', '%l'), array($path, $line), $linkFormat);
return sprintf(' in <a href="%s" title="Go to source">%s line %d</a>', $link, $file, $line);
}
return sprintf(' in <a title="%s line %3$d" ondblclick="var f=this.innerHTML;this.innerHTML=this.title;this.title=f;">%s line %d</a>', $path, $file, $line);
}
/**
* Formats an array as a string.
*
@ -359,11 +391,11 @@ EOF;
$result = array();
foreach ($args as $key => $item) {
if ('object' === $item[0]) {
$formattedValue = sprintf("<em>object</em>(%s)", $this->abbrClass($item[1]));
$formattedValue = sprintf("<em>object</em>(%s)", $this->formatClass($item[1]));
} elseif ('array' === $item[0]) {
$formattedValue = sprintf("<em>array</em>(%s)", is_array($item[1]) ? $this->formatArgs($item[1]) : $item[1]);
} elseif ('string' === $item[0]) {
$formattedValue = sprintf("'%s'", htmlspecialchars($item[1], ENT_QUOTES | ENT_SUBSTITUTE, $this->charset));
} elseif ('string' === $item[0]) {
$formattedValue = sprintf("'%s'", self::utf8Htmlize($item[1]));
} elseif ('null' === $item[0]) {
$formattedValue = '<em>null</em>';
} elseif ('boolean' === $item[0]) {
@ -371,7 +403,7 @@ EOF;
} elseif ('resource' === $item[0]) {
$formattedValue = '<em>resource</em>';
} else {
$formattedValue = str_replace("\n", '', var_export(htmlspecialchars((string) $item[1], ENT_QUOTES | ENT_SUBSTITUTE, $this->charset), true));
$formattedValue = str_replace("\n", '', var_export(self::utf8Htmlize((string) $item[1]), true));
}
$result[] = is_int($key) ? $formattedValue : sprintf("'%s' => %s", $key, $formattedValue);
@ -380,6 +412,25 @@ EOF;
return implode(', ', $result);
}
/**
* Returns an UTF-8 and HTML encoded string
*/
protected static function utf8Htmlize($str)
{
if (!preg_match('//u', $str) && function_exists('iconv')) {
set_error_handler('var_dump', 0);
$charset = ini_get('default_charset');
if ('UTF-8' === $charset || $str !== @iconv($charset, $charset, $str)) {
$charset = 'CP1252';
}
restore_error_handler();
$str = iconv($charset, 'UTF-8', $str);
}
return htmlspecialchars($str, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}
/**
* @internal
*/

View File

@ -51,29 +51,23 @@ class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface
if (false !== $namespaceSeparatorIndex = strrpos($fullyQualifiedClassName, '\\')) {
$className = substr($fullyQualifiedClassName, $namespaceSeparatorIndex + 1);
$namespacePrefix = substr($fullyQualifiedClassName, 0, $namespaceSeparatorIndex);
$message = sprintf(
'Attempted to load %s "%s" from namespace "%s" in %s line %d. Do you need to "use" it from another namespace?',
$typeName,
$className,
$namespacePrefix,
$error['file'],
$error['line']
);
$message = sprintf('Attempted to load %s "%s" from namespace "%s".', $typeName, $className, $namespacePrefix);
$tail = ' for another namespace?';
} else {
$className = $fullyQualifiedClassName;
$message = sprintf(
'Attempted to load %s "%s" from the global namespace in %s line %d. Did you forget a use statement for this %s?',
$typeName,
$className,
$error['file'],
$error['line'],
$typeName
);
$message = sprintf('Attempted to load %s "%s" from the global namespace.', $typeName, $className);
$tail = '?';
}
if ($classes = $this->getClassCandidates($className)) {
$message .= sprintf(' Perhaps you need to add a use statement for one of the following: %s.', implode(', ', $classes));
if ($candidates = $this->getClassCandidates($className)) {
$tail = array_pop($candidates).'"?';
if ($candidates) {
$tail = ' for e.g. "'.implode('", "', $candidates).'" or "'.$tail;
} else {
$tail = ' for "'.$tail;
}
}
$message .= "\nDid you forget a \"use\" statement".$tail;
return new ClassNotFoundException($message, $exception);
}

View File

@ -47,21 +47,10 @@ class UndefinedFunctionFatalErrorHandler implements FatalErrorHandlerInterface
if (false !== $namespaceSeparatorIndex = strrpos($fullyQualifiedFunctionName, '\\')) {
$functionName = substr($fullyQualifiedFunctionName, $namespaceSeparatorIndex + 1);
$namespacePrefix = substr($fullyQualifiedFunctionName, 0, $namespaceSeparatorIndex);
$message = sprintf(
'Attempted to call function "%s" from namespace "%s" in %s line %d.',
$functionName,
$namespacePrefix,
$error['file'],
$error['line']
);
$message = sprintf('Attempted to call function "%s" from namespace "%s".', $functionName, $namespacePrefix);
} else {
$functionName = $fullyQualifiedFunctionName;
$message = sprintf(
'Attempted to call function "%s" from the global namespace in %s line %d.',
$functionName,
$error['file'],
$error['line']
);
$message = sprintf('Attempted to call function "%s" from the global namespace.', $functionName);
}
$candidates = array();
@ -81,9 +70,13 @@ class UndefinedFunctionFatalErrorHandler implements FatalErrorHandlerInterface
if ($candidates) {
sort($candidates);
$message .= ' Did you mean to call: '.implode(', ', array_map(function ($val) {
return '"'.$val.'"';
}, $candidates)).'?';
$last = array_pop($candidates).'"?';
if ($candidates) {
$candidates = 'e.g. "'.implode('", "', $candidates).'" or "'.$last;
} else {
$candidates = '"'.$last;
}
$message .= "\nDid you mean to call ".$candidates;
}
return new UndefinedFunctionException($message, $exception);

View File

@ -34,7 +34,7 @@ class UndefinedMethodFatalErrorHandler implements FatalErrorHandlerInterface
$className = $matches[1];
$methodName = $matches[2];
$message = sprintf('Attempted to call method "%s" on class "%s" in %s line %d.', $methodName, $className, $error['file'], $error['line']);
$message = sprintf('Attempted to call method "%s" on class "%s".', $methodName, $className);
$candidates = array();
foreach (get_class_methods($className) as $definedMethodName) {
@ -46,7 +46,13 @@ class UndefinedMethodFatalErrorHandler implements FatalErrorHandlerInterface
if ($candidates) {
sort($candidates);
$message .= sprintf(' Did you mean to call: "%s"?', implode('", "', $candidates));
$last = array_pop($candidates).'"?';
if ($candidates) {
$candidates = 'e.g. "'.implode('", "', $candidates).'" or "'.$last;
} else {
$candidates = '"'.$last;
}
$message .= "\nDid you mean to call ".$candidates;
}
return new UndefinedMethodException($message, $exception);

View File

@ -6,23 +6,26 @@ Debug provides tools to make debugging easier.
Enabling all debug tools is as easy as calling the `enable()` method on the
main `Debug` class:
use Symfony\Component\Debug\Debug;
```php
use Symfony\Component\Debug\Debug;
Debug::enable();
Debug::enable();
```
You can also use the tools individually:
use Symfony\Component\Debug\ErrorHandler;
use Symfony\Component\Debug\ExceptionHandler;
```php
use Symfony\Component\Debug\ErrorHandler;
use Symfony\Component\Debug\ExceptionHandler;
error_reporting(-1);
ErrorHandler::register($errorReportingLevel);
if ('cli' !== php_sapi_name()) {
ExceptionHandler::register();
} elseif (!ini_get('log_errors') || ini_get('error_log')) {
ini_set('display_errors', 1);
}
if ('cli' !== php_sapi_name()) {
ini_set('display_errors', 0);
ExceptionHandler::register();
} elseif (!ini_get('log_errors') || ini_get('error_log')) {
ini_set('display_errors', 1);
}
ErrorHandler::register($errorReportingLevel);
```
Note that the `Debug::enable()` call also registers the debug class loader
from the Symfony ClassLoader component when available.

View File

@ -0,0 +1,72 @@
Symfony Debug Extension
=======================
This extension adds a ``symfony_zval_info($key, $array, $options = 0)`` function that:
- exposes zval_hash/refcounts, allowing e.g. efficient exploration of arbitrary structures in PHP,
- does work with references, preventing memory copying.
Its behavior is about the same as:
.. code-block:: php
<?php
function symfony_zval_info($key, $array, $options = 0)
{
// $options is currently not used, but could be in future version.
if (!array_key_exists($key, $array)) {
return null;
}
$info = array(
'type' => gettype($array[$key]),
'zval_hash' => /* hashed memory address of $array[$key] */,
'zval_refcount' => /* internal zval refcount of $array[$key] */,
'zval_isref' => /* is_ref status of $array[$key] */,
);
switch ($info['type']) {
case 'object':
$info += array(
'object_class' => get_class($array[$key]),
'object_refcount' => /* internal object refcount of $array[$key] */,
'object_hash' => spl_object_hash($array[$key]),
'object_handle' => /* internal object handle $array[$key] */,
);
break;
case 'resource':
$info += array(
'resource_handle' => (int) $array[$key],
'resource_type' => get_resource_type($array[$key]),
'resource_refcount' => /* internal resource refcount of $array[$key] */,
);
break;
case 'array':
$info += array(
'array_count' => count($array[$key]),
);
break;
case 'string':
$info += array(
'strlen' => strlen($array[$key]),
);
break;
}
return $info;
}
To enable the extension from source, run:
.. code-block:: sh
phpize
./configure
make
sudo make install

View File

@ -0,0 +1,63 @@
dnl $Id$
dnl config.m4 for extension symfony_debug
dnl Comments in this file start with the string 'dnl'.
dnl Remove where necessary. This file will not work
dnl without editing.
dnl If your extension references something external, use with:
dnl PHP_ARG_WITH(symfony_debug, for symfony_debug support,
dnl Make sure that the comment is aligned:
dnl [ --with-symfony_debug Include symfony_debug support])
dnl Otherwise use enable:
PHP_ARG_ENABLE(symfony_debug, whether to enable symfony_debug support,
dnl Make sure that the comment is aligned:
[ --enable-symfony_debug Enable symfony_debug support])
if test "$PHP_SYMFONY_DEBUG" != "no"; then
dnl Write more examples of tests here...
dnl # --with-symfony_debug -> check with-path
dnl SEARCH_PATH="/usr/local /usr" # you might want to change this
dnl SEARCH_FOR="/include/symfony_debug.h" # you most likely want to change this
dnl if test -r $PHP_SYMFONY_DEBUG/$SEARCH_FOR; then # path given as parameter
dnl SYMFONY_DEBUG_DIR=$PHP_SYMFONY_DEBUG
dnl else # search default path list
dnl AC_MSG_CHECKING([for symfony_debug files in default path])
dnl for i in $SEARCH_PATH ; do
dnl if test -r $i/$SEARCH_FOR; then
dnl SYMFONY_DEBUG_DIR=$i
dnl AC_MSG_RESULT(found in $i)
dnl fi
dnl done
dnl fi
dnl
dnl if test -z "$SYMFONY_DEBUG_DIR"; then
dnl AC_MSG_RESULT([not found])
dnl AC_MSG_ERROR([Please reinstall the symfony_debug distribution])
dnl fi
dnl # --with-symfony_debug -> add include path
dnl PHP_ADD_INCLUDE($SYMFONY_DEBUG_DIR/include)
dnl # --with-symfony_debug -> check for lib and symbol presence
dnl LIBNAME=symfony_debug # you may want to change this
dnl LIBSYMBOL=symfony_debug # you most likely want to change this
dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL,
dnl [
dnl PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $SYMFONY_DEBUG_DIR/lib, SYMFONY_DEBUG_SHARED_LIBADD)
dnl AC_DEFINE(HAVE_SYMFONY_DEBUGLIB,1,[ ])
dnl ],[
dnl AC_MSG_ERROR([wrong symfony_debug lib version or lib not found])
dnl ],[
dnl -L$SYMFONY_DEBUG_DIR/lib -lm
dnl ])
dnl
dnl PHP_SUBST(SYMFONY_DEBUG_SHARED_LIBADD)
PHP_NEW_EXTENSION(symfony_debug, symfony_debug.c, $ext_shared)
fi

View File

@ -0,0 +1,13 @@
// $Id$
// vim:ft=javascript
// If your extension references something external, use ARG_WITH
// ARG_WITH("symfony_debug", "for symfony_debug support", "no");
// Otherwise, use ARG_ENABLE
// ARG_ENABLE("symfony_debug", "enable symfony_debug support", "no");
if (PHP_SYMFONY_DEBUG != "no") {
EXTENSION("symfony_debug", "symfony_debug.c");
}

View File

@ -0,0 +1,55 @@
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#ifndef PHP_SYMFONY_DEBUG_H
#define PHP_SYMFONY_DEBUG_H
extern zend_module_entry symfony_debug_module_entry;
#define phpext_symfony_debug_ptr &symfony_debug_module_entry
#define PHP_SYMFONY_DEBUG_VERSION "1.0"
#ifdef PHP_WIN32
# define PHP_SYMFONY_DEBUG_API __declspec(dllexport)
#elif defined(__GNUC__) && __GNUC__ >= 4
# define PHP_SYMFONY_DEBUG_API __attribute__ ((visibility("default")))
#else
# define PHP_SYMFONY_DEBUG_API
#endif
#ifdef ZTS
#include "TSRM.h"
#endif
ZEND_BEGIN_MODULE_GLOBALS(symfony_debug)
intptr_t req_rand_init;
ZEND_END_MODULE_GLOBALS(symfony_debug)
PHP_MINIT_FUNCTION(symfony_debug);
PHP_MSHUTDOWN_FUNCTION(symfony_debug);
PHP_RINIT_FUNCTION(symfony_debug);
PHP_RSHUTDOWN_FUNCTION(symfony_debug);
PHP_MINFO_FUNCTION(symfony_debug);
PHP_GINIT_FUNCTION(symfony_debug);
PHP_GSHUTDOWN_FUNCTION(symfony_debug);
PHP_FUNCTION(symfony_zval_info);
static char *_symfony_debug_memory_address_hash(void *);
static const char *_symfony_debug_zval_type(zval *);
static const char* _symfony_debug_get_resource_type(long);
static int _symfony_debug_get_resource_refcount(long);
#ifdef ZTS
#define SYMFONY_DEBUG_G(v) TSRMG(symfony_debug_globals_id, zend_symfony_debug_globals *, v)
#else
#define SYMFONY_DEBUG_G(v) (symfony_debug_globals.v)
#endif
#endif /* PHP_SYMFONY_DEBUG_H */

View File

@ -0,0 +1,224 @@
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "php_symfony_debug.h"
#include "ext/standard/php_rand.h"
#include "ext/standard/php_lcg.h"
#include "ext/spl/php_spl.h"
#include "Zend/zend_gc.h"
ZEND_DECLARE_MODULE_GLOBALS(symfony_debug)
ZEND_BEGIN_ARG_INFO_EX(symfony_zval_arginfo, 0, 0, 2)
ZEND_ARG_INFO(0, key)
ZEND_ARG_ARRAY_INFO(0, array, 0)
ZEND_ARG_INFO(0, options)
ZEND_END_ARG_INFO()
const zend_function_entry symfony_debug_functions[] = {
PHP_FE(symfony_zval_info, symfony_zval_arginfo)
PHP_FE_END
};
PHP_FUNCTION(symfony_zval_info)
{
zval *key = NULL, *arg = NULL;
zval **data = NULL;
HashTable *array = NULL;
long options = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zh|l", &key, &array, &options) == FAILURE) {
return;
}
switch (Z_TYPE_P(key)) {
case IS_STRING:
if (zend_symtable_find(array, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, (void **)&data) == FAILURE) {
return;
}
break;
case IS_LONG:
if (zend_hash_index_find(array, Z_LVAL_P(key), (void **)&data)) {
return;
}
break;
}
arg = *data;
array_init(return_value);
add_assoc_string(return_value, "type", (char *)_symfony_debug_zval_type(arg), 1);
add_assoc_stringl(return_value, "zval_hash", _symfony_debug_memory_address_hash((void *)arg), 16, 1);
add_assoc_long(return_value, "zval_refcount", Z_REFCOUNT_P(arg));
add_assoc_bool(return_value, "zval_isref", (zend_bool)Z_ISREF_P(arg));
if (Z_TYPE_P(arg) == IS_OBJECT) {
static char hash[33] = {0};
php_spl_object_hash(arg, (char *)hash);
add_assoc_stringl(return_value, "object_class", (char *)Z_OBJCE_P(arg)->name, Z_OBJCE_P(arg)->name_length, 1);
add_assoc_long(return_value, "object_refcount", EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(arg)].bucket.obj.refcount);
add_assoc_string(return_value, "object_hash", hash, 1);
add_assoc_long(return_value, "object_handle", Z_OBJ_HANDLE_P(arg));
} else if (Z_TYPE_P(arg) == IS_ARRAY) {
add_assoc_long(return_value, "array_count", zend_hash_num_elements(Z_ARRVAL_P(arg)));
} else if(Z_TYPE_P(arg) == IS_RESOURCE) {
add_assoc_long(return_value, "resource_handle", Z_LVAL_P(arg));
add_assoc_string(return_value, "resource_type", (char *)_symfony_debug_get_resource_type(Z_LVAL_P(arg)), 1);
add_assoc_long(return_value, "resource_refcount", _symfony_debug_get_resource_refcount(Z_LVAL_P(arg)));
} else if (Z_TYPE_P(arg) == IS_STRING) {
add_assoc_long(return_value, "strlen", Z_STRLEN_P(arg));
}
}
static const char* _symfony_debug_get_resource_type(long rsid)
{
const char *res_type;
res_type = zend_rsrc_list_get_rsrc_type(rsid);
if (!res_type) {
return "Unknown";
}
return res_type;
}
static int _symfony_debug_get_resource_refcount(long rsid)
{
zend_rsrc_list_entry *le;
if (zend_hash_index_find(&EG(regular_list), rsid, (void **) &le)==SUCCESS) {
return le->refcount;
}
return 0;
}
static char *_symfony_debug_memory_address_hash(void *address)
{
static char result[17] = {0};
intptr_t address_rand;
if (!SYMFONY_DEBUG_G(req_rand_init)) {
if (!BG(mt_rand_is_seeded)) {
php_mt_srand(GENERATE_SEED() TSRMLS_CC);
}
SYMFONY_DEBUG_G(req_rand_init) = (intptr_t)php_mt_rand();
}
address_rand = (intptr_t)address ^ SYMFONY_DEBUG_G(req_rand_init);
snprintf(result, 17, "%016zx", address_rand);
return result;
}
static const char *_symfony_debug_zval_type(zval *zv)
{
switch (Z_TYPE_P(zv)) {
case IS_NULL:
return "NULL";
break;
case IS_BOOL:
return "boolean";
break;
case IS_LONG:
return "integer";
break;
case IS_DOUBLE:
return "double";
break;
case IS_STRING:
return "string";
break;
case IS_ARRAY:
return "array";
break;
case IS_OBJECT:
return "object";
case IS_RESOURCE:
return "resource";
default:
return "unknown type";
}
}
zend_module_entry symfony_debug_module_entry = {
STANDARD_MODULE_HEADER,
"symfony_debug",
symfony_debug_functions,
PHP_MINIT(symfony_debug),
PHP_MSHUTDOWN(symfony_debug),
PHP_RINIT(symfony_debug),
PHP_RSHUTDOWN(symfony_debug),
PHP_MINFO(symfony_debug),
PHP_SYMFONY_DEBUG_VERSION,
PHP_MODULE_GLOBALS(symfony_debug),
PHP_GINIT(symfony_debug),
PHP_GSHUTDOWN(symfony_debug),
NULL,
STANDARD_MODULE_PROPERTIES_EX
};
#ifdef COMPILE_DL_SYMFONY_DEBUG
ZEND_GET_MODULE(symfony_debug)
#endif
PHP_GINIT_FUNCTION(symfony_debug)
{
symfony_debug_globals->req_rand_init = 0;
}
PHP_GSHUTDOWN_FUNCTION(symfony_debug)
{
}
PHP_MINIT_FUNCTION(symfony_debug)
{
return SUCCESS;
}
PHP_MSHUTDOWN_FUNCTION(symfony_debug)
{
return SUCCESS;
}
PHP_RINIT_FUNCTION(symfony_debug)
{
return SUCCESS;
}
PHP_RSHUTDOWN_FUNCTION(symfony_debug)
{
return SUCCESS;
}
PHP_MINFO_FUNCTION(symfony_debug)
{
php_info_print_table_start();
php_info_print_table_header(2, "Symfony Debug support", "enabled");
php_info_print_table_header(2, "Symfony Debug version", PHP_SYMFONY_DEBUG_VERSION);
php_info_print_table_end();
}

View File

@ -0,0 +1,151 @@
--TEST--
Test symfony_zval_info API
--SKIPIF--
<?php if (!extension_loaded("symfony_debug")) print "skip"; ?>
--FILE--
<?php
$int = 42;
$float = 42.42;
$str = "foobar";
$object = new StdClass;
$array = array('foo', 'bar');
$resource = tmpfile();
$null = null;
$bool = true;
$anotherint = 42;
$refcount2 = &$anotherint;
$var = array('int' => $int,
'float' => $float,
'str' => $str,
'object' => $object,
'array' => $array,
'resource' => $resource,
'null' => $null,
'bool' => $bool,
'refcount' => &$refcount2);
var_dump(symfony_zval_info('int', $var));
var_dump(symfony_zval_info('float', $var));
var_dump(symfony_zval_info('str', $var));
var_dump(symfony_zval_info('object', $var));
var_dump(symfony_zval_info('array', $var));
var_dump(symfony_zval_info('resource', $var));
var_dump(symfony_zval_info('null', $var));
var_dump(symfony_zval_info('bool', $var));
var_dump(symfony_zval_info('refcount', $var));
var_dump(symfony_zval_info('not-exist', $var));
?>
--EXPECTF--
array(4) {
["type"]=>
string(7) "integer"
["zval_hash"]=>
string(16) "%s"
["zval_refcount"]=>
int(2)
["zval_isref"]=>
bool(false)
}
array(4) {
["type"]=>
string(6) "double"
["zval_hash"]=>
string(16) "%s"
["zval_refcount"]=>
int(2)
["zval_isref"]=>
bool(false)
}
array(5) {
["type"]=>
string(6) "string"
["zval_hash"]=>
string(16) "%s"
["zval_refcount"]=>
int(2)
["zval_isref"]=>
bool(false)
["strlen"]=>
int(6)
}
array(8) {
["type"]=>
string(6) "object"
["zval_hash"]=>
string(16) "%s"
["zval_refcount"]=>
int(2)
["zval_isref"]=>
bool(false)
["object_class"]=>
string(8) "stdClass"
["object_refcount"]=>
int(1)
["object_hash"]=>
string(32) "%s"
["object_handle"]=>
int(1)
}
array(5) {
["type"]=>
string(5) "array"
["zval_hash"]=>
string(16) "%s"
["zval_refcount"]=>
int(2)
["zval_isref"]=>
bool(false)
["array_count"]=>
int(2)
}
array(7) {
["type"]=>
string(8) "resource"
["zval_hash"]=>
string(16) "%s"
["zval_refcount"]=>
int(2)
["zval_isref"]=>
bool(false)
["resource_handle"]=>
int(4)
["resource_type"]=>
string(6) "stream"
["resource_refcount"]=>
int(1)
}
array(4) {
["type"]=>
string(4) "NULL"
["zval_hash"]=>
string(16) "%s"
["zval_refcount"]=>
int(2)
["zval_isref"]=>
bool(false)
}
array(4) {
["type"]=>
string(7) "boolean"
["zval_hash"]=>
string(16) "%s"
["zval_refcount"]=>
int(2)
["zval_isref"]=>
bool(false)
}
array(4) {
["type"]=>
string(7) "integer"
["zval_hash"]=>
string(16) "%s"
["zval_refcount"]=>
int(3)
["zval_isref"]=>
bool(true)
}
NULL

View File

@ -106,11 +106,11 @@ class DebugClassLoaderTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(E_STRICT, $exception->getSeverity());
$this->assertStringStartsWith(__FILE__, $exception->getFile());
$this->assertRegexp('/^Runtime Notice: Declaration/', $exception->getMessage());
} catch (\Exception $e) {
} catch (\Exception $exception) {
restore_error_handler();
restore_exception_handler();
throw $e;
throw $exception;
}
}

View File

@ -11,6 +11,7 @@
namespace Symfony\Component\Debug\Tests;
use Psr\Log\LogLevel;
use Symfony\Component\Debug\ErrorHandler;
use Symfony\Component\Debug\Exception\ContextErrorException;
@ -18,6 +19,7 @@ use Symfony\Component\Debug\Exception\ContextErrorException;
* ErrorHandlerTest
*
* @author Robert Schönthal <seroscho@googlemail.com>
* @author Nicolas Grekas <p@tchwork.com>
*/
class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
{
@ -44,6 +46,46 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
error_reporting($this->errorReporting);
}
public function testRegister()
{
$handler = ErrorHandler::register();
try {
$this->assertInstanceOf('Symfony\Component\Debug\ErrorHandler', $handler);
$this->assertSame($handler, ErrorHandler::register());
$newHandler = new ErrorHandler();
$this->assertSame($newHandler, ErrorHandler::register($newHandler, false));
$h = set_error_handler('var_dump');
restore_error_handler();
$this->assertSame(array($handler, 'handleError'), $h);
try {
$this->assertSame($newHandler, ErrorHandler::register($newHandler, true));
$h = set_error_handler('var_dump');
restore_error_handler();
$this->assertSame(array($newHandler, 'handleError'), $h);
} catch (\Exception $e) {
}
restore_error_handler();
restore_exception_handler();
if (isset($e)) {
throw $e;
}
} catch (\Exception $e) {
}
restore_error_handler();
restore_exception_handler();
if (isset($e)) {
throw $e;
}
}
public function testNotice()
{
ErrorHandler::register();
@ -54,6 +96,8 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
} catch (ContextErrorException $exception) {
// if an exception is thrown, the test passed
restore_error_handler();
restore_exception_handler();
$this->assertEquals(E_NOTICE, $exception->getSeverity());
$this->assertEquals(__FILE__, $exception->getFile());
$this->assertRegexp('/^Notice: Undefined variable: (foo|bar)/', $exception->getMessage());
@ -62,7 +106,7 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
$trace = $exception->getTrace();
$this->assertEquals(__FILE__, $trace[0]['file']);
$this->assertEquals('Symfony\Component\Debug\ErrorHandler', $trace[0]['class']);
$this->assertEquals('handle', $trace[0]['function']);
$this->assertEquals('handleError', $trace[0]['function']);
$this->assertEquals('->', $trace[0]['type']);
$this->assertEquals(__FILE__, $trace[1]['file']);
@ -70,16 +114,16 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('triggerNotice', $trace[1]['function']);
$this->assertEquals('::', $trace[1]['type']);
$this->assertEquals(__FILE__, $trace[1]['file']);
$this->assertEquals(__CLASS__, $trace[2]['class']);
$this->assertEquals('testNotice', $trace[2]['function']);
$this->assertEquals(__FUNCTION__, $trace[2]['function']);
$this->assertEquals('->', $trace[2]['type']);
} catch (\Exception $e) {
restore_error_handler();
restore_exception_handler();
throw $e;
}
restore_error_handler();
}
// dummy function to test trace in error handler.
@ -93,78 +137,257 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
public function testConstruct()
{
try {
$handler = ErrorHandler::register(3);
$level = new \ReflectionProperty($handler, 'level');
$level->setAccessible(true);
$this->assertEquals(3, $level->getValue($handler));
$handler = ErrorHandler::register();
$handler->throwAt(3, true);
$this->assertEquals(3 | E_RECOVERABLE_ERROR | E_USER_ERROR, $handler->throwAt(0));
restore_error_handler();
restore_exception_handler();
} catch (\Exception $e) {
restore_error_handler();
restore_exception_handler();
throw $e;
}
}
public function testHandle()
public function testDefaultLogger()
{
try {
$handler = ErrorHandler::register(0);
$this->assertFalse($handler->handle(0, 'foo', 'foo.php', 12, array()));
$handler = ErrorHandler::register();
$logger = $this->getMock('Psr\Log\LoggerInterface');
$handler->setDefaultLogger($logger, E_NOTICE);
$handler->setDefaultLogger($logger, array(E_USER_NOTICE => LogLevel::CRITICAL));
$loggers = array(
E_DEPRECATED => array(null, LogLevel::INFO),
E_USER_DEPRECATED => array(null, LogLevel::INFO),
E_NOTICE => array($logger, LogLevel::NOTICE),
E_USER_NOTICE => array($logger, LogLevel::CRITICAL),
E_STRICT => array(null, LogLevel::NOTICE),
E_WARNING => array(null, LogLevel::WARNING),
E_USER_WARNING => array(null, LogLevel::WARNING),
E_COMPILE_WARNING => array(null, LogLevel::WARNING),
E_CORE_WARNING => array(null, LogLevel::WARNING),
E_USER_ERROR => array(null, LogLevel::ERROR),
E_RECOVERABLE_ERROR => array(null, LogLevel::ERROR),
E_COMPILE_ERROR => array(null, LogLevel::EMERGENCY),
E_PARSE => array(null, LogLevel::EMERGENCY),
E_ERROR => array(null, LogLevel::EMERGENCY),
E_CORE_ERROR => array(null, LogLevel::EMERGENCY),
);
$this->assertSame($loggers, $handler->setLoggers(array()));
restore_error_handler();
restore_exception_handler();
} catch (\Exception $e) {
restore_error_handler();
restore_exception_handler();
$handler = ErrorHandler::register(3);
$this->assertFalse($handler->handle(4, 'foo', 'foo.php', 12, array()));
throw $e;
}
}
public function testHandleError()
{
try {
$handler = ErrorHandler::register();
$handler->throwAt(0, true);
$this->assertFalse($handler->handleError(0, 'foo', 'foo.php', 12, array()));
restore_error_handler();
restore_exception_handler();
$handler = ErrorHandler::register(3);
$handler = ErrorHandler::register();
$handler->throwAt(3, true);
$this->assertFalse($handler->handleError(4, 'foo', 'foo.php', 12, array()));
restore_error_handler();
restore_exception_handler();
$handler = ErrorHandler::register();
$handler->throwAt(3, true);
try {
$handler->handle(111, 'foo', 'foo.php', 12, array());
$handler->handleError(4, 'foo', 'foo.php', 12, array());
} catch (\ErrorException $e) {
$this->assertSame('111: foo in foo.php line 12', $e->getMessage());
$this->assertSame(111, $e->getSeverity());
$this->assertSame('Parse Error: foo', $e->getMessage());
$this->assertSame(4, $e->getSeverity());
$this->assertSame('foo.php', $e->getFile());
$this->assertSame(12, $e->getLine());
}
restore_error_handler();
restore_exception_handler();
$handler = ErrorHandler::register(E_USER_DEPRECATED);
$this->assertFalse($handler->handle(E_USER_DEPRECATED, 'foo', 'foo.php', 12, array()));
$handler = ErrorHandler::register();
$handler->throwAt(E_USER_DEPRECATED, true);
$this->assertFalse($handler->handleError(E_USER_DEPRECATED, 'foo', 'foo.php', 12, array()));
restore_error_handler();
restore_exception_handler();
$handler = ErrorHandler::register(E_DEPRECATED);
$this->assertFalse($handler->handle(E_DEPRECATED, 'foo', 'foo.php', 12, array()));
$handler = ErrorHandler::register();
$handler->throwAt(E_DEPRECATED, true);
$this->assertFalse($handler->handleError(E_DEPRECATED, 'foo', 'foo.php', 12, array()));
restore_error_handler();
restore_exception_handler();
$logger = $this->getMock('Psr\Log\LoggerInterface');
$that = $this;
$warnArgCheck = function ($message, $context) use ($that) {
$warnArgCheck = function ($logLevel, $message, $context) use ($that) {
$that->assertEquals('info', $logLevel);
$that->assertEquals('foo', $message);
$that->assertArrayHasKey('type', $context);
$that->assertEquals($context['type'], ErrorHandler::TYPE_DEPRECATION);
$that->assertEquals($context['type'], E_USER_DEPRECATED);
$that->assertArrayHasKey('stack', $context);
$that->assertInternalType('array', $context['stack']);
};
$logger
->expects($this->once())
->method('warning')
->method('log')
->will($this->returnCallback($warnArgCheck))
;
$handler = ErrorHandler::register(E_USER_DEPRECATED);
$handler->setLogger($logger);
$this->assertTrue($handler->handle(E_USER_DEPRECATED, 'foo', 'foo.php', 12, array()));
$handler = ErrorHandler::register();
$handler->setDefaultLogger($logger, E_USER_DEPRECATED);
$this->assertTrue($handler->handleError(E_USER_DEPRECATED, 'foo', 'foo.php', 12, array()));
restore_error_handler();
restore_exception_handler();
$logger = $this->getMock('Psr\Log\LoggerInterface');
$that = $this;
$logArgCheck = function ($level, $message, $context) use ($that) {
$that->assertEquals('Undefined variable: undefVar', $message);
$that->assertArrayHasKey('type', $context);
$that->assertEquals($context['type'], E_NOTICE);
};
$logger
->expects($this->once())
->method('log')
->will($this->returnCallback($logArgCheck))
;
$handler = ErrorHandler::register();
$handler->setDefaultLogger($logger, E_NOTICE);
$handler->screamAt(E_NOTICE);
unset($undefVar);
@$undefVar++;
restore_error_handler();
restore_exception_handler();
} catch (\Exception $e) {
restore_error_handler();
restore_exception_handler();
throw $e;
}
}
public function testHandleException()
{
try {
$handler = ErrorHandler::register();
$exception = new \Exception('foo');
$logger = $this->getMock('Psr\Log\LoggerInterface');
$that = $this;
$logArgCheck = function ($level, $message, $context) use ($that) {
$that->assertEquals('Uncaught Exception: foo', $message);
$that->assertArrayHasKey('type', $context);
$that->assertEquals($context['type'], E_ERROR);
};
$logger
->expects($this->exactly(2))
->method('log')
->will($this->returnCallback($logArgCheck))
;
$handler->setDefaultLogger($logger, E_ERROR);
try {
$handler->handleException($exception);
$this->fail('Exception expected');
} catch (\Exception $e) {
$this->assertSame($exception, $e);
}
$that = $this;
$handler->setExceptionHandler(function ($e) use ($exception, $that) {
$that->assertSame($exception, $e);
});
$handler->handleException($exception);
restore_error_handler();
restore_exception_handler();
} catch (\Exception $e) {
restore_error_handler();
restore_exception_handler();
throw $e;
}
}
public function testHandleFatalError()
{
try {
$handler = ErrorHandler::register();
$error = array(
'type' => E_PARSE,
'message' => 'foo',
'file' => 'bar',
'line' => 123,
);
$logger = $this->getMock('Psr\Log\LoggerInterface');
$that = $this;
$logArgCheck = function ($level, $message, $context) use ($that) {
$that->assertEquals('Fatal Parse Error: foo', $message);
$that->assertArrayHasKey('type', $context);
$that->assertEquals($context['type'], E_ERROR);
};
$logger
->expects($this->once())
->method('log')
->will($this->returnCallback($logArgCheck))
;
$handler->setDefaultLogger($logger, E_ERROR);
$handler->handleFatalError($error);
restore_error_handler();
restore_exception_handler();
} catch (\Exception $e) {
restore_error_handler();
restore_exception_handler();
throw $e;
}
}
public function testDeprecatedInterface()
{
try {
$handler = ErrorHandler::register(0);
$this->assertFalse($handler->handle(0, 'foo', 'foo.php', 12, array()));
restore_error_handler();
restore_exception_handler();
$logger = $this->getMock('Psr\Log\LoggerInterface');
@ -187,63 +410,12 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
@$undefVar++;
restore_error_handler();
restore_exception_handler();
} catch (\Exception $e) {
restore_error_handler();
restore_exception_handler();
throw $e;
}
}
/**
* @dataProvider provideFatalErrorHandlersData
*/
public function testFatalErrorHandlers($error, $class, $translatedMessage)
{
$handler = new ErrorHandler();
$exceptionHandler = new MockExceptionHandler();
$m = new \ReflectionMethod($handler, 'handleFatalError');
$m->setAccessible(true);
$m->invoke($handler, array($exceptionHandler, 'handle'), $error);
restore_error_handler();
$this->assertInstanceof($class, $exceptionHandler->e);
// class names are case insensitive and PHP/HHVM do not return the same
$this->assertSame(strtolower($translatedMessage), strtolower($exceptionHandler->e->getMessage()));
$this->assertSame($error['type'], $exceptionHandler->e->getSeverity());
$this->assertSame($error['file'], $exceptionHandler->e->getFile());
$this->assertSame($error['line'], $exceptionHandler->e->getLine());
}
public function provideFatalErrorHandlersData()
{
return array(
// undefined function
array(
array(
'type' => 1,
'line' => 12,
'file' => 'foo.php',
'message' => 'Call to undefined function test_namespaced_function_again()',
),
'Symfony\Component\Debug\Exception\UndefinedFunctionException',
'Attempted to call function "test_namespaced_function_again" from the global namespace in foo.php line 12. Did you mean to call: "\\symfony\\component\\debug\\tests\\test_namespaced_function_again"?',
),
// class not found
array(
array(
'type' => 1,
'line' => 12,
'file' => 'foo.php',
'message' => 'Class \'WhizBangFactory\' not found',
),
'Symfony\Component\Debug\Exception\ClassNotFoundException',
'Attempted to load class "WhizBangFactory" from the global namespace in foo.php line 12. Did you forget a use statement for this class?',
),
);
}
}
function test_namespaced_function_again()
{
}

View File

@ -162,8 +162,8 @@ class FlattenExceptionTest extends \PHPUnit_Framework_TestCase
'message' => 'test',
'class' => 'Exception',
'trace' => array(array(
'namespace' => '', 'short_class' => '', 'class' => '','type' => '','function' => '', 'file' => 'foo.php', 'line' => 123,
'args' => array(),
'namespace' => '', 'short_class' => '', 'class' => '','type' => '','function' => '', 'file' => 'foo.php', 'line' => 123,
'args' => array(),
)),
),
), $flattened->toArray());
@ -214,14 +214,14 @@ class FlattenExceptionTest extends \PHPUnit_Framework_TestCase
'class' => 'Exception',
'trace' => array(
array(
'namespace' => '', 'short_class' => '', 'class' => '','type' => '','function' => '',
'file' => 'foo.php', 'line' => 123,
'args' => array(),
'namespace' => '', 'short_class' => '', 'class' => '','type' => '','function' => '',
'file' => 'foo.php', 'line' => 123,
'args' => array(),
),
array(
'namespace' => '', 'short_class' => '', 'class' => '','type' => '','function' => 'test',
'file' => __FILE__, 'line' => 123,
'args' => array(
'namespace' => '', 'short_class' => '', 'class' => '','type' => '','function' => 'test',
'file' => __FILE__, 'line' => 123,
'args' => array(
array(
'incomplete-object', 'BogusTestClass',
),

View File

@ -12,6 +12,8 @@
namespace Symfony\Component\Debug\Tests;
use Symfony\Component\Debug\ExceptionHandler;
use Symfony\Component\Debug\Exception\OutOfMemoryException;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
@ -23,13 +25,13 @@ class ExceptionHandlerTest extends \PHPUnit_Framework_TestCase
$response = $handler->createResponse(new \RuntimeException('Foo'));
$this->assertContains('<h1>Whoops, looks like something went wrong.</h1>', $response->getContent());
$this->assertNotContains('<div class="block_exception clear_fix">', $response->getContent());
$this->assertNotContains('<h2 class="block_exception clear_fix">', $response->getContent());
$handler = new ExceptionHandler(true);
$response = $handler->createResponse(new \RuntimeException('Foo'));
$this->assertContains('<h1>Whoops, looks like something went wrong.</h1>', $response->getContent());
$this->assertContains('<div class="block_exception clear_fix">', $response->getContent());
$this->assertContains('<h2 class="block_exception clear_fix">', $response->getContent());
}
public function testStatusCode()
@ -59,4 +61,56 @@ class ExceptionHandlerTest extends \PHPUnit_Framework_TestCase
$handler = new ExceptionHandler(true);
$response = $handler->createResponse(new \RuntimeException('Foo', 0, new \RuntimeException('Bar')));
}
public function testHandle()
{
$exception = new \Exception('foo');
if (class_exists('Symfony\Component\HttpFoundation\Response')) {
$handler = $this->getMock('Symfony\Component\Debug\ExceptionHandler', array('createResponse'));
$handler
->expects($this->exactly(2))
->method('createResponse')
->will($this->returnValue(new Response()));
} else {
$handler = $this->getMock('Symfony\Component\Debug\ExceptionHandler', array('sendPhpResponse'));
$handler
->expects($this->exactly(2))
->method('sendPhpResponse');
}
$handler->handle($exception);
$that = $this;
$handler->setHandler(function ($e) use ($exception, $that) {
$that->assertSame($exception, $e);
});
$handler->handle($exception);
}
public function testHandleOutOfMemoryException()
{
$exception = new OutOfMemoryException('foo', 0, E_ERROR, __FILE__, __LINE__);
if (class_exists('Symfony\Component\HttpFoundation\Response')) {
$handler = $this->getMock('Symfony\Component\Debug\ExceptionHandler', array('createResponse'));
$handler
->expects($this->once())
->method('createResponse')
->will($this->returnValue(new Response()));
} else {
$handler = $this->getMock('Symfony\Component\Debug\ExceptionHandler', array('sendPhpResponse'));
$handler
->expects($this->once())
->method('sendPhpResponse');
}
$that = $this;
$handler->setHandler(function ($e) use ($that) {
$that->fail('OutOfMemoryException should bypass the handler');
});
$handler->handle($exception);
}
}

View File

@ -41,7 +41,7 @@ class ClassNotFoundFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
'file' => 'foo.php',
'message' => 'Class \'WhizBangFactory\' not found',
),
'Attempted to load class "WhizBangFactory" from the global namespace in foo.php line 12. Did you forget a use statement for this class?',
"Attempted to load class \"WhizBangFactory\" from the global namespace.\nDid you forget a \"use\" statement?",
),
array(
array(
@ -50,7 +50,7 @@ class ClassNotFoundFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
'file' => 'foo.php',
'message' => 'Class \'Foo\\Bar\\WhizBangFactory\' not found',
),
'Attempted to load class "WhizBangFactory" from namespace "Foo\\Bar" in foo.php line 12. Do you need to "use" it from another namespace?',
"Attempted to load class \"WhizBangFactory\" from namespace \"Foo\\Bar\".\nDid you forget a \"use\" statement for another namespace?",
),
array(
array(
@ -59,7 +59,7 @@ class ClassNotFoundFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
'file' => 'foo.php',
'message' => 'Class \'UndefinedFunctionException\' not found',
),
'Attempted to load class "UndefinedFunctionException" from the global namespace in foo.php line 12. Did you forget a use statement for this class? Perhaps you need to add a use statement for one of the following: Symfony\Component\Debug\Exception\UndefinedFunctionException.',
"Attempted to load class \"UndefinedFunctionException\" from the global namespace.\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?",
),
array(
array(
@ -68,7 +68,7 @@ class ClassNotFoundFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
'file' => 'foo.php',
'message' => 'Class \'PEARClass\' not found',
),
'Attempted to load class "PEARClass" from the global namespace in foo.php line 12. Did you forget a use statement for this class? Perhaps you need to add a use statement for one of the following: Symfony_Component_Debug_Tests_Fixtures_PEARClass.',
"Attempted to load class \"PEARClass\" from the global namespace.\nDid you forget a \"use\" statement for \"Symfony_Component_Debug_Tests_Fixtures_PEARClass\"?",
),
array(
array(
@ -77,7 +77,7 @@ class ClassNotFoundFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
'file' => 'foo.php',
'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found',
),
'Attempted to load class "UndefinedFunctionException" from namespace "Foo\Bar" in foo.php line 12. Do you need to "use" it from another namespace? Perhaps you need to add a use statement for one of the following: Symfony\Component\Debug\Exception\UndefinedFunctionException.',
"Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\Bar\".\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?",
),
);
}

View File

@ -42,7 +42,7 @@ class UndefinedFunctionFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
'file' => 'foo.php',
'message' => 'Call to undefined function test_namespaced_function()',
),
'Attempted to call function "test_namespaced_function" from the global namespace in foo.php line 12. Did you mean to call: "\\symfony\\component\\debug\\tests\\fatalerrorhandler\\test_namespaced_function"?',
"Attempted to call function \"test_namespaced_function\" from the global namespace.\nDid you mean to call \"\\symfony\\component\\debug\\tests\\fatalerrorhandler\\test_namespaced_function\"?",
),
array(
array(
@ -51,7 +51,7 @@ class UndefinedFunctionFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
'file' => 'foo.php',
'message' => 'Call to undefined function Foo\\Bar\\Baz\\test_namespaced_function()',
),
'Attempted to call function "test_namespaced_function" from namespace "Foo\\Bar\\Baz" in foo.php line 12. Did you mean to call: "\\symfony\\component\\debug\\tests\\fatalerrorhandler\\test_namespaced_function"?',
"Attempted to call function \"test_namespaced_function\" from namespace \"Foo\\Bar\\Baz\".\nDid you mean to call \"\\symfony\\component\\debug\\tests\\fatalerrorhandler\\test_namespaced_function\"?",
),
array(
array(
@ -60,7 +60,7 @@ class UndefinedFunctionFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
'file' => 'foo.php',
'message' => 'Call to undefined function foo()',
),
'Attempted to call function "foo" from the global namespace in foo.php line 12.',
'Attempted to call function "foo" from the global namespace.',
),
array(
array(
@ -69,7 +69,7 @@ class UndefinedFunctionFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
'file' => 'foo.php',
'message' => 'Call to undefined function Foo\\Bar\\Baz\\foo()',
),
'Attempted to call function "foo" from namespace "Foo\Bar\Baz" in foo.php line 12.',
'Attempted to call function "foo" from namespace "Foo\Bar\Baz".',
),
);
}

View File

@ -41,7 +41,7 @@ class UndefinedMethodFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
'file' => 'foo.php',
'message' => 'Call to undefined method SplObjectStorage::what()',
),
'Attempted to call method "what" on class "SplObjectStorage" in foo.php line 12.',
'Attempted to call method "what" on class "SplObjectStorage".',
),
array(
array(
@ -50,7 +50,7 @@ class UndefinedMethodFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
'file' => 'foo.php',
'message' => 'Call to undefined method SplObjectStorage::walid()',
),
'Attempted to call method "walid" on class "SplObjectStorage" in foo.php line 12. Did you mean to call: "valid"?',
"Attempted to call method \"walid\" on class \"SplObjectStorage\".\nDid you mean to call \"valid\"?",
),
array(
array(
@ -59,7 +59,7 @@ class UndefinedMethodFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
'file' => 'foo.php',
'message' => 'Call to undefined method SplObjectStorage::offsetFet()',
),
'Attempted to call method "offsetFet" on class "SplObjectStorage" in foo.php line 12. Did you mean to call: "offsetGet", "offsetSet", "offsetUnset"?',
"Attempted to call method \"offsetFet\" on class \"SplObjectStorage\".\nDid you mean to call e.g. \"offsetGet\", \"offsetSet\" or \"offsetUnset\"?",
),
);
}

View File

@ -16,7 +16,8 @@
}
],
"require": {
"php": ">=5.3.3"
"php": ">=5.3.3",
"psr/log": "~1.0"
},
"require-dev": {
"symfony/http-kernel": "~2.1",
@ -33,7 +34,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "2.5-dev"
"dev-master": "2.6-dev"
}
}
}

View File

@ -1,6 +1,11 @@
CHANGELOG
=========
2.6.0
-----
* added new factory syntax and deprecated the old one
2.5.0
-----

View File

@ -59,7 +59,7 @@ class AnalyzeServiceReferencesPass implements RepeatablePassInterface
public function process(ContainerBuilder $container)
{
$this->container = $container;
$this->graph = $container->getCompiler()->getServiceReferenceGraph();
$this->graph = $container->getCompiler()->getServiceReferenceGraph();
$this->graph->clear();
foreach ($container->getDefinitions() as $id => $definition) {
@ -81,6 +81,9 @@ class AnalyzeServiceReferencesPass implements RepeatablePassInterface
if ($definition->getConfigurator()) {
$this->processArguments(array($definition->getConfigurator()));
}
if ($definition->getFactory()) {
$this->processArguments(array($definition->getFactory()));
}
}
}

View File

@ -58,8 +58,8 @@ class CheckCircularReferencesPass implements CompilerPassInterface
private function checkOutEdges(array $edges)
{
foreach ($edges as $edge) {
$node = $edge->getDestNode();
$id = $node->getId();
$node = $edge->getDestNode();
$id = $node->getId();
if (empty($this->checkedNodes[$id])) {
$searchKey = array_search($id, $this->currentPath);

View File

@ -42,28 +42,22 @@ class CheckDefinitionValidityPass implements CompilerPassInterface
foreach ($container->getDefinitions() as $id => $definition) {
// synthetic service is public
if ($definition->isSynthetic() && !$definition->isPublic()) {
throw new RuntimeException(sprintf(
'A synthetic service ("%s") must be public.',
$id
));
throw new RuntimeException(sprintf('A synthetic service ("%s") must be public.', $id));
}
// synthetic service has non-prototype scope
if ($definition->isSynthetic() && ContainerInterface::SCOPE_PROTOTYPE === $definition->getScope()) {
throw new RuntimeException(sprintf(
'A synthetic service ("%s") cannot be of scope "prototype".',
$id
));
throw new RuntimeException(sprintf('A synthetic service ("%s") cannot be of scope "prototype".', $id));
}
if ($definition->getFactory() && ($definition->getFactoryClass() || $definition->getFactoryService() || $definition->getFactoryMethod())) {
throw new RuntimeException(sprintf('A service ("%s") can use either the old or the new factory syntax, not both.', $id));
}
// non-synthetic, non-abstract service has class
if (!$definition->isAbstract() && !$definition->isSynthetic() && !$definition->getClass()) {
if ($definition->getFactoryClass() || $definition->getFactoryService()) {
throw new RuntimeException(sprintf(
'Please add the class to service "%s" even if it is constructed by a factory '
.'since we might need to add method calls based on compile-time checks.',
$id
));
if ($definition->getFactory() || $definition->getFactoryClass() || $definition->getFactoryService()) {
throw new RuntimeException(sprintf('Please add the class to service "%s" even if it is constructed by a factory since we might need to add method calls based on compile-time checks.', $id));
}
throw new RuntimeException(sprintf(

View File

@ -64,9 +64,10 @@ class InlineServiceDefinitionsPass implements RepeatablePassInterface
);
$configurator = $this->inlineArguments($container, array($definition->getConfigurator()));
$definition->setConfigurator(
$configurator[0]
);
$definition->setConfigurator($configurator[0]);
$factory = $this->inlineArguments($container, array($definition->getFactory()));
$definition->setFactory($factory[0]);
}
}

View File

@ -103,6 +103,9 @@ class ResolveDefinitionTemplatesPass implements CompilerPassInterface
if (isset($changes['factory_service'])) {
$def->setFactoryService($definition->getFactoryService());
}
if (isset($changes['factory'])) {
$def->setFactory($definition->getFactory());
}
if (isset($changes['configurator'])) {
$def->setConfigurator($definition->getConfigurator());
}

View File

@ -12,6 +12,7 @@
namespace Symfony\Component\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
@ -84,7 +85,12 @@ class ResolveReferencesToAliasesPass implements CompilerPassInterface
*/
private function getDefinitionId($id)
{
$seen = array();
while ($this->container->hasAlias($id)) {
if (isset($seen[$id])) {
throw new ServiceCircularReferenceException($id, array_keys($seen));
}
$seen[$id] = true;
$id = (string) $this->container->getAlias($id);
}

View File

@ -217,11 +217,11 @@ class Container implements IntrospectableContainerInterface
$this->$method();
}
if (self::SCOPE_CONTAINER !== $scope && null === $service) {
unset($this->scopedServices[$scope][$id]);
}
if (null === $service) {
if (self::SCOPE_CONTAINER !== $scope) {
unset($this->scopedServices[$scope][$id]);
}
unset($this->services[$id]);
}
}
@ -450,14 +450,15 @@ class Container implements IntrospectableContainerInterface
// the global service map
$services = array($this->services, $this->scopedServices[$name]);
unset($this->scopedServices[$name]);
foreach ($this->scopeChildren[$name] as $child) {
if (!isset($this->scopedServices[$child])) {
continue;
}
$services[] = $this->scopedServices[$child];
unset($this->scopedServices[$child]);
foreach ($this->scopeChildren[$name] as $child) {
if (isset($this->scopedServices[$child])) {
$services[] = $this->scopedServices[$child];
unset($this->scopedServices[$child]);
}
}
// update global map
$this->services = call_user_func_array('array_diff_key', $services);
// check if we need to restore services of a previous scope of this type
@ -465,6 +466,10 @@ class Container implements IntrospectableContainerInterface
$services = $this->scopeStacks[$name]->pop();
$this->scopedServices += $services;
if ($this->scopeStacks[$name]->isEmpty()) {
unset($this->scopeStacks[$name]);
}
foreach ($services as $array) {
foreach ($array as $id => $service) {
$this->set($id, $service, $name);

View File

@ -25,6 +25,7 @@ use Symfony\Component\Config\Resource\ResourceInterface;
use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\InstantiatorInterface;
use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\RealServiceInstantiator;
use Symfony\Component\ExpressionLanguage\Expression;
use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
/**
* ContainerBuilder is a DI container that provides an API to easily describe services.
@ -84,6 +85,11 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
*/
private $expressionLanguage;
/**
* @var ExpressionFunctionProviderInterface[]
*/
private $expressionLanguageProviders = array();
/**
* Sets the track resources flag.
*
@ -464,10 +470,6 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
return $service;
}
if (isset($this->loading[$id])) {
throw new LogicException(sprintf('The service "%s" has a circular reference to itself.', $id), 0, $e);
}
if (!$this->hasDefinition($id) && isset($this->aliasDefinitions[$id])) {
return $this->get($this->aliasDefinitions[$id]);
}
@ -939,7 +941,19 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
$arguments = $this->resolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getArguments())));
if (null !== $definition->getFactoryMethod()) {
if (null !== $definition->getFactory()) {
$factory = $definition->getFactory();
if (is_string($factory)) {
$callable = $definition->getFactory();
} elseif (is_array($factory)) {
$callable = array($this->resolveServices($factory[0]), $factory[1]);
} else {
throw new RuntimeException(sprintf('Cannot create service "%s" because of invalid factory', $id));
}
$service = call_user_func_array($callable, $arguments);
} elseif (null !== $definition->getFactoryMethod()) {
if (null !== $definition->getFactoryClass()) {
$factory = $parameterBag->resolveValue($definition->getFactoryClass());
} elseif (null !== $definition->getFactoryService()) {
@ -1056,6 +1070,11 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
return array_unique($tags);
}
public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider)
{
$this->expressionLanguageProviders[] = $provider;
}
/**
* Returns the Service Conditionals.
*
@ -1161,7 +1180,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
throw new RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.');
}
$this->expressionLanguage = new ExpressionLanguage();
$this->expressionLanguage = new ExpressionLanguage(null, $this->expressionLanguageProviders);
}
return $this->expressionLanguage;

View File

@ -26,10 +26,10 @@ use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
interface ContainerInterface
{
const EXCEPTION_ON_INVALID_REFERENCE = 1;
const NULL_ON_INVALID_REFERENCE = 2;
const IGNORE_ON_INVALID_REFERENCE = 3;
const SCOPE_CONTAINER = 'container';
const SCOPE_PROTOTYPE = 'prototype';
const NULL_ON_INVALID_REFERENCE = 2;
const IGNORE_ON_INVALID_REFERENCE = 3;
const SCOPE_CONTAINER = 'container';
const SCOPE_PROTOTYPE = 'prototype';
/**
* Sets a service.

View File

@ -25,6 +25,7 @@ class Definition
{
private $class;
private $file;
private $factory;
private $factoryClass;
private $factoryMethod;
private $factoryService;
@ -56,6 +57,34 @@ class Definition
$this->arguments = $arguments;
}
/**
* Sets a factory.
*
* @param string|array $factory A PHP function or an array containing a class/Reference and a method to call
*
* @return Definition The current instance
*/
public function setFactory($factory)
{
if (is_string($factory) && strpos($factory, '::') !== false) {
$factory = explode('::', $factory, 2);
}
$this->factory = $factory;
return $this;
}
/**
* Gets the factory.
*
* @return string|array The PHP function or an array containing a class/Reference and a method to call
*/
public function getFactory()
{
return $this->factory;
}
/**
* Sets the name of the class that acts as a factory using the factory method,
* which will be invoked statically.
@ -65,6 +94,7 @@ class Definition
* @return Definition The current instance
*
* @api
* @deprecated Deprecated since version 2.6, to be removed in 3.0.
*/
public function setFactoryClass($factoryClass)
{
@ -79,6 +109,7 @@ class Definition
* @return string|null The factory class name
*
* @api
* @deprecated Deprecated since version 2.6, to be removed in 3.0.
*/
public function getFactoryClass()
{
@ -93,6 +124,7 @@ class Definition
* @return Definition The current instance
*
* @api
* @deprecated Deprecated since version 2.6, to be removed in 3.0.
*/
public function setFactoryMethod($factoryMethod)
{
@ -142,6 +174,7 @@ class Definition
* @return string|null The factory method name
*
* @api
* @deprecated Deprecated since version 2.6, to be removed in 3.0.
*/
public function getFactoryMethod()
{
@ -156,6 +189,7 @@ class Definition
* @return Definition The current instance
*
* @api
* @deprecated Deprecated since version 2.6, to be removed in 3.0.
*/
public function setFactoryService($factoryService)
{
@ -170,6 +204,7 @@ class Definition
* @return string|null The factory service id
*
* @api
* @deprecated Deprecated since version 2.6, to be removed in 3.0.
*/
public function getFactoryService()
{

View File

@ -76,6 +76,16 @@ class DefinitionDecorator extends Definition
return parent::setClass($class);
}
/**
* {@inheritdoc}
*/
public function setFactory($callable)
{
$this->changes['factory'] = true;
return parent::setFactory($callable);
}
/**
* {@inheritdoc}
*

View File

@ -35,8 +35,8 @@ class GraphvizDumper extends Dumper
private $edges;
private $options = array(
'graph' => array('ratio' => 'compress'),
'node' => array('fontsize' => 11, 'fontname' => 'Arial', 'shape' => 'record'),
'edge' => array('fontsize' => 9, 'fontname' => 'Arial', 'color' => 'grey', 'arrowhead' => 'open', 'arrowsize' => 0.5),
'node' => array('fontsize' => 11, 'fontname' => 'Arial', 'shape' => 'record'),
'edge' => array('fontsize' => 9, 'fontname' => 'Arial', 'color' => 'grey', 'arrowhead' => 'open', 'arrowsize' => 0.5),
'node.instance' => array('fillcolor' => '#9999ff', 'style' => 'filled'),
'node.definition' => array('fillcolor' => '#eeeeee'),
'node.missing' => array('fillcolor' => '#ff9999', 'style' => 'filled'),

View File

@ -25,6 +25,7 @@ use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface as
use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\NullDumper;
use Symfony\Component\DependencyInjection\ExpressionLanguage;
use Symfony\Component\ExpressionLanguage\Expression;
use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
/**
* PhpDumper dumps a service container as a PHP class.
@ -55,6 +56,11 @@ class PhpDumper extends Dumper
private $reservedVariables = array('instance', 'class');
private $expressionLanguage;
/**
* @var ExpressionFunctionProviderInterface[]
*/
private $expressionLanguageProviders = array();
/**
* @var \Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface
*/
@ -100,7 +106,7 @@ class PhpDumper extends Dumper
public function dump(array $options = array())
{
$options = array_merge(array(
'class' => 'ProjectServiceContainer',
'class' => 'ProjectServiceContainer',
'base_class' => 'Container',
'namespace' => '',
), $options);
@ -109,6 +115,7 @@ class PhpDumper extends Dumper
if ($this->container->isFrozen()) {
$code .= $this->addFrozenConstructor();
$code .= $this->addFrozenCompile();
} else {
$code .= $this->addConstructor();
}
@ -160,6 +167,7 @@ class PhpDumper extends Dumper
$this->getServiceCallsFromArguments($iDefinition->getMethodCalls(), $calls, $behavior);
$this->getServiceCallsFromArguments($iDefinition->getProperties(), $calls, $behavior);
$this->getServiceCallsFromArguments(array($iDefinition->getConfigurator()), $calls, $behavior);
$this->getServiceCallsFromArguments(array($iDefinition->getFactory()), $calls, $behavior);
}
$code = '';
@ -339,9 +347,9 @@ class PhpDumper extends Dumper
throw new InvalidArgumentException(sprintf('"%s" is not a valid class name for the "%s" service.', $class, $id));
}
$simple = $this->isSimpleInstance($id, $definition);
$simple = $this->isSimpleInstance($id, $definition);
$isProxyCandidate = $this->getProxyDumper()->isProxyCandidate($definition);
$instantiation = '';
$instantiation = '';
if (!$isProxyCandidate && ContainerInterface::SCOPE_CONTAINER === $definition->getScope()) {
$instantiation = "\$this->services['$id'] = ".($simple ? '' : '$instance');
@ -522,6 +530,17 @@ class PhpDumper extends Dumper
$return[] = '@throws RuntimeException always since this service is expected to be injected dynamically';
} elseif ($class = $definition->getClass()) {
$return[] = sprintf("@return %s A %s instance.", 0 === strpos($class, '%') ? 'object' : "\\".$class, $class);
} elseif ($definition->getFactory()) {
$factory = $definition->getFactory();
if (is_string($factory)) {
$return[] = sprintf('@return object An instance returned by %s().', $factory);
} elseif (is_array($factory) && (is_string($factory[0]) || $factory[0] instanceof Definition || $factory[0] instanceof Reference)) {
if (is_string($factory[0]) || $factory[0] instanceof Reference) {
$return[] = sprintf('@return object An instance returned by %s::%s().', (string) $factory[0], $factory[1]);
} elseif ($factory[0] instanceof Definition) {
$return[] = sprintf('@return object An instance returned by %s::%s().', $factory[0]->getClass(), $factory[1]);
}
}
} elseif ($definition->getFactoryClass()) {
$return[] = sprintf('@return object An instance returned by %s::%s().', $definition->getFactoryClass(), $definition->getFactoryMethod());
} elseif ($definition->getFactoryService()) {
@ -559,17 +578,17 @@ EOF;
}
if ($definition->isLazy()) {
$lazyInitialization = '$lazyLoad = true';
$lazyInitialization = '$lazyLoad = true';
$lazyInitializationDoc = "\n * @param bool \$lazyLoad whether to try lazy-loading the service with a proxy\n *";
} else {
$lazyInitialization = '';
$lazyInitialization = '';
$lazyInitializationDoc = '';
}
// with proxies, for 5.3.3 compatibility, the getter must be public to be accessible to the initializer
$isProxyCandidate = $this->getProxyDumper()->isProxyCandidate($definition);
$visibility = $isProxyCandidate ? 'public' : 'protected';
$code = <<<EOF
$visibility = $isProxyCandidate ? 'public' : 'protected';
$code = <<<EOF
/**
* Gets the '$id' service.$doc
@ -700,7 +719,25 @@ EOF;
$arguments[] = $this->dumpValue($value);
}
if (null !== $definition->getFactoryMethod()) {
if (null !== $definition->getFactory()) {
$callable = $definition->getFactory();
if (is_array($callable)) {
if ($callable[0] instanceof Reference
|| ($callable[0] instanceof Definition && $this->definitionVariables->contains($callable[0]))) {
return sprintf(" $return{$instantiation}%s->%s(%s);\n", $this->dumpValue($callable[0]), $callable[1], $arguments ? implode(', ', $arguments) : '');
}
$class = $this->dumpValue($callable[0]);
// If the class is a string we can optimize call_user_func away
if (strpos($class, "'") === 0) {
return sprintf(" $return{$instantiation}%s::%s(%s);\n", $this->dumpLiteralClass($class), $callable[1], $arguments ? implode(', ', $arguments) : '');
}
return sprintf(" $return{$instantiation}call_user_func(array(%s, '%s')%s);\n", $this->dumpValue($callable[0]), $callable[1], $arguments ? ', '.implode(', ', $arguments) : '');
}
return sprintf(" $return{$instantiation}\\%s(%s);\n", $callable, $arguments ? implode(', ', $arguments) : '');
} elseif (null !== $definition->getFactoryMethod()) {
if (null !== $definition->getFactoryClass()) {
$class = $this->dumpValue($definition->getFactoryClass());
@ -769,16 +806,18 @@ EOF;
*/
private function addConstructor()
{
$arguments = $this->container->getParameterBag()->all() ? 'new ParameterBag($this->getDefaultParameters())' : null;
$parameters = $this->exportParameters($this->container->getParameterBag()->all());
$code = <<<EOF
private static \$parameters = $parameters;
/**
* Constructor.
*/
public function __construct()
{
parent::__construct($arguments);
parent::__construct(new ParameterBag(self::\$parameters));
EOF;
@ -806,23 +845,17 @@ EOF;
*/
private function addFrozenConstructor()
{
$parameters = $this->exportParameters($this->container->getParameterBag()->all());
$code = <<<EOF
private \$parameters;
private static \$parameters = $parameters;
/**
* Constructor.
*/
public function __construct()
{
EOF;
if ($this->container->getParameterBag()->all()) {
$code .= "\n \$this->parameters = \$this->getDefaultParameters();\n";
}
$code .= <<<EOF
\$this->services =
\$this->scopedServices =
\$this->scopeStacks = array();
@ -851,6 +884,26 @@ EOF;
return $code;
}
/**
* Adds the constructor for a frozen container.
*
* @return string
*/
private function addFrozenCompile()
{
return <<<EOF
/**
* {@inheritdoc}
*/
public function compile()
{
throw new LogicException('You cannot compile a dumped frozen container.');
}
EOF;
}
/**
* Adds the methodMap property definition
*
@ -910,8 +963,6 @@ EOF;
return '';
}
$parameters = $this->exportParameters($this->container->getParameterBag()->all());
$code = '';
if ($this->container->isFrozen()) {
$code .= <<<EOF
@ -923,11 +974,11 @@ EOF;
{
\$name = strtolower(\$name);
if (!(isset(\$this->parameters[\$name]) || array_key_exists(\$name, \$this->parameters))) {
if (!(isset(self::\$parameters[\$name]) || array_key_exists(\$name, self::\$parameters))) {
throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', \$name));
}
return \$this->parameters[\$name];
return self::\$parameters[\$name];
}
/**
@ -937,7 +988,7 @@ EOF;
{
\$name = strtolower(\$name);
return isset(\$this->parameters[\$name]) || array_key_exists(\$name, \$this->parameters);
return isset(self::\$parameters[\$name]) || array_key_exists(\$name, self::\$parameters);
}
/**
@ -954,28 +1005,15 @@ EOF;
public function getParameterBag()
{
if (null === \$this->parameterBag) {
\$this->parameterBag = new FrozenParameterBag(\$this->parameters);
\$this->parameterBag = new FrozenParameterBag(self::\$parameters);
}
return \$this->parameterBag;
}
EOF;
}
$code .= <<<EOF
/**
* Gets the default parameters.
*
* @return array An array of the default parameters
*/
protected function getDefaultParameters()
{
return $parameters;
}
EOF;
return $code;
}
@ -1095,7 +1133,8 @@ EOF;
$this->getDefinitionsFromArguments($definition->getArguments()),
$this->getDefinitionsFromArguments($definition->getMethodCalls()),
$this->getDefinitionsFromArguments($definition->getProperties()),
$this->getDefinitionsFromArguments(array($definition->getConfigurator()))
$this->getDefinitionsFromArguments(array($definition->getConfigurator())),
$this->getDefinitionsFromArguments(array($definition->getFactory()))
);
$this->inlinedDefinitions->offsetSet($definition, $definitions);
@ -1173,7 +1212,7 @@ EOF;
/**
* Dumps values.
*
* @param array $value
* @param mixed $value
* @param bool $interpolate
*
* @return string
@ -1210,6 +1249,30 @@ EOF;
throw new RuntimeException('Cannot dump definitions which have a variable class name.');
}
if (null !== $value->getFactory()) {
$factory = $value->getFactory();
if (is_string($factory)) {
return sprintf('\\%s(%s)', $factory, implode(', ', $arguments));
}
if (is_array($factory)) {
if (is_string($factory[0])) {
return sprintf('\\%s::%s(%s)', $factory[0], $factory[1], implode(', ', $arguments));
}
if ($factory[0] instanceof Definition) {
return sprintf("call_user_func(array(%s, '%s')%s)", $this->dumpValue($factory[0]), $factory[1], count($arguments) > 0 ? ', '.implode(', ', $arguments) : '');
}
if ($factory[0] instanceof Reference) {
return sprintf('%s->%s(%s)', $this->dumpValue($factory[0]), $factory[1], implode(', ', $arguments));
}
}
throw new RuntimeException('Cannot dump definition because of invalid factory');
}
if (null !== $value->getFactoryMethod()) {
if (null !== $value->getFactoryClass()) {
return sprintf("call_user_func(array(%s, '%s')%s)", $this->dumpValue($value->getFactoryClass()), $value->getFactoryMethod(), count($arguments) > 0 ? ', '.implode(', ', $arguments) : '');
@ -1283,6 +1346,11 @@ EOF;
return sprintf("\$this->getParameter('%s')", strtolower($name));
}
public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider)
{
$this->expressionLanguageProviders[] = $provider;
}
/**
* Gets a service call
*
@ -1372,7 +1440,7 @@ EOF;
if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
throw new RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.');
}
$this->expressionLanguage = new ExpressionLanguage();
$this->expressionLanguage = new ExpressionLanguage(null, $this->expressionLanguageProviders);
}
return $this->expressionLanguage;

View File

@ -176,6 +176,17 @@ class XmlDumper extends Dumper
$this->addMethodCalls($definition->getMethodCalls(), $service);
if ($callable = $definition->getFactory()) {
$factory = $this->document->createElement('factory');
if (is_array($callable)) {
$factory->setAttribute($callable[0] instanceof Reference ? 'service' : 'class', $callable[0]);
$factory->setAttribute('method', $callable[1]);
} else {
$factory->setAttribute('function', $callable);
}
$service->appendChild($factory);
}
if ($callable = $definition->getConfigurator()) {
$configurator = $this->document->createElement('configurator');
if (is_array($callable)) {

View File

@ -147,16 +147,12 @@ class YamlDumper extends Dumper
}
}
if ($callable = $definition->getConfigurator()) {
if (is_array($callable)) {
if ($callable[0] instanceof Reference) {
$callable = array($this->getServiceCall((string) $callable[0], $callable[0]), $callable[1]);
} else {
$callable = array($callable[0], $callable[1]);
}
}
if ($callable = $definition->getFactory()) {
$code .= sprintf(" factory: %s\n", $this->dumper->dump($this->dumpCallable($callable), 0));
}
$code .= sprintf(" configurator: %s\n", $this->dumper->dump($callable, 0));
if ($callable = $definition->getConfigurator()) {
$code .= sprintf(" configurator: %s\n", $this->dumper->dump($this->dumpCallable($callable), 0));
}
return $code;
@ -222,6 +218,26 @@ class YamlDumper extends Dumper
return $this->dumper->dump(array('parameters' => $parameters), 2);
}
/**
* Dumps callable to YAML format
*
* @param callable $callable
*
* @return callable
*/
private function dumpCallable($callable)
{
if (is_array($callable)) {
if ($callable[0] instanceof Reference) {
$callable = array($this->getServiceCall((string) $callable[0], $callable[0]), $callable[1]);
} else {
$callable = array($callable[0], $callable[1]);
}
}
return $callable;
}
/**
* Dumps the value to YAML format
*

View File

@ -12,31 +12,22 @@
namespace Symfony\Component\DependencyInjection;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage as BaseExpressionLanguage;
use Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface;
/**
* Adds some function to the default ExpressionLanguage.
*
* To get a service, use service('request').
* To get a parameter, use parameter('kernel.debug').
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @see ExpressionLanguageProvider
*/
class ExpressionLanguage extends BaseExpressionLanguage
{
protected function registerFunctions()
public function __construct(ParserCacheInterface $cache = null, array $providers = array())
{
parent::registerFunctions();
// prepend the default provider to let users overide it easily
array_unshift($providers, new ExpressionLanguageProvider());
$this->register('service', function ($arg) {
return sprintf('$this->get(%s)', $arg);
}, function (array $variables, $value) {
return $variables['container']->get($value);
});
$this->register('parameter', function ($arg) {
return sprintf('$this->getParameter(%s)', $arg);
}, function (array $variables, $value) {
return $variables['container']->getParameter($value);
});
parent::__construct($cache, $providers);
}
}

View File

@ -0,0 +1,43 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\DependencyInjection;
use Symfony\Component\ExpressionLanguage\ExpressionFunction;
use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
/**
* Define some ExpressionLanguage functions.
*
* To get a service, use service('request').
* To get a parameter, use parameter('kernel.debug').
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ExpressionLanguageProvider implements ExpressionFunctionProviderInterface
{
public function getFunctions()
{
return array(
new ExpressionFunction('service', function ($arg) {
return sprintf('$this->get(%s)', $arg);
}, function (array $variables, $value) {
return $variables['container']->get($value);
}),
new ExpressionFunction('parameter', function ($arg) {
return sprintf('$this->getParameter(%s)', $arg);
}, function (array $variables, $value) {
return $variables['container']->getParameter($value);
}),
);
}
}

View File

@ -36,23 +36,15 @@ class ClosureLoader extends Loader
}
/**
* Loads a Closure.
*
* @param \Closure $closure The resource
* @param string $type The resource type
* {@inheritdoc}
*/
public function load($closure, $type = null)
public function load($resource, $type = null)
{
call_user_func($closure, $this->container);
call_user_func($resource, $this->container);
}
/**
* Returns true if this class supports the given resource.
*
* @param mixed $resource A resource
* @param string $type The resource type
*
* @return bool true if this class supports the given resource, false otherwise
* {@inheritdoc}
*/
public function supports($resource, $type = null)
{

View File

@ -27,8 +27,8 @@ abstract class FileLoader extends BaseFileLoader
/**
* Constructor.
*
* @param ContainerBuilder $container A ContainerBuilder instance
* @param FileLocatorInterface $locator A FileLocator instance
* @param ContainerBuilder $container A ContainerBuilder instance
* @param FileLocatorInterface $locator A FileLocator instance
*/
public function __construct(ContainerBuilder $container, FileLocatorInterface $locator)
{

View File

@ -22,22 +22,17 @@ use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
class IniFileLoader extends FileLoader
{
/**
* Loads a resource.
*
* @param mixed $file The resource
* @param string $type The resource type
*
* @throws InvalidArgumentException When ini file is not valid
* {@inheritdoc}
*/
public function load($file, $type = null)
public function load($resource, $type = null)
{
$path = $this->locator->locate($file);
$path = $this->locator->locate($resource);
$this->container->addResource(new FileResource($path));
$result = parse_ini_file($path, true);
if (false === $result || array() === $result) {
throw new InvalidArgumentException(sprintf('The "%s" file is not valid.', $file));
throw new InvalidArgumentException(sprintf('The "%s" file is not valid.', $resource));
}
if (isset($result['parameters']) && is_array($result['parameters'])) {
@ -48,12 +43,7 @@ class IniFileLoader extends FileLoader
}
/**
* Returns true if this class supports the given resource.
*
* @param mixed $resource A resource
* @param string $type The resource type
*
* @return bool true if this class supports the given resource, false otherwise
* {@inheritdoc}
*/
public function supports($resource, $type = null)
{

View File

@ -24,18 +24,15 @@ use Symfony\Component\Config\Resource\FileResource;
class PhpFileLoader extends FileLoader
{
/**
* Loads a PHP file.
*
* @param mixed $file The resource
* @param string $type The resource type
* {@inheritdoc}
*/
public function load($file, $type = null)
public function load($resource, $type = null)
{
// the container and loader variables are exposed to the included file below
$container = $this->container;
$loader = $this;
$path = $this->locator->locate($file);
$path = $this->locator->locate($resource);
$this->setCurrentDir(dirname($path));
$this->container->addResource(new FileResource($path));
@ -43,12 +40,7 @@ class PhpFileLoader extends FileLoader
}
/**
* Returns true if this class supports the given resource.
*
* @param mixed $resource A resource
* @param string $type The resource type
*
* @return bool true if this class supports the given resource, false otherwise
* {@inheritdoc}
*/
public function supports($resource, $type = null)
{

View File

@ -32,14 +32,11 @@ class XmlFileLoader extends FileLoader
const NS = 'http://symfony.com/schema/dic/services';
/**
* Loads an XML file.
*
* @param mixed $file The resource
* @param string $type The resource type
* {@inheritdoc}
*/
public function load($file, $type = null)
public function load($resource, $type = null)
{
$path = $this->locator->locate($file);
$path = $this->locator->locate($resource);
$xml = $this->parseFileToDOM($path);
@ -62,12 +59,7 @@ class XmlFileLoader extends FileLoader
}
/**
* Returns true if this class supports the given resource.
*
* @param mixed $resource A resource
* @param string $type The resource type
*
* @return bool true if this class supports the given resource, false otherwise
* {@inheritdoc}
*/
public function supports($resource, $type = null)
{
@ -167,6 +159,21 @@ class XmlFileLoader extends FileLoader
$definition->setArguments($this->getArgumentsAsPhp($service, 'argument'));
$definition->setProperties($this->getArgumentsAsPhp($service, 'property'));
if ($factories = $this->getChildren($service, 'factory')) {
$factory = $factories[0];
if ($function = $factory->getAttribute('function')) {
$definition->setFactory($function);
} else {
if ($childService = $factory->getAttribute('service')) {
$class = new Reference($childService, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, false);
} else {
$class = $factory->getAttribute('class');
}
$definition->setFactory(array($class, $factory->getAttribute('method')));
}
}
if ($configurators = $this->getChildren($service, 'configurator')) {
$configurator = $configurators[0];
if ($function = $configurator->getAttribute('function')) {

View File

@ -33,14 +33,11 @@ class YamlFileLoader extends FileLoader
private $yamlParser;
/**
* Loads a Yaml file.
*
* @param mixed $file The resource
* @param string $type The resource type
* {@inheritdoc}
*/
public function load($file, $type = null)
public function load($resource, $type = null)
{
$path = $this->locator->locate($file);
$path = $this->locator->locate($resource);
$content = $this->loadFile($path);
@ -57,7 +54,7 @@ class YamlFileLoader extends FileLoader
// parameters
if (isset($content['parameters'])) {
if (!is_array($content['parameters'])) {
throw new InvalidArgumentException(sprintf('The "parameters" key should contain an array in %s. Check your YAML syntax.', $file));
throw new InvalidArgumentException(sprintf('The "parameters" key should contain an array in %s. Check your YAML syntax.', $resource));
}
foreach ($content['parameters'] as $key => $value) {
@ -69,16 +66,11 @@ class YamlFileLoader extends FileLoader
$this->loadFromExtensions($content);
// services
$this->parseDefinitions($content, $file);
$this->parseDefinitions($content, $resource);
}
/**
* Returns true if this class supports the given resource.
*
* @param mixed $resource A resource
* @param string $type The resource type
*
* @return bool true if this class supports the given resource, false otherwise
* {@inheritdoc}
*/
public function supports($resource, $type = null)
{
@ -194,6 +186,19 @@ class YamlFileLoader extends FileLoader
$definition->setAbstract($service['abstract']);
}
if (isset($service['factory'])) {
if (is_string($service['factory'])) {
if (strpos($service['factory'], ':') !== false && strpos($service['factory'], '::') === false) {
$parts = explode(':', $service['factory']);
$definition->setFactory(array($this->resolveServices('@'.$parts[0]), $parts[1]));
} else {
$definition->setFactory($service['factory']);
}
} else {
$definition->setFactory(array($this->resolveServices($service['factory'][0]), $service['factory'][1]));
}
}
if (isset($service['factory_class'])) {
$definition->setFactoryClass($service['factory_class']);
}

View File

@ -64,7 +64,7 @@
<xsd:attribute name="ignore-errors" type="boolean" />
</xsd:complexType>
<xsd:complexType name="configurator">
<xsd:complexType name="callable">
<xsd:attribute name="id" type="xsd:string" />
<xsd:attribute name="service" type="xsd:string" />
<xsd:attribute name="class" type="xsd:string" />
@ -76,7 +76,8 @@
<xsd:choice maxOccurs="unbounded">
<xsd:element name="file" type="xsd:string" minOccurs="0" maxOccurs="1" />
<xsd:element name="argument" type="argument" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="configurator" type="configurator" minOccurs="0" maxOccurs="1" />
<xsd:element name="configurator" type="callable" minOccurs="0" maxOccurs="1" />
<xsd:element name="factory" type="callable" minOccurs="0" maxOccurs="1" />
<xsd:element name="call" type="call" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="tag" type="tag" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="property" type="property" minOccurs="0" maxOccurs="unbounded" />

View File

@ -6,45 +6,50 @@ Injection Container.
Here is a simple example that shows how to register services and parameters:
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
```php
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
$sc = new ContainerBuilder();
$sc
->register('foo', '%foo.class%')
->addArgument(new Reference('bar'))
;
$sc->setParameter('foo.class', 'Foo');
$sc = new ContainerBuilder();
$sc
->register('foo', '%foo.class%')
->addArgument(new Reference('bar'))
;
$sc->setParameter('foo.class', 'Foo');
$sc->get('foo');
$sc->get('foo');
```
Method Calls (Setter Injection):
$sc = new ContainerBuilder();
```php
$sc = new ContainerBuilder();
$sc
->register('bar', '%bar.class%')
->addMethodCall('setFoo', array(new Reference('foo')))
;
$sc->setParameter('bar.class', 'Bar');
$sc
->register('bar', '%bar.class%')
->addMethodCall('setFoo', array(new Reference('foo')))
;
$sc->setParameter('bar.class', 'Bar');
$sc->get('bar');
$sc->get('bar');
```
Factory Class:
If your service is retrieved by calling a static method:
$sc = new ContainerBuilder();
```php
$sc = new ContainerBuilder();
$sc
->register('bar', '%bar.class%')
->setFactoryClass('%bar.class%')
->setFactoryMethod('getInstance')
->addArgument('Aarrg!!!')
;
$sc->setParameter('bar.class', 'Bar');
$sc
->register('bar', '%bar.class%')
->setFactory(array('%bar.class%', 'getInstance'))
->addArgument('Aarrg!!!')
;
$sc->setParameter('bar.class', 'Bar');
$sc->get('bar');
$sc->get('bar');
```
File Include:
@ -52,16 +57,18 @@ For some services, especially those that are difficult or impossible to
autoload, you may need the container to include a file before
instantiating your class.
$sc = new ContainerBuilder();
```php
$sc = new ContainerBuilder();
$sc
->register('bar', '%bar.class%')
->setFile('/path/to/file')
->addArgument('Aarrg!!!')
;
$sc->setParameter('bar.class', 'Bar');
$sc
->register('bar', '%bar.class%')
->setFile('/path/to/file')
->addArgument('Aarrg!!!')
;
$sc->setParameter('bar.class', 'Bar');
$sc->get('bar');
$sc->get('bar');
```
Resources
---------

View File

@ -50,6 +50,17 @@ class CheckDefinitionValidityPassTest extends \PHPUnit_Framework_TestCase
$this->process($container);
}
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
*/
public function testProcessDetectsBothFactorySyntaxesUsed()
{
$container = new ContainerBuilder();
$container->register('a')->setFactory(array('a', 'b'))->setFactoryClass('a');
$this->process($container);
}
public function testProcess()
{
$container = new ContainerBuilder();

View File

@ -48,6 +48,17 @@ class ResolveReferencesToAliasesPassTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('foo', (string) $arguments[0]);
}
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException
*/
public function testAliasCircularReference()
{
$container = new ContainerBuilder();
$container->setAlias('bar', 'foo');
$container->setAlias('foo', 'bar');
$this->process($container);
}
protected function process(ContainerBuilder $container)
{
$pass = new ResolveReferencesToAliasesPass();

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