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

View File

@ -173,4 +173,30 @@ class CompiledRoute extends SymfonyCompiledRoute {
return $this->route->getRequirements(); 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\\Translation\\' => array($vendorDir . '/symfony/translation'),
'Symfony\\Component\\Serializer\\' => array($vendorDir . '/symfony/serializer'), 'Symfony\\Component\\Serializer\\' => array($vendorDir . '/symfony/serializer'),
'Symfony\\Component\\Routing\\' => array($vendorDir . '/symfony/routing'), 'Symfony\\Component\\Routing\\' => array($vendorDir . '/symfony/routing'),
'Symfony\\Component\\Process\\' => array($vendorDir . '/symfony/process'),
'Symfony\\Component\\HttpKernel\\' => array($vendorDir . '/symfony/http-kernel'), 'Symfony\\Component\\HttpKernel\\' => array($vendorDir . '/symfony/http-kernel'),
'Symfony\\Component\\HttpFoundation\\' => array($vendorDir . '/symfony/http-foundation'), 'Symfony\\Component\\HttpFoundation\\' => array($vendorDir . '/symfony/http-foundation'),
'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'), '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## ##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## ##Usage##
@ -24,7 +29,7 @@ More advanced example (returns detailed diagnostic error codes):
```php ```php
<?php <?php
use egulias\EmailValidator\EmailValidator; use Egulias\EmailValidator\EmailValidator;
$validator = new EmailValidator; $validator = new EmailValidator;
$email = 'dominic@sayers.cc'; $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 * 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 * Dominic Sayers [@dominicsayers](http://github.com/dominicsayers): The original isemail function
##Licence## ##License##
Released under the MIT Licence attached with this code. Released under the MIT License attached with this code.

View File

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

View File

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

View File

@ -31,10 +31,13 @@ class EmailLexer extends AbstractLexer
const S_SEMICOLON = 275; const S_SEMICOLON = 275;
const S_OPENQBRACKET = 276; const S_OPENQBRACKET = 276;
const S_CLOSEQBRACKET = 277; const S_CLOSEQBRACKET = 277;
const S_SLASH = 278;
const S_EMPTY = null; const S_EMPTY = null;
const GENERIC = 300; const GENERIC = 300;
const CRLF = 301; const CRLF = 301;
const INVALID = 302; 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) * 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_SEMICOLON,
'@' => self::S_AT, '@' => self::S_AT,
'\\' => self::S_BACKSLASH, '\\' => self::S_BACKSLASH,
'/' => self::S_SLASH,
',' => self::S_COMMA, ',' => self::S_COMMA,
'.' => self::S_DOT, '.' => self::S_DOT,
'"' => self::S_DQUOTE, '"' => self::S_DQUOTE,
@ -67,14 +71,31 @@ class EmailLexer extends AbstractLexer
'>' => self::S_GREATERTHAN, '>' => self::S_GREATERTHAN,
'{' => self::S_OPENQBRACKET, '{' => self::S_OPENQBRACKET,
'}' => self::S_CLOSEQBRACKET, '}' => self::S_CLOSEQBRACKET,
'' => self::S_EMPTY '' => self::S_EMPTY,
'\0' => self::C_NUL,
); );
protected $invalidASCII = array(226 => 1,);
protected $hasInvalidTokens = false;
protected $previous; protected $previous;
public function reset()
{
$this->hasInvalidTokens = false;
parent::reset();
}
public function hasInvalidTokens()
{
return $this->hasInvalidTokens;
}
/** /**
* @param $type * @param $type
* @throws \UnexpectedValueException * @throws \UnexpectedValueException
* @return boolean
*/ */
public function find($type) public function find($type)
{ {
@ -100,7 +121,7 @@ class EmailLexer extends AbstractLexer
/** /**
* moveNext * moveNext
* *
* @return mixed * @return boolean
*/ */
public function moveNext() public function moveNext()
{ {
@ -112,7 +133,7 @@ class EmailLexer extends AbstractLexer
/** /**
* Lexical catchable patterns. * Lexical catchable patterns.
* *
* @return array * @return string[]
*/ */
protected function getCatchablePatterns() protected function getCatchablePatterns()
{ {
@ -130,7 +151,7 @@ class EmailLexer extends AbstractLexer
/** /**
* Lexical non-catchable patterns. * Lexical non-catchable patterns.
* *
* @return array * @return string[]
*/ */
protected function getNonCatchablePatterns() protected function getNonCatchablePatterns()
{ {
@ -146,14 +167,48 @@ class EmailLexer extends AbstractLexer
*/ */
protected function getType(&$value) protected function getType(&$value)
{ {
if ($this->isNullType($value)) {
return self::C_NUL;
}
if (isset($this->charValue[$value])) { if (isset($this->charValue[$value])) {
return $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::INVALID;
} }
return self::GENERIC; 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); $this->domainPartParser = new DomainPart($this->lexer);
} }
/**
* @param string $str
*/
public function parse($str) public function parse($str)
{ {
$this->lexer->setInput($str); $this->lexer->setInput($str);
@ -36,6 +39,10 @@ class EmailParser
throw new \InvalidArgumentException('ERR_NOLOCALPART'); throw new \InvalidArgumentException('ERR_NOLOCALPART');
} }
if ($this->lexer->hasInvalidTokens()) {
throw new \InvalidArgumentException('ERR_INVALID_ATEXT');
}
$this->localPartParser->parse($str); $this->localPartParser->parse($str);
$this->domainPartParser->parse($str); $this->domainPartParser->parse($str);
@ -78,6 +85,10 @@ class EmailParser
return true; return true;
} }
/**
* @param string $localPart
* @param string $parsedDomainPart
*/
protected function addLongEmailWarning($localPart, $parsedDomainPart) protected function addLongEmailWarning($localPart, $parsedDomainPart)
{ {
if (strlen($localPart . '@' . $parsedDomainPart) > self::EMAIL_MAX_LENGTH) { if (strlen($localPart . '@' . $parsedDomainPart) > self::EMAIL_MAX_LENGTH) {

View File

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

View File

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

View File

@ -4,6 +4,7 @@ namespace Egulias\EmailValidator\Parser;
use Egulias\EmailValidator\EmailLexer; use Egulias\EmailValidator\EmailLexer;
use Egulias\EmailValidator\EmailValidator; use Egulias\EmailValidator\EmailValidator;
use \InvalidArgumentException;
class LocalPart extends Parser class LocalPart extends Parser
@ -21,8 +22,7 @@ class LocalPart extends Parser
$closingQuote = $this->checkDQUOTE($closingQuote); $closingQuote = $this->checkDQUOTE($closingQuote);
if ($closingQuote && $parseDQuote) { if ($closingQuote && $parseDQuote) {
$this->parseDoubleQuote(); $parseDQuote = $this->parseDoubleQuote();
$parseDQuote = false;
} }
if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) { if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) {
@ -56,23 +56,51 @@ class LocalPart extends Parser
protected function parseDoubleQuote() protected function parseDoubleQuote()
{ {
$parseAgain = true;
$special = array( $special = array(
EmailLexer::S_CR => true, EmailLexer::S_CR => true,
EmailLexer::S_HTAB => true, EmailLexer::S_HTAB => true,
EmailLexer::S_LF => 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; $setSpecialsWarning = true;
$this->lexer->moveNext(); $this->lexer->moveNext();
while ($this->lexer->token['type'] !== EmailLexer::S_DQUOTE && $this->lexer->token) { while ($this->lexer->token['type'] !== EmailLexer::S_DQUOTE && $this->lexer->token) {
$parseAgain = false;
if (isset($special[$this->lexer->token['type']]) && $setSpecialsWarning) { if (isset($special[$this->lexer->token['type']]) && $setSpecialsWarning) {
$this->warnings[] = EmailValidator::CFWS_FWS; $this->warnings[] = EmailValidator::CFWS_FWS;
$setSpecialsWarning = false; $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) protected function isInvalidToken($token, $closingQuote)
{ {

View File

@ -124,8 +124,7 @@ abstract class Parser
if ($previous['type'] === EmailLexer::S_BACKSLASH if ($previous['type'] === EmailLexer::S_BACKSLASH
&& &&
($this->lexer->token['type'] === EmailLexer::S_SP || $this->lexer->token['type'] !== EmailLexer::GENERIC
$this->lexer->token['type'] === EmailLexer::S_HTAB)
) { ) {
return true; return true;
} }
@ -164,6 +163,7 @@ abstract class Parser
if ($this->lexer->isNextToken(EmailLexer::GENERIC) && $previous['type'] === EmailLexer::GENERIC) { if ($this->lexer->isNextToken(EmailLexer::GENERIC) && $previous['type'] === EmailLexer::GENERIC) {
throw new \InvalidArgumentException('ERR_EXPECTING_ATEXT'); throw new \InvalidArgumentException('ERR_EXPECTING_ATEXT');
} }
$this->warnings[] = EmailValidator::RFC5321_QUOTEDSTRING; $this->warnings[] = EmailValidator::RFC5321_QUOTEDSTRING;
try { try {
$this->lexer->find(EmailLexer::S_DQUOTE); $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)); $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() public function getTokens()
{ {
return array( return array(
@ -61,6 +70,7 @@ class EmailLexerTests extends \PHPUnit_Framework_TestCase
array("\"", EmailLexer::S_DQUOTE), array("\"", EmailLexer::S_DQUOTE),
array("-", EmailLexer::S_HYPHEN), array("-", EmailLexer::S_HYPHEN),
array("\\", EmailLexer::S_BACKSLASH), array("\\", EmailLexer::S_BACKSLASH),
array("/", EmailLexer::S_SLASH),
array("(", EmailLexer::S_OPENPARENTHESIS), array("(", EmailLexer::S_OPENPARENTHESIS),
array(")", EmailLexer::S_CLOSEPARENTHESIS), array(")", EmailLexer::S_CLOSEPARENTHESIS),
array('<', EmailLexer::S_LOWERTHAN), array('<', EmailLexer::S_LOWERTHAN),
@ -74,7 +84,9 @@ class EmailLexerTests extends \PHPUnit_Framework_TestCase
array('{', EmailLexer::S_OPENQBRACKET), array('{', EmailLexer::S_OPENQBRACKET),
array('}', EmailLexer::S_CLOSEQBRACKET), array('}', EmailLexer::S_CLOSEQBRACKET),
array('', EmailLexer::S_EMPTY), 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('"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() public function getInvalidEmails()
{ {
return array( return array(
array('example.@example.co.uk'), array('example.@example.co.uk'),
array('example@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(example)example@example.co.uk'),
array('.example@localhost'), array('.example@localhost'),
array('ex\ample@localhost'), array('ex\ample@localhost'),
@ -72,6 +77,27 @@ class EmailValidatorTest extends \PHPUnit_Framework_TestCase
array('username@example,com'), array('username@example,com'),
array('usern,ame@example.com'), array('usern,ame@example.com'),
array('user[na]me@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()); $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() public function getInvalidEmailsWithWarnings()
{ {
return array( return array(
@ -275,6 +311,17 @@ class EmailValidatorTest extends \PHPUnit_Framework_TestCase
'parttoolonglocalparttoolonglocalparttoolonglocalparttoolonglocalparttoolonglocalparttoolonglocalpart'. 'parttoolonglocalparttoolonglocalparttoolonglocalparttoolonglocalparttoolonglocalparttoolonglocalpart'.
'toolonglocalparttoolonglocalparttoolonglocalparttoolonglocalpar' '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 RingPHP
======= =======
Provides low level APIs used to power HTTP clients and servers through a Provides a simple API and specification that abstracts away the details of HTTP
simple, PHP ``callable`` that accepts a request hash and returns a future into a single PHP function. RingPHP be used to power HTTP clients and servers
response hash. RingPHP supports both synchronous and asynchronous through a PHP function that accepts a request hash and returns a response hash
workflows by utilizing both futures and `promises <https://github.com/reactphp/promise>`_. 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>`_, 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 which, in turn, was inspired by Python's WSGI and Ruby's Rack. RingPHP is
and non-blocking requests. 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 RingPHP
======= =======
Provides low level APIs used to power HTTP clients and servers through a Provides a simple API and specification that abstracts away the details of HTTP
simple, PHP ``callable`` that accepts a request hash and returns a future into a single PHP function. RingPHP be used to power HTTP clients and servers
response hash. RingPHP supports both synchronous and asynchronous through a PHP function that accepts a request hash and returns a response hash
workflows by utilizing both futures and `promises <https://github.com/reactphp/promise>`_. 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>`_, By abstracting the implementation details of different HTTP clients and
but has been modified to accommodate clients and servers for both blocking servers, RingPHP allows you to utilize pluggable HTTP clients and servers
and non-blocking requests. without tying your application to a specific implementation.
RingPHP is utilized as the handler layer in
`Guzzle <http://guzzlephp.org>`_ 5.0+ to send HTTP requests.
.. toctree:: .. toctree::
:maxdepth: 1 :maxdepth: 2
spec spec
futures futures
client_middleware client_middleware
client_handlers client_handlers
testing 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
-------- --------
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``). and return a response array (``GuzzleHttp\Ring\Future\FutureArrayInterface``).
For example: For example:

View File

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

View File

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

View File

@ -41,24 +41,17 @@ class Core
*/ */
public static function headerLines($message, $header) public static function headerLines($message, $header)
{ {
// Slight optimization for exact matches. $result = [];
if (isset($message['headers'][$header])) {
return $message['headers'][$header];
}
// Check for message with no headers after the "fast" isset check. if (!empty($message['headers'])) {
if (!isset($message['headers'])) {
return [];
}
// Iterate and case-insensitively find the header by name.
foreach ($message['headers'] as $name => $value) { foreach ($message['headers'] as $name => $value) {
if (!strcasecmp($name, $header)) { if (!strcasecmp($name, $header)) {
return $value; $result = array_merge($result, $value);
}
} }
} }
return []; return $result;
} }
/** /**
@ -91,18 +84,17 @@ class Core
*/ */
public static function firstHeader($message, $header) public static function firstHeader($message, $header)
{ {
$match = self::headerLines($message, $header); if (!empty($message['headers'])) {
foreach ($message['headers'] as $name => $value) {
if (!isset($match[0])) { if (!strcasecmp($name, $header)) {
return null;
}
// Return the match itself if it is a single value. // Return the match itself if it is a single value.
if (!($pos = strpos($match[0], ','))) { $pos = strpos($value[0], ',');
return $match[0]; return $pos ? substr($value[0], 0, $pos) : $value[0];
}
}
} }
return substr($match[0], 0, $pos); return null;
} }
/** /**
@ -115,7 +107,15 @@ class Core
*/ */
public static function hasHeader($message, $header) 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; private $isRealized = false;
/** /**
* @param PromiseInterface $promise Promise to shadow with the future. Only * @param PromiseInterface $promise Promise to shadow with the future.
* supply if the promise is not owned
* by the deferred value.
* @param callable $wait Function that blocks until the deferred * @param callable $wait Function that blocks until the deferred
* computation has been resolved. This * computation has been resolved. This
* function MUST resolve the deferred value * function MUST resolve the deferred value

View File

@ -404,6 +404,19 @@ class CurlFactoryTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($content, Core::body($response)); $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 * @expectedException \InvalidArgumentException
*/ */
@ -570,7 +583,7 @@ class CurlFactoryTest extends \PHPUnit_Framework_TestCase
'http_method' => 'GET', 'http_method' => 'GET',
'headers' => [ 'headers' => [
'host' => [Server::$host], 'host' => [Server::$host],
'content-length' => 3, 'content-length' => [3],
], ],
'body' => 'foo', 'body' => 'foo',
]); ]);
@ -709,6 +722,71 @@ class CurlFactoryTest extends \PHPUnit_Framework_TestCase
); );
$this->assertInstanceOf('GuzzleHttp\Ring\Exception\ConnectException', $response['error']); $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)); $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) protected function getSendResult(array $opts)
{ {
$this->queueRes(); $this->queueRes();

View File

@ -15,6 +15,22 @@ class CoreTest extends \PHPUnit_Framework_TestCase
$this->assertNull(Core::firstHeader([], 'Foo')); $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() public function testReturnsFirstHeaderWhenSimple()
{ {
$this->assertEquals('Bar', Core::firstHeader([ $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() public function testExtractsHeaderLines()
{ {
$this->assertEquals( $this->assertEquals(

View File

@ -23,7 +23,7 @@
}, },
"extra": { "extra": {
"branch-alias": { "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); 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() 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

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

View File

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

View File

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

View File

@ -28,7 +28,7 @@
"target-dir": "Symfony/Component/ClassLoader", "target-dir": "Symfony/Component/ClassLoader",
"extra": { "extra": {
"branch-alias": { "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; 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

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

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

View File

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

View File

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

View File

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

View File

@ -78,7 +78,8 @@ class DebugClassLoader
public static function enable() public static function enable()
{ {
// Ensures we don't hit https://bugs.php.net/42098 // 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())) { if (!is_array($functions = spl_autoload_functions())) {
return; return;

View File

@ -22,141 +22,353 @@ use Symfony\Component\Debug\FatalErrorHandler\ClassNotFoundFatalErrorHandler;
use Symfony\Component\Debug\FatalErrorHandler\FatalErrorHandlerInterface; 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> * @author Nicolas Grekas <p@tchwork.com>
*/ */
class ErrorHandler class ErrorHandler
{ {
/**
* @deprecated since 2.6, to be removed in 3.0.
*/
const TYPE_DEPRECATION = -100; const TYPE_DEPRECATION = -100;
private $levels = array( 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_RECOVERABLE_ERROR => 'Catchable Fatal Error',
E_DEPRECATED => 'Deprecated', E_DEPRECATED => 'Deprecated',
E_USER_DEPRECATED => 'User Deprecated', E_USER_DEPRECATED => 'User Deprecated',
E_ERROR => 'Error', E_NOTICE => 'Notice',
E_CORE_ERROR => 'Core Error', 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_COMPILE_ERROR => 'Compile Error', E_COMPILE_ERROR => 'Compile Error',
E_PARSE => 'Parse 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 $displayErrors = 0x1FFF;
private static $stackedErrors = array();
private static $stackedErrorLevels = array();
/** /**
* Registers the error handler. * 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 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 $displayErrors Display errors (for dev environment) or just log them (production usage) * @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(); if (null === self::$reservedMemory) {
$handler->setLevel($level); self::$reservedMemory = str_repeat('x', 10240);
$handler->setDisplayErrors($displayErrors); register_shutdown_function(__CLASS__.'::handleFatalError');
}
ini_set('display_errors', 0); $levels = -1;
set_error_handler(array($handler, 'handle'));
register_shutdown_function(array($handler, 'handleFatal')); if ($handlerIsNew = !$handler instanceof self) {
$handler->reservedMemory = str_repeat('x', 10240); // @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; 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)) {
* Sets the display_errors flag value. foreach ($levels as $type => $logLevel) {
* if (empty($this->loggers[$type][0]) || $replace) {
* @param int $displayErrors The display_errors flag value $loggers[$type] = array($logger, $logLevel);
*/
public function setDisplayErrors($displayErrors)
{
$this->displayErrors = $displayErrors;
} }
/**
* 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)
*/
public static function setLogger(LoggerInterface $logger, $channel = 'deprecation')
{
self::$loggers[$channel] = $logger;
} }
/**
* @throws \ErrorException When error_reporting returns error
*/
public function handle($level, $message, $file = 'unknown', $line = 0, $context = array())
{
if ($level & (E_USER_DEPRECATED | E_DEPRECATED)) {
if (isset(self::$loggers['deprecation'])) {
if (self::$stackedErrorLevels) {
self::$stackedErrors[] = func_get_args();
} else { } else {
if (version_compare(PHP_VERSION, '5.4', '<')) { if (null === $levels) {
$stack = array_map( $levels = E_ALL | E_STRICT;
function ($row) { }
unset($row['args']); foreach ($this->loggers as $type => $log) {
if (($type & $levels) && (empty($log[0]) || $replace)) {
$log[0] = $logger;
$loggers[$type] = $log;
}
}
}
return $row; $this->setLoggers($loggers);
}, }
array_slice(debug_backtrace(false), 0, 10)
); /**
* Sets a logger for each error level.
*
* @param array $loggers Error levels to [LoggerInterface|null, LogLevel::*] map
*
* @return array The previous map
*
* @throws \InvalidArgumentException
*/
public function setLoggers(array $loggers)
{
$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 { } else {
$stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 10); throw new \InvalidArgumentException('Invalid logger provided');
}
$this->loggers[$type] = $log + $prev[$type];
}
$this->reRegister($prevLogged | $this->thrownErrors);
return $prev;
} }
self::$loggers['deprecation']->warning($message, array('type' => self::TYPE_DEPRECATION, 'stack' => $stack)); /**
* Sets a user exception handler.
*
* @param callable $handler A handler that will be called on Exception
*
* @return callable|null The previous exception handler
*
* @throws \InvalidArgumentException
*/
public function setExceptionHandler($handler)
{
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;
} }
return true; /**
* 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 throwAt($levels, $replace = false)
{
$prev = $this->thrownErrors;
$this->thrownErrors = ($levels | E_RECOVERABLE_ERROR | E_USER_ERROR) & ~E_USER_DEPRECATED & ~E_DEPRECATED;
if (!$replace) {
$this->thrownErrors |= $prev;
} }
} elseif ($this->displayErrors && error_reporting() & $level && $this->level & $level) { $this->reRegister($prev | $this->loggedErrors);
if (PHP_VERSION_ID < 50400 && isset($context['GLOBALS']) && is_array($context)) {
$c = $context; // Whatever the signature of the method, // $this->displayErrors is @deprecated since 2.6
unset($c['GLOBALS'], $context); // $context is always a reference in 5.3 $this->displayErrors = $this->thrownErrors;
$context = $c;
return $prev;
} }
$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')) { * 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 // Checking for class existence is a work around for https://bugs.php.net/42098
$exception = new ContextErrorException($exception, 0, $level, $file, $line, $context); $throw = new ContextErrorException($this->levels[$type].': '.$message, 0, $type, $file, $line, $context);
} else { } else {
$exception = new \ErrorException($exception, 0, $level, $file, $line); $throw = new \ErrorException($this->levels[$type].': '.$message, 0, $type, $file, $line);
} }
if (PHP_VERSION_ID <= 50407 && (PHP_VERSION_ID >= 50400 || PHP_VERSION_ID <= 50317)) { if (PHP_VERSION_ID <= 50407 && (PHP_VERSION_ID >= 50400 || PHP_VERSION_ID <= 50317)) {
@ -164,46 +376,157 @@ class ErrorHandler
// handler and shutdown handlers are bypassed before 5.4.8/5.3.18. // 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. // We temporarily re-enable display_errors to prevent any blank page related to this bug.
$exception->errorHandlerCanary = new ErrorHandlerCanary(); $throw->errorHandlerCanary = new ErrorHandlerCanary();
} }
throw $exception; throw $throw;
} }
if (isset(self::$loggers['scream']) && !(error_reporting() & $level)) { // For duplicated errors, log the trace only once
if (self::$stackedErrorLevels) { $e = md5("{$type}/{$line}/{$file}\x00{$message}", true);
self::$stackedErrors[] = func_get_args(); $trace = true;
if (!($this->tracedErrors & $type) || isset($this->loggedTraces[$e])) {
$trace = false;
} else { } else {
switch ($level) { $this->loggedTraces[$e] = 1;
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;
} }
self::$loggers['scream']->log($logLevel, $message, array( $e = compact('type', 'file', 'line', 'level');
'type' => $level,
'file' => $file, if ($type & $level) {
'line' => $line, if ($this->scopedErrors & $type) {
'scream' => error_reporting(), $e['context'] = $context;
)); if ($trace) {
$e['stack'] = debug_backtrace(true); // Provide object
}
} elseif ($trace) {
$e['stack'] = debug_backtrace(PHP_VERSION_ID >= 50306 ? DEBUG_BACKTRACE_IGNORE_ARGS : false);
} }
} }
return false; if ($this->isRecursive) {
$log = 0;
} elseif (self::$stackedErrorLevels) {
self::$stackedErrors[] = array($this->loggers[$type], $message, $e);
} else {
try {
$this->isRecursive = true;
$this->loggers[$type][0]->log($this->loggers[$type][1], $message, $e);
$this->isRecursive = false;
} catch (\Exception $e) {
$this->isRecursive = false;
throw $e;
}
}
}
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. * Ensures also that non-catchable fatal errors are never silenced.
* *
* As shown by http://bugs.php.net/42098 and http://bugs.php.net/60724 * 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() public static function unstackErrors()
{ {
@ -237,63 +560,11 @@ class ErrorHandler
$errors = self::$stackedErrors; $errors = self::$stackedErrors;
self::$stackedErrors = array(); self::$stackedErrors = array();
$errorHandler = set_error_handler('var_dump');
restore_error_handler();
if ($errorHandler) {
foreach ($errors as $e) { foreach ($errors as $e) {
call_user_func_array($errorHandler, $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. * 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 $level = null === $level ? error_reporting() : $level;
set_error_handler('var_dump', 0); $this->throwAt($level, true);
ini_set('display_errors', 1); }
$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']); * Sets the display_errors flag value.
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); * @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 { } else {
$exception = new FatalErrorException($message, 0, $error['type'], $error['file'], $error['line'], 3, true); $displayErrors = $this->displayErrors;
$this->throwAt(0, true);
foreach ($this->getFatalErrorHandlers() as $handler) { $this->displayErrors = $displayErrors;
if ($e = $handler->handleError($error, $exception)) {
$exception = $e;
break;
}
} }
} }
try { /**
call_user_func($exceptionHandler, $exception); * Sets a logger for the given channel.
} catch (\Exception $e) { *
// The handler failed. Let PHP handle that now. * @param LoggerInterface $logger A logger interface
throw $exception; * @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

@ -34,15 +34,15 @@ if (!defined('ENT_SUBSTITUTE')) {
class ExceptionHandler class ExceptionHandler
{ {
private $debug; private $debug;
private $charset;
private $handler; private $handler;
private $caughtBuffer; private $caughtBuffer;
private $caughtLength; private $caughtLength;
private $fileLinkFormat;
public function __construct($debug = true, $charset = 'UTF-8') public function __construct($debug = true, $fileLinkFormat = null)
{ {
$this->debug = $debug; $this->debug = $debug;
$this->charset = $charset; $this->fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format');
} }
/** /**
@ -52,11 +52,15 @@ class ExceptionHandler
* *
* @return ExceptionHandler The registered exception handler * @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; return $handler;
} }
@ -79,6 +83,21 @@ class ExceptionHandler
return $old; 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. * Sends a response for the given Exception.
* *
@ -205,29 +224,26 @@ class ExceptionHandler
$total = $count + 1; $total = $count + 1;
foreach ($exception->toArray() as $position => $e) { foreach ($exception->toArray() as $position => $e) {
$ind = $count - $position + 1; $ind = $count - $position + 1;
$class = $this->abbrClass($e['class']); $class = $this->formatClass($e['class']);
$message = nl2br($e['message']); $message = nl2br(self::utf8Htmlize($e['message']));
$content .= sprintf(<<<EOF $content .= sprintf(<<<EOF
<div class="block_exception clear_fix"> <h2 class="block_exception clear_fix">
<h2><span>%d/%d</span> %s: %s</h2> <span class="exception_counter">%d/%d</span>
</div> <span class="exception_title">%s%s:</span>
<span class="exception_message">%s</span>
</h2>
<div class="block"> <div class="block">
<ol class="traces list_exception"> <ol class="traces list_exception">
EOF EOF
, $ind, $total, $class, $message); , $ind, $total, $class, $this->formatPath($e['trace'][0]['file'], $e['trace'][0]['line']), $message);
foreach ($e['trace'] as $trace) { foreach ($e['trace'] as $trace) {
$content .= ' <li>'; $content .= ' <li>';
if ($trace['function']) { 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 (isset($trace['file']) && isset($trace['line'])) {
if ($linkFormat = ini_get('xdebug.file_link_format')) { $content .= $this->formatPath($trace['file'], $trace['line']);
$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 .= "</li>\n"; $content .= "</li>\n";
} }
@ -272,12 +288,14 @@ EOF;
.sf-reset abbr { border-bottom: 1px dotted #000; cursor: help; } .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 p { font-size:14px; line-height:20px; color:#868686; padding-bottom:20px }
.sf-reset strong { font-weight:bold; } .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 img { border:none; }
.sf-reset a:hover { text-decoration:underline; } .sf-reset a:hover { text-decoration:underline; }
.sf-reset em { font-style:italic; } .sf-reset em { font-style:italic; }
.sf-reset h1, .sf-reset h2 { font: 20px Georgia, "Times New Roman", Times, serif } .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 .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; .sf-reset .block { background-color:#FFFFFF; padding:10px 28px; margin-bottom:20px;
-webkit-border-bottom-right-radius: 16px; -webkit-border-bottom-right-radius: 16px;
@ -303,8 +321,8 @@ EOF;
overflow: hidden; overflow: hidden;
word-wrap: break-word; word-wrap: break-word;
} }
.sf-reset li a { background:none; color:#868686; text-decoration:none; } .sf-reset a { background:none; color:#868686; text-decoration:none; }
.sf-reset li a:hover { background:none; color:#313131; text-decoration:underline; } .sf-reset a:hover { background:none; color:#313131; text-decoration:underline; }
.sf-reset ol { padding: 10px 0; } .sf-reset ol { padding: 10px 0; }
.sf-reset h1 { background-color:#FFFFFF; padding: 15px 28px; margin-bottom: 20px; .sf-reset h1 { background-color:#FFFFFF; padding: 15px 28px; margin-bottom: 20px;
-webkit-border-radius: 10px; -webkit-border-radius: 10px;
@ -321,7 +339,7 @@ EOF;
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta charset="UTF-8" />
<meta name="robots" content="noindex,nofollow" /> <meta name="robots" content="noindex,nofollow" />
<style> <style>
/* Copyright (c) 2010, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.com/yui/license.html */ /* 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; EOF;
} }
private function abbrClass($class) private function formatClass($class)
{ {
$parts = explode('\\', $class); $parts = explode('\\', $class);
return sprintf("<abbr title=\"%s\">%s</abbr>", $class, array_pop($parts)); 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. * Formats an array as a string.
* *
@ -359,11 +391,11 @@ EOF;
$result = array(); $result = array();
foreach ($args as $key => $item) { foreach ($args as $key => $item) {
if ('object' === $item[0]) { 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]) { } elseif ('array' === $item[0]) {
$formattedValue = sprintf("<em>array</em>(%s)", is_array($item[1]) ? $this->formatArgs($item[1]) : $item[1]); $formattedValue = sprintf("<em>array</em>(%s)", is_array($item[1]) ? $this->formatArgs($item[1]) : $item[1]);
} elseif ('string' === $item[0]) { } elseif ('string' === $item[0]) {
$formattedValue = sprintf("'%s'", htmlspecialchars($item[1], ENT_QUOTES | ENT_SUBSTITUTE, $this->charset)); $formattedValue = sprintf("'%s'", self::utf8Htmlize($item[1]));
} elseif ('null' === $item[0]) { } elseif ('null' === $item[0]) {
$formattedValue = '<em>null</em>'; $formattedValue = '<em>null</em>';
} elseif ('boolean' === $item[0]) { } elseif ('boolean' === $item[0]) {
@ -371,7 +403,7 @@ EOF;
} elseif ('resource' === $item[0]) { } elseif ('resource' === $item[0]) {
$formattedValue = '<em>resource</em>'; $formattedValue = '<em>resource</em>';
} else { } 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); $result[] = is_int($key) ? $formattedValue : sprintf("'%s' => %s", $key, $formattedValue);
@ -380,6 +412,25 @@ EOF;
return implode(', ', $result); 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 * @internal
*/ */

View File

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

View File

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

View File

@ -34,7 +34,7 @@ class UndefinedMethodFatalErrorHandler implements FatalErrorHandlerInterface
$className = $matches[1]; $className = $matches[1];
$methodName = $matches[2]; $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(); $candidates = array();
foreach (get_class_methods($className) as $definedMethodName) { foreach (get_class_methods($className) as $definedMethodName) {
@ -46,7 +46,13 @@ class UndefinedMethodFatalErrorHandler implements FatalErrorHandlerInterface
if ($candidates) { if ($candidates) {
sort($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); 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 Enabling all debug tools is as easy as calling the `enable()` method on the
main `Debug` class: main `Debug` class:
```php
use Symfony\Component\Debug\Debug; use Symfony\Component\Debug\Debug;
Debug::enable(); Debug::enable();
```
You can also use the tools individually: You can also use the tools individually:
```php
use Symfony\Component\Debug\ErrorHandler; use Symfony\Component\Debug\ErrorHandler;
use Symfony\Component\Debug\ExceptionHandler; use Symfony\Component\Debug\ExceptionHandler;
error_reporting(-1);
ErrorHandler::register($errorReportingLevel);
if ('cli' !== php_sapi_name()) { if ('cli' !== php_sapi_name()) {
ini_set('display_errors', 0);
ExceptionHandler::register(); ExceptionHandler::register();
} elseif (!ini_get('log_errors') || ini_get('error_log')) { } elseif (!ini_get('log_errors') || ini_get('error_log')) {
ini_set('display_errors', 1); ini_set('display_errors', 1);
} }
ErrorHandler::register($errorReportingLevel);
```
Note that the `Debug::enable()` call also registers the debug class loader Note that the `Debug::enable()` call also registers the debug class loader
from the Symfony ClassLoader component when available. 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->assertEquals(E_STRICT, $exception->getSeverity());
$this->assertStringStartsWith(__FILE__, $exception->getFile()); $this->assertStringStartsWith(__FILE__, $exception->getFile());
$this->assertRegexp('/^Runtime Notice: Declaration/', $exception->getMessage()); $this->assertRegexp('/^Runtime Notice: Declaration/', $exception->getMessage());
} catch (\Exception $e) { } catch (\Exception $exception) {
restore_error_handler(); restore_error_handler();
restore_exception_handler(); restore_exception_handler();
throw $e; throw $exception;
} }
} }

View File

@ -11,6 +11,7 @@
namespace Symfony\Component\Debug\Tests; namespace Symfony\Component\Debug\Tests;
use Psr\Log\LogLevel;
use Symfony\Component\Debug\ErrorHandler; use Symfony\Component\Debug\ErrorHandler;
use Symfony\Component\Debug\Exception\ContextErrorException; use Symfony\Component\Debug\Exception\ContextErrorException;
@ -18,6 +19,7 @@ use Symfony\Component\Debug\Exception\ContextErrorException;
* ErrorHandlerTest * ErrorHandlerTest
* *
* @author Robert Schönthal <seroscho@googlemail.com> * @author Robert Schönthal <seroscho@googlemail.com>
* @author Nicolas Grekas <p@tchwork.com>
*/ */
class ErrorHandlerTest extends \PHPUnit_Framework_TestCase class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
{ {
@ -44,6 +46,46 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
error_reporting($this->errorReporting); 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() public function testNotice()
{ {
ErrorHandler::register(); ErrorHandler::register();
@ -54,6 +96,8 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
} catch (ContextErrorException $exception) { } catch (ContextErrorException $exception) {
// if an exception is thrown, the test passed // if an exception is thrown, the test passed
restore_error_handler(); restore_error_handler();
restore_exception_handler();
$this->assertEquals(E_NOTICE, $exception->getSeverity()); $this->assertEquals(E_NOTICE, $exception->getSeverity());
$this->assertEquals(__FILE__, $exception->getFile()); $this->assertEquals(__FILE__, $exception->getFile());
$this->assertRegexp('/^Notice: Undefined variable: (foo|bar)/', $exception->getMessage()); $this->assertRegexp('/^Notice: Undefined variable: (foo|bar)/', $exception->getMessage());
@ -62,7 +106,7 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
$trace = $exception->getTrace(); $trace = $exception->getTrace();
$this->assertEquals(__FILE__, $trace[0]['file']); $this->assertEquals(__FILE__, $trace[0]['file']);
$this->assertEquals('Symfony\Component\Debug\ErrorHandler', $trace[0]['class']); $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('->', $trace[0]['type']);
$this->assertEquals(__FILE__, $trace[1]['file']); $this->assertEquals(__FILE__, $trace[1]['file']);
@ -70,16 +114,16 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('triggerNotice', $trace[1]['function']); $this->assertEquals('triggerNotice', $trace[1]['function']);
$this->assertEquals('::', $trace[1]['type']); $this->assertEquals('::', $trace[1]['type']);
$this->assertEquals(__FILE__, $trace[1]['file']);
$this->assertEquals(__CLASS__, $trace[2]['class']); $this->assertEquals(__CLASS__, $trace[2]['class']);
$this->assertEquals('testNotice', $trace[2]['function']); $this->assertEquals(__FUNCTION__, $trace[2]['function']);
$this->assertEquals('->', $trace[2]['type']); $this->assertEquals('->', $trace[2]['type']);
} catch (\Exception $e) { } catch (\Exception $e) {
restore_error_handler(); restore_error_handler();
restore_exception_handler();
throw $e; throw $e;
} }
restore_error_handler();
} }
// dummy function to test trace in error handler. // dummy function to test trace in error handler.
@ -93,78 +137,257 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
public function testConstruct() public function testConstruct()
{ {
try { try {
$handler = ErrorHandler::register(3); $handler = ErrorHandler::register();
$handler->throwAt(3, true);
$level = new \ReflectionProperty($handler, 'level'); $this->assertEquals(3 | E_RECOVERABLE_ERROR | E_USER_ERROR, $handler->throwAt(0));
$level->setAccessible(true);
$this->assertEquals(3, $level->getValue($handler));
restore_error_handler(); restore_error_handler();
restore_exception_handler();
} catch (\Exception $e) { } catch (\Exception $e) {
restore_error_handler(); restore_error_handler();
restore_exception_handler();
throw $e; throw $e;
} }
} }
public function testHandle() public function testDefaultLogger()
{ {
try { try {
$handler = ErrorHandler::register(0); $handler = ErrorHandler::register();
$this->assertFalse($handler->handle(0, 'foo', 'foo.php', 12, array()));
$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_error_handler();
restore_exception_handler();
$handler = ErrorHandler::register(3); } catch (\Exception $e) {
$this->assertFalse($handler->handle(4, 'foo', 'foo.php', 12, array()));
restore_error_handler(); restore_error_handler();
restore_exception_handler();
$handler = ErrorHandler::register(3); throw $e;
}
}
public function testHandleError()
{
try { try {
$handler->handle(111, 'foo', 'foo.php', 12, array()); $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();
$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->handleError(4, 'foo', 'foo.php', 12, array());
} catch (\ErrorException $e) { } catch (\ErrorException $e) {
$this->assertSame('111: foo in foo.php line 12', $e->getMessage()); $this->assertSame('Parse Error: foo', $e->getMessage());
$this->assertSame(111, $e->getSeverity()); $this->assertSame(4, $e->getSeverity());
$this->assertSame('foo.php', $e->getFile()); $this->assertSame('foo.php', $e->getFile());
$this->assertSame(12, $e->getLine()); $this->assertSame(12, $e->getLine());
} }
restore_error_handler(); restore_error_handler();
restore_exception_handler();
$handler = ErrorHandler::register(E_USER_DEPRECATED); $handler = ErrorHandler::register();
$this->assertFalse($handler->handle(E_USER_DEPRECATED, 'foo', 'foo.php', 12, array())); $handler->throwAt(E_USER_DEPRECATED, true);
$this->assertFalse($handler->handleError(E_USER_DEPRECATED, 'foo', 'foo.php', 12, array()));
restore_error_handler(); restore_error_handler();
restore_exception_handler();
$handler = ErrorHandler::register(E_DEPRECATED); $handler = ErrorHandler::register();
$this->assertFalse($handler->handle(E_DEPRECATED, 'foo', 'foo.php', 12, array())); $handler->throwAt(E_DEPRECATED, true);
$this->assertFalse($handler->handleError(E_DEPRECATED, 'foo', 'foo.php', 12, array()));
restore_error_handler(); restore_error_handler();
restore_exception_handler();
$logger = $this->getMock('Psr\Log\LoggerInterface'); $logger = $this->getMock('Psr\Log\LoggerInterface');
$that = $this; $that = $this;
$warnArgCheck = function ($message, $context) use ($that) { $warnArgCheck = function ($logLevel, $message, $context) use ($that) {
$that->assertEquals('info', $logLevel);
$that->assertEquals('foo', $message); $that->assertEquals('foo', $message);
$that->assertArrayHasKey('type', $context); $that->assertArrayHasKey('type', $context);
$that->assertEquals($context['type'], ErrorHandler::TYPE_DEPRECATION); $that->assertEquals($context['type'], E_USER_DEPRECATED);
$that->assertArrayHasKey('stack', $context); $that->assertArrayHasKey('stack', $context);
$that->assertInternalType('array', $context['stack']); $that->assertInternalType('array', $context['stack']);
}; };
$logger $logger
->expects($this->once()) ->expects($this->once())
->method('warning') ->method('log')
->will($this->returnCallback($warnArgCheck)) ->will($this->returnCallback($warnArgCheck))
; ;
$handler = ErrorHandler::register(E_USER_DEPRECATED); $handler = ErrorHandler::register();
$handler->setLogger($logger); $handler->setDefaultLogger($logger, E_USER_DEPRECATED);
$this->assertTrue($handler->handle(E_USER_DEPRECATED, 'foo', 'foo.php', 12, array())); $this->assertTrue($handler->handleError(E_USER_DEPRECATED, 'foo', 'foo.php', 12, array()));
restore_error_handler(); 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'); $logger = $this->getMock('Psr\Log\LoggerInterface');
@ -187,63 +410,12 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
@$undefVar++; @$undefVar++;
restore_error_handler(); restore_error_handler();
restore_exception_handler();
} catch (\Exception $e) { } catch (\Exception $e) {
restore_error_handler(); restore_error_handler();
restore_exception_handler();
throw $e; 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

@ -12,6 +12,8 @@
namespace Symfony\Component\Debug\Tests; namespace Symfony\Component\Debug\Tests;
use Symfony\Component\Debug\ExceptionHandler; 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\NotFoundHttpException;
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
@ -23,13 +25,13 @@ class ExceptionHandlerTest extends \PHPUnit_Framework_TestCase
$response = $handler->createResponse(new \RuntimeException('Foo')); $response = $handler->createResponse(new \RuntimeException('Foo'));
$this->assertContains('<h1>Whoops, looks like something went wrong.</h1>', $response->getContent()); $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); $handler = new ExceptionHandler(true);
$response = $handler->createResponse(new \RuntimeException('Foo')); $response = $handler->createResponse(new \RuntimeException('Foo'));
$this->assertContains('<h1>Whoops, looks like something went wrong.</h1>', $response->getContent()); $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() public function testStatusCode()
@ -59,4 +61,56 @@ class ExceptionHandlerTest extends \PHPUnit_Framework_TestCase
$handler = new ExceptionHandler(true); $handler = new ExceptionHandler(true);
$response = $handler->createResponse(new \RuntimeException('Foo', 0, new \RuntimeException('Bar'))); $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', 'file' => 'foo.php',
'message' => 'Class \'WhizBangFactory\' not found', '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(
array( array(
@ -50,7 +50,7 @@ class ClassNotFoundFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
'file' => 'foo.php', 'file' => 'foo.php',
'message' => 'Class \'Foo\\Bar\\WhizBangFactory\' not found', '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(
array( array(
@ -59,7 +59,7 @@ class ClassNotFoundFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
'file' => 'foo.php', 'file' => 'foo.php',
'message' => 'Class \'UndefinedFunctionException\' not found', '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(
array( array(
@ -68,7 +68,7 @@ class ClassNotFoundFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
'file' => 'foo.php', 'file' => 'foo.php',
'message' => 'Class \'PEARClass\' not found', '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(
array( array(
@ -77,7 +77,7 @@ class ClassNotFoundFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
'file' => 'foo.php', 'file' => 'foo.php',
'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found', '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', 'file' => 'foo.php',
'message' => 'Call to undefined function test_namespaced_function()', '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(
array( array(
@ -51,7 +51,7 @@ class UndefinedFunctionFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
'file' => 'foo.php', 'file' => 'foo.php',
'message' => 'Call to undefined function Foo\\Bar\\Baz\\test_namespaced_function()', '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(
array( array(
@ -60,7 +60,7 @@ class UndefinedFunctionFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
'file' => 'foo.php', 'file' => 'foo.php',
'message' => 'Call to undefined function foo()', '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(
array( array(
@ -69,7 +69,7 @@ class UndefinedFunctionFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
'file' => 'foo.php', 'file' => 'foo.php',
'message' => 'Call to undefined function Foo\\Bar\\Baz\\foo()', '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', 'file' => 'foo.php',
'message' => 'Call to undefined method SplObjectStorage::what()', '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(
array( array(
@ -50,7 +50,7 @@ class UndefinedMethodFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
'file' => 'foo.php', 'file' => 'foo.php',
'message' => 'Call to undefined method SplObjectStorage::walid()', '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(
array( array(
@ -59,7 +59,7 @@ class UndefinedMethodFatalErrorHandlerTest extends \PHPUnit_Framework_TestCase
'file' => 'foo.php', 'file' => 'foo.php',
'message' => 'Call to undefined method SplObjectStorage::offsetFet()', '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": { "require": {
"php": ">=5.3.3" "php": ">=5.3.3",
"psr/log": "~1.0"
}, },
"require-dev": { "require-dev": {
"symfony/http-kernel": "~2.1", "symfony/http-kernel": "~2.1",
@ -33,7 +34,7 @@
"minimum-stability": "dev", "minimum-stability": "dev",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "2.5-dev" "dev-master": "2.6-dev"
} }
} }
} }

View File

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

View File

@ -81,6 +81,9 @@ class AnalyzeServiceReferencesPass implements RepeatablePassInterface
if ($definition->getConfigurator()) { if ($definition->getConfigurator()) {
$this->processArguments(array($definition->getConfigurator())); $this->processArguments(array($definition->getConfigurator()));
} }
if ($definition->getFactory()) {
$this->processArguments(array($definition->getFactory()));
}
} }
} }

View File

@ -42,28 +42,22 @@ class CheckDefinitionValidityPass implements CompilerPassInterface
foreach ($container->getDefinitions() as $id => $definition) { foreach ($container->getDefinitions() as $id => $definition) {
// synthetic service is public // synthetic service is public
if ($definition->isSynthetic() && !$definition->isPublic()) { if ($definition->isSynthetic() && !$definition->isPublic()) {
throw new RuntimeException(sprintf( throw new RuntimeException(sprintf('A synthetic service ("%s") must be public.', $id));
'A synthetic service ("%s") must be public.',
$id
));
} }
// synthetic service has non-prototype scope // synthetic service has non-prototype scope
if ($definition->isSynthetic() && ContainerInterface::SCOPE_PROTOTYPE === $definition->getScope()) { if ($definition->isSynthetic() && ContainerInterface::SCOPE_PROTOTYPE === $definition->getScope()) {
throw new RuntimeException(sprintf( throw new RuntimeException(sprintf('A synthetic service ("%s") cannot be of scope "prototype".', $id));
'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 // non-synthetic, non-abstract service has class
if (!$definition->isAbstract() && !$definition->isSynthetic() && !$definition->getClass()) { if (!$definition->isAbstract() && !$definition->isSynthetic() && !$definition->getClass()) {
if ($definition->getFactoryClass() || $definition->getFactoryService()) { if ($definition->getFactory() || $definition->getFactoryClass() || $definition->getFactoryService()) {
throw new RuntimeException(sprintf( 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));
'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( throw new RuntimeException(sprintf(

View File

@ -64,9 +64,10 @@ class InlineServiceDefinitionsPass implements RepeatablePassInterface
); );
$configurator = $this->inlineArguments($container, array($definition->getConfigurator())); $configurator = $this->inlineArguments($container, array($definition->getConfigurator()));
$definition->setConfigurator( $definition->setConfigurator($configurator[0]);
$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'])) { if (isset($changes['factory_service'])) {
$def->setFactoryService($definition->getFactoryService()); $def->setFactoryService($definition->getFactoryService());
} }
if (isset($changes['factory'])) {
$def->setFactory($definition->getFactory());
}
if (isset($changes['configurator'])) { if (isset($changes['configurator'])) {
$def->setConfigurator($definition->getConfigurator()); $def->setConfigurator($definition->getConfigurator());
} }

View File

@ -12,6 +12,7 @@
namespace Symfony\Component\DependencyInjection\Compiler; namespace Symfony\Component\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder;
@ -84,7 +85,12 @@ class ResolveReferencesToAliasesPass implements CompilerPassInterface
*/ */
private function getDefinitionId($id) private function getDefinitionId($id)
{ {
$seen = array();
while ($this->container->hasAlias($id)) { 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); $id = (string) $this->container->getAlias($id);
} }

View File

@ -217,11 +217,11 @@ class Container implements IntrospectableContainerInterface
$this->$method(); $this->$method();
} }
if (self::SCOPE_CONTAINER !== $scope && null === $service) { if (null === $service) {
if (self::SCOPE_CONTAINER !== $scope) {
unset($this->scopedServices[$scope][$id]); unset($this->scopedServices[$scope][$id]);
} }
if (null === $service) {
unset($this->services[$id]); unset($this->services[$id]);
} }
} }
@ -450,14 +450,15 @@ class Container implements IntrospectableContainerInterface
// the global service map // the global service map
$services = array($this->services, $this->scopedServices[$name]); $services = array($this->services, $this->scopedServices[$name]);
unset($this->scopedServices[$name]); unset($this->scopedServices[$name]);
foreach ($this->scopeChildren[$name] as $child) {
if (!isset($this->scopedServices[$child])) {
continue;
}
foreach ($this->scopeChildren[$name] as $child) {
if (isset($this->scopedServices[$child])) {
$services[] = $this->scopedServices[$child]; $services[] = $this->scopedServices[$child];
unset($this->scopedServices[$child]); unset($this->scopedServices[$child]);
} }
}
// update global map
$this->services = call_user_func_array('array_diff_key', $services); $this->services = call_user_func_array('array_diff_key', $services);
// check if we need to restore services of a previous scope of this type // 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(); $services = $this->scopeStacks[$name]->pop();
$this->scopedServices += $services; $this->scopedServices += $services;
if ($this->scopeStacks[$name]->isEmpty()) {
unset($this->scopeStacks[$name]);
}
foreach ($services as $array) { foreach ($services as $array) {
foreach ($array as $id => $service) { foreach ($array as $id => $service) {
$this->set($id, $service, $name); $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\InstantiatorInterface;
use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\RealServiceInstantiator; use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\RealServiceInstantiator;
use Symfony\Component\ExpressionLanguage\Expression; use Symfony\Component\ExpressionLanguage\Expression;
use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
/** /**
* ContainerBuilder is a DI container that provides an API to easily describe services. * 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; private $expressionLanguage;
/**
* @var ExpressionFunctionProviderInterface[]
*/
private $expressionLanguageProviders = array();
/** /**
* Sets the track resources flag. * Sets the track resources flag.
* *
@ -464,10 +470,6 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
return $service; 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])) { if (!$this->hasDefinition($id) && isset($this->aliasDefinitions[$id])) {
return $this->get($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()))); $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()) { if (null !== $definition->getFactoryClass()) {
$factory = $parameterBag->resolveValue($definition->getFactoryClass()); $factory = $parameterBag->resolveValue($definition->getFactoryClass());
} elseif (null !== $definition->getFactoryService()) { } elseif (null !== $definition->getFactoryService()) {
@ -1056,6 +1070,11 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
return array_unique($tags); return array_unique($tags);
} }
public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider)
{
$this->expressionLanguageProviders[] = $provider;
}
/** /**
* Returns the Service Conditionals. * Returns the Service Conditionals.
* *
@ -1161,7 +1180,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) { if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
throw new RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.'); 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; return $this->expressionLanguage;

View File

@ -25,6 +25,7 @@ class Definition
{ {
private $class; private $class;
private $file; private $file;
private $factory;
private $factoryClass; private $factoryClass;
private $factoryMethod; private $factoryMethod;
private $factoryService; private $factoryService;
@ -56,6 +57,34 @@ class Definition
$this->arguments = $arguments; $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, * Sets the name of the class that acts as a factory using the factory method,
* which will be invoked statically. * which will be invoked statically.
@ -65,6 +94,7 @@ class Definition
* @return Definition The current instance * @return Definition The current instance
* *
* @api * @api
* @deprecated Deprecated since version 2.6, to be removed in 3.0.
*/ */
public function setFactoryClass($factoryClass) public function setFactoryClass($factoryClass)
{ {
@ -79,6 +109,7 @@ class Definition
* @return string|null The factory class name * @return string|null The factory class name
* *
* @api * @api
* @deprecated Deprecated since version 2.6, to be removed in 3.0.
*/ */
public function getFactoryClass() public function getFactoryClass()
{ {
@ -93,6 +124,7 @@ class Definition
* @return Definition The current instance * @return Definition The current instance
* *
* @api * @api
* @deprecated Deprecated since version 2.6, to be removed in 3.0.
*/ */
public function setFactoryMethod($factoryMethod) public function setFactoryMethod($factoryMethod)
{ {
@ -142,6 +174,7 @@ class Definition
* @return string|null The factory method name * @return string|null The factory method name
* *
* @api * @api
* @deprecated Deprecated since version 2.6, to be removed in 3.0.
*/ */
public function getFactoryMethod() public function getFactoryMethod()
{ {
@ -156,6 +189,7 @@ class Definition
* @return Definition The current instance * @return Definition The current instance
* *
* @api * @api
* @deprecated Deprecated since version 2.6, to be removed in 3.0.
*/ */
public function setFactoryService($factoryService) public function setFactoryService($factoryService)
{ {
@ -170,6 +204,7 @@ class Definition
* @return string|null The factory service id * @return string|null The factory service id
* *
* @api * @api
* @deprecated Deprecated since version 2.6, to be removed in 3.0.
*/ */
public function getFactoryService() public function getFactoryService()
{ {

View File

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

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\LazyProxy\PhpDumper\NullDumper;
use Symfony\Component\DependencyInjection\ExpressionLanguage; use Symfony\Component\DependencyInjection\ExpressionLanguage;
use Symfony\Component\ExpressionLanguage\Expression; use Symfony\Component\ExpressionLanguage\Expression;
use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
/** /**
* PhpDumper dumps a service container as a PHP class. * PhpDumper dumps a service container as a PHP class.
@ -55,6 +56,11 @@ class PhpDumper extends Dumper
private $reservedVariables = array('instance', 'class'); private $reservedVariables = array('instance', 'class');
private $expressionLanguage; private $expressionLanguage;
/**
* @var ExpressionFunctionProviderInterface[]
*/
private $expressionLanguageProviders = array();
/** /**
* @var \Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface * @var \Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface
*/ */
@ -109,6 +115,7 @@ class PhpDumper extends Dumper
if ($this->container->isFrozen()) { if ($this->container->isFrozen()) {
$code .= $this->addFrozenConstructor(); $code .= $this->addFrozenConstructor();
$code .= $this->addFrozenCompile();
} else { } else {
$code .= $this->addConstructor(); $code .= $this->addConstructor();
} }
@ -160,6 +167,7 @@ class PhpDumper extends Dumper
$this->getServiceCallsFromArguments($iDefinition->getMethodCalls(), $calls, $behavior); $this->getServiceCallsFromArguments($iDefinition->getMethodCalls(), $calls, $behavior);
$this->getServiceCallsFromArguments($iDefinition->getProperties(), $calls, $behavior); $this->getServiceCallsFromArguments($iDefinition->getProperties(), $calls, $behavior);
$this->getServiceCallsFromArguments(array($iDefinition->getConfigurator()), $calls, $behavior); $this->getServiceCallsFromArguments(array($iDefinition->getConfigurator()), $calls, $behavior);
$this->getServiceCallsFromArguments(array($iDefinition->getFactory()), $calls, $behavior);
} }
$code = ''; $code = '';
@ -522,6 +530,17 @@ class PhpDumper extends Dumper
$return[] = '@throws RuntimeException always since this service is expected to be injected dynamically'; $return[] = '@throws RuntimeException always since this service is expected to be injected dynamically';
} elseif ($class = $definition->getClass()) { } elseif ($class = $definition->getClass()) {
$return[] = sprintf("@return %s A %s instance.", 0 === strpos($class, '%') ? 'object' : "\\".$class, $class); $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()) { } elseif ($definition->getFactoryClass()) {
$return[] = sprintf('@return object An instance returned by %s::%s().', $definition->getFactoryClass(), $definition->getFactoryMethod()); $return[] = sprintf('@return object An instance returned by %s::%s().', $definition->getFactoryClass(), $definition->getFactoryMethod());
} elseif ($definition->getFactoryService()) { } elseif ($definition->getFactoryService()) {
@ -700,7 +719,25 @@ EOF;
$arguments[] = $this->dumpValue($value); $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()) { if (null !== $definition->getFactoryClass()) {
$class = $this->dumpValue($definition->getFactoryClass()); $class = $this->dumpValue($definition->getFactoryClass());
@ -769,16 +806,18 @@ EOF;
*/ */
private function addConstructor() private function addConstructor()
{ {
$arguments = $this->container->getParameterBag()->all() ? 'new ParameterBag($this->getDefaultParameters())' : null; $parameters = $this->exportParameters($this->container->getParameterBag()->all());
$code = <<<EOF $code = <<<EOF
private static \$parameters = $parameters;
/** /**
* Constructor. * Constructor.
*/ */
public function __construct() public function __construct()
{ {
parent::__construct($arguments); parent::__construct(new ParameterBag(self::\$parameters));
EOF; EOF;
@ -806,23 +845,17 @@ EOF;
*/ */
private function addFrozenConstructor() private function addFrozenConstructor()
{ {
$parameters = $this->exportParameters($this->container->getParameterBag()->all());
$code = <<<EOF $code = <<<EOF
private \$parameters; private static \$parameters = $parameters;
/** /**
* Constructor. * Constructor.
*/ */
public function __construct() public function __construct()
{ {
EOF;
if ($this->container->getParameterBag()->all()) {
$code .= "\n \$this->parameters = \$this->getDefaultParameters();\n";
}
$code .= <<<EOF
\$this->services = \$this->services =
\$this->scopedServices = \$this->scopedServices =
\$this->scopeStacks = array(); \$this->scopeStacks = array();
@ -851,6 +884,26 @@ EOF;
return $code; 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 * Adds the methodMap property definition
* *
@ -910,8 +963,6 @@ EOF;
return ''; return '';
} }
$parameters = $this->exportParameters($this->container->getParameterBag()->all());
$code = ''; $code = '';
if ($this->container->isFrozen()) { if ($this->container->isFrozen()) {
$code .= <<<EOF $code .= <<<EOF
@ -923,11 +974,11 @@ EOF;
{ {
\$name = strtolower(\$name); \$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)); 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); \$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,27 +1005,14 @@ EOF;
public function getParameterBag() public function getParameterBag()
{ {
if (null === \$this->parameterBag) { if (null === \$this->parameterBag) {
\$this->parameterBag = new FrozenParameterBag(\$this->parameters); \$this->parameterBag = new FrozenParameterBag(self::\$parameters);
} }
return \$this->parameterBag; return \$this->parameterBag;
} }
EOF;
}
$code .= <<<EOF
/**
* Gets the default parameters.
*
* @return array An array of the default parameters
*/
protected function getDefaultParameters()
{
return $parameters;
}
EOF; EOF;
}
return $code; return $code;
} }
@ -1095,7 +1133,8 @@ EOF;
$this->getDefinitionsFromArguments($definition->getArguments()), $this->getDefinitionsFromArguments($definition->getArguments()),
$this->getDefinitionsFromArguments($definition->getMethodCalls()), $this->getDefinitionsFromArguments($definition->getMethodCalls()),
$this->getDefinitionsFromArguments($definition->getProperties()), $this->getDefinitionsFromArguments($definition->getProperties()),
$this->getDefinitionsFromArguments(array($definition->getConfigurator())) $this->getDefinitionsFromArguments(array($definition->getConfigurator())),
$this->getDefinitionsFromArguments(array($definition->getFactory()))
); );
$this->inlinedDefinitions->offsetSet($definition, $definitions); $this->inlinedDefinitions->offsetSet($definition, $definitions);
@ -1173,7 +1212,7 @@ EOF;
/** /**
* Dumps values. * Dumps values.
* *
* @param array $value * @param mixed $value
* @param bool $interpolate * @param bool $interpolate
* *
* @return string * @return string
@ -1210,6 +1249,30 @@ EOF;
throw new RuntimeException('Cannot dump definitions which have a variable class name.'); 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->getFactoryMethod()) {
if (null !== $value->getFactoryClass()) { if (null !== $value->getFactoryClass()) {
return sprintf("call_user_func(array(%s, '%s')%s)", $this->dumpValue($value->getFactoryClass()), $value->getFactoryMethod(), count($arguments) > 0 ? ', '.implode(', ', $arguments) : ''); 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)); return sprintf("\$this->getParameter('%s')", strtolower($name));
} }
public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider)
{
$this->expressionLanguageProviders[] = $provider;
}
/** /**
* Gets a service call * Gets a service call
* *
@ -1372,7 +1440,7 @@ EOF;
if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) { if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
throw new RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.'); 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; return $this->expressionLanguage;

View File

@ -176,6 +176,17 @@ class XmlDumper extends Dumper
$this->addMethodCalls($definition->getMethodCalls(), $service); $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()) { if ($callable = $definition->getConfigurator()) {
$configurator = $this->document->createElement('configurator'); $configurator = $this->document->createElement('configurator');
if (is_array($callable)) { if (is_array($callable)) {

View File

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

View File

@ -12,31 +12,22 @@
namespace Symfony\Component\DependencyInjection; namespace Symfony\Component\DependencyInjection;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage as BaseExpressionLanguage; use Symfony\Component\ExpressionLanguage\ExpressionLanguage as BaseExpressionLanguage;
use Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface;
/** /**
* Adds some function to the default ExpressionLanguage. * 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> * @author Fabien Potencier <fabien@symfony.com>
*
* @see ExpressionLanguageProvider
*/ */
class ExpressionLanguage extends BaseExpressionLanguage 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) { parent::__construct($cache, $providers);
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);
});
} }
} }

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. * {@inheritdoc}
*
* @param \Closure $closure The resource
* @param string $type The resource type
*/ */
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. * {@inheritdoc}
*
* @param mixed $resource A resource
* @param string $type The resource type
*
* @return bool true if this class supports the given resource, false otherwise
*/ */
public function supports($resource, $type = null) public function supports($resource, $type = null)
{ {

View File

@ -22,22 +22,17 @@ use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
class IniFileLoader extends FileLoader class IniFileLoader extends FileLoader
{ {
/** /**
* Loads a resource. * {@inheritdoc}
*
* @param mixed $file The resource
* @param string $type The resource type
*
* @throws InvalidArgumentException When ini file is not valid
*/ */
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)); $this->container->addResource(new FileResource($path));
$result = parse_ini_file($path, true); $result = parse_ini_file($path, true);
if (false === $result || array() === $result) { 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'])) { 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. * {@inheritdoc}
*
* @param mixed $resource A resource
* @param string $type The resource type
*
* @return bool true if this class supports the given resource, false otherwise
*/ */
public function supports($resource, $type = null) public function supports($resource, $type = null)
{ {

View File

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

View File

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

View File

@ -33,14 +33,11 @@ class YamlFileLoader extends FileLoader
private $yamlParser; private $yamlParser;
/** /**
* Loads a Yaml file. * {@inheritdoc}
*
* @param mixed $file The resource
* @param string $type The resource type
*/ */
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); $content = $this->loadFile($path);
@ -57,7 +54,7 @@ class YamlFileLoader extends FileLoader
// parameters // parameters
if (isset($content['parameters'])) { if (isset($content['parameters'])) {
if (!is_array($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) { foreach ($content['parameters'] as $key => $value) {
@ -69,16 +66,11 @@ class YamlFileLoader extends FileLoader
$this->loadFromExtensions($content); $this->loadFromExtensions($content);
// services // services
$this->parseDefinitions($content, $file); $this->parseDefinitions($content, $resource);
} }
/** /**
* Returns true if this class supports the given resource. * {@inheritdoc}
*
* @param mixed $resource A resource
* @param string $type The resource type
*
* @return bool true if this class supports the given resource, false otherwise
*/ */
public function supports($resource, $type = null) public function supports($resource, $type = null)
{ {
@ -194,6 +186,19 @@ class YamlFileLoader extends FileLoader
$definition->setAbstract($service['abstract']); $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'])) { if (isset($service['factory_class'])) {
$definition->setFactoryClass($service['factory_class']); $definition->setFactoryClass($service['factory_class']);
} }

View File

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

View File

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

View File

@ -50,6 +50,17 @@ class CheckDefinitionValidityPassTest extends \PHPUnit_Framework_TestCase
$this->process($container); $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() public function testProcess()
{ {
$container = new ContainerBuilder(); $container = new ContainerBuilder();

View File

@ -48,6 +48,17 @@ class ResolveReferencesToAliasesPassTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('foo', (string) $arguments[0]); $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) protected function process(ContainerBuilder $container)
{ {
$pass = new ResolveReferencesToAliasesPass(); $pass = new ResolveReferencesToAliasesPass();

View File

@ -341,6 +341,23 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase
$this->assertInstanceOf('BazClass', $builder->get('baz_service')); $this->assertInstanceOf('BazClass', $builder->get('baz_service'));
} }
/**
* @covers Symfony\Component\DependencyInjection\ContainerBuilder::createService
*/
public function testCreateServiceFactory()
{
$builder = new ContainerBuilder();
$builder->register('foo', 'Bar\FooClass')->setFactory('Bar\FooClass::getInstance');
$builder->register('qux', 'Bar\FooClass')->setFactory(array('Bar\FooClass', 'getInstance'));
$builder->register('bar', 'Bar\FooClass')->setFactory(array(new Definition('Bar\FooClass'), 'getInstance'));
$builder->register('baz', 'Bar\FooClass')->setFactory(array(new Reference('bar'), 'getInstance'));
$this->assertTrue($builder->get('foo')->called, '->createService() calls the factory method to create the service instance');
$this->assertTrue($builder->get('qux')->called, '->createService() calls the factory method to create the service instance');
$this->assertTrue($builder->get('bar')->called, '->createService() uses anonymous service as factory');
$this->assertTrue($builder->get('baz')->called, '->createService() uses another service as factory');
}
/** /**
* @covers Symfony\Component\DependencyInjection\ContainerBuilder::createService * @covers Symfony\Component\DependencyInjection\ContainerBuilder::createService
*/ */

View File

@ -55,13 +55,35 @@ class ContainerTest extends \PHPUnit_Framework_TestCase
); );
} }
/**
* @dataProvider dataForTestUnderscore
*/
public function testUnderscore($id, $expected)
{
$this->assertEquals($expected, Container::underscore($id), sprintf('Container::underscore("%s")', $id));
}
public function dataForTestUnderscore()
{
return array(
array('FooBar', 'foo_bar'),
array('Foo_Bar', 'foo.bar'),
array('Foo_BarBaz', 'foo.bar_baz'),
array('FooBar_BazQux', 'foo_bar.baz_qux'),
array('_Foo', '.foo'),
array('Foo_', 'foo.'),
);
}
/** /**
* @covers Symfony\Component\DependencyInjection\Container::compile * @covers Symfony\Component\DependencyInjection\Container::compile
*/ */
public function testCompile() public function testCompile()
{ {
$sc = new Container(new ParameterBag(array('foo' => 'bar'))); $sc = new Container(new ParameterBag(array('foo' => 'bar')));
$this->assertFalse($sc->getParameterBag()->isResolved(), '->compile() resolves the parameter bag');
$sc->compile(); $sc->compile();
$this->assertTrue($sc->getParameterBag()->isResolved(), '->compile() resolves the parameter bag');
$this->assertInstanceOf('Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag', $sc->getParameterBag(), '->compile() changes the parameter bag to a FrozenParameterBag instance'); $this->assertInstanceOf('Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag', $sc->getParameterBag(), '->compile() changes the parameter bag to a FrozenParameterBag instance');
$this->assertEquals(array('foo' => 'bar'), $sc->getParameterBag()->all(), '->compile() copies the current parameters to the new parameter bag'); $this->assertEquals(array('foo' => 'bar'), $sc->getParameterBag()->all(), '->compile() copies the current parameters to the new parameter bag');
} }
@ -123,7 +145,8 @@ class ContainerTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(array('service_container', 'foo', 'bar'), $sc->getServiceIds(), '->getServiceIds() returns all defined service ids'); $this->assertEquals(array('service_container', 'foo', 'bar'), $sc->getServiceIds(), '->getServiceIds() returns all defined service ids');
$sc = new ProjectServiceContainer(); $sc = new ProjectServiceContainer();
$this->assertEquals(array('scoped', 'scoped_foo', 'inactive', 'bar', 'foo_bar', 'foo.baz', 'circular', 'throw_exception', 'throws_exception_on_service_configuration', 'service_container'), $sc->getServiceIds(), '->getServiceIds() returns defined service ids by getXXXService() methods'); $sc->set('foo', $obj = new \stdClass());
$this->assertEquals(array('scoped', 'scoped_foo', 'scoped_synchronized_foo', 'inactive', 'bar', 'foo_bar', 'foo.baz', 'circular', 'throw_exception', 'throws_exception_on_service_configuration', 'service_container', 'foo'), $sc->getServiceIds(), '->getServiceIds() returns defined service ids by getXXXService() methods, followed by service ids defined by set()');
} }
/** /**
@ -143,7 +166,7 @@ class ContainerTest extends \PHPUnit_Framework_TestCase
{ {
$sc = new Container(); $sc = new Container();
$sc->set('foo', null); $sc->set('foo', null);
$this->assertFalse($sc->has('foo')); $this->assertFalse($sc->has('foo'), '->set() with null service resets the service');
} }
/** /**
@ -152,7 +175,7 @@ class ContainerTest extends \PHPUnit_Framework_TestCase
public function testSetDoesNotAllowPrototypeScope() public function testSetDoesNotAllowPrototypeScope()
{ {
$c = new Container(); $c = new Container();
$c->set('foo', new \stdClass(), 'prototype'); $c->set('foo', new \stdClass(), Container::SCOPE_PROTOTYPE);
} }
/** /**
@ -172,9 +195,18 @@ class ContainerTest extends \PHPUnit_Framework_TestCase
$c->enterScope('foo'); $c->enterScope('foo');
$c->set('foo', $foo = new \stdClass(), 'foo'); $c->set('foo', $foo = new \stdClass(), 'foo');
$services = $this->getField($c, 'scopedServices'); $scoped = $this->getField($c, 'scopedServices');
$this->assertTrue(isset($services['foo']['foo'])); $this->assertTrue(isset($scoped['foo']['foo']), '->set() sets a scoped service');
$this->assertSame($foo, $services['foo']['foo']); $this->assertSame($foo, $scoped['foo']['foo'], '->set() sets a scoped service');
}
public function testSetAlsoCallsSynchronizeService()
{
$c = new ProjectServiceContainer();
$c->addScope(new Scope('foo'));
$c->enterScope('foo');
$c->set('scoped_synchronized_foo', $bar = new \stdClass(), 'foo');
$this->assertTrue($c->synchronized, '->set() calls synchronize*Service() if it is defined for the service');
} }
/** /**
@ -185,6 +217,7 @@ class ContainerTest extends \PHPUnit_Framework_TestCase
$sc = new ProjectServiceContainer(); $sc = new ProjectServiceContainer();
$sc->set('foo', $foo = new \stdClass()); $sc->set('foo', $foo = new \stdClass());
$this->assertEquals($foo, $sc->get('foo'), '->get() returns the service for the given id'); $this->assertEquals($foo, $sc->get('foo'), '->get() returns the service for the given id');
$this->assertEquals($foo, $sc->get('Foo'), '->get() returns the service for the given id, and converts id to lowercase');
$this->assertEquals($sc->__bar, $sc->get('bar'), '->get() returns the service for the given id'); $this->assertEquals($sc->__bar, $sc->get('bar'), '->get() returns the service for the given id');
$this->assertEquals($sc->__foo_bar, $sc->get('foo_bar'), '->get() returns the service if a get*Method() is defined'); $this->assertEquals($sc->__foo_bar, $sc->get('foo_bar'), '->get() returns the service if a get*Method() is defined');
$this->assertEquals($sc->__foo_baz, $sc->get('foo.baz'), '->get() returns the service if a get*Method() is defined'); $this->assertEquals($sc->__foo_baz, $sc->get('foo.baz'), '->get() returns the service if a get*Method() is defined');
@ -199,7 +232,7 @@ class ContainerTest extends \PHPUnit_Framework_TestCase
} catch (\Exception $e) { } catch (\Exception $e) {
$this->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException', $e, '->get() throws a ServiceNotFoundException exception if the service is empty'); $this->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException', $e, '->get() throws a ServiceNotFoundException exception if the service is empty');
} }
$this->assertNull($sc->get('', ContainerInterface::NULL_ON_INVALID_REFERENCE)); $this->assertNull($sc->get('', ContainerInterface::NULL_ON_INVALID_REFERENCE), '->get() returns null if the service is empty');
} }
public function testGetThrowServiceNotFoundException() public function testGetThrowServiceNotFoundException()
@ -316,16 +349,15 @@ class ContainerTest extends \PHPUnit_Framework_TestCase
$a = new \stdClass(); $a = new \stdClass();
$container->set('a', $a, 'bar'); $container->set('a', $a, 'bar');
$services = $this->getField($container, 'scopedServices'); $scoped = $this->getField($container, 'scopedServices');
$this->assertTrue(isset($services['bar']['a'])); $this->assertTrue(isset($scoped['bar']['a']));
$this->assertSame($a, $services['bar']['a']); $this->assertSame($a, $scoped['bar']['a']);
$this->assertTrue($container->has('a')); $this->assertTrue($container->has('a'));
$container->leaveScope('foo'); $container->leaveScope('foo');
$services = $this->getField($container, 'scopedServices'); $scoped = $this->getField($container, 'scopedServices');
$this->assertFalse(isset($services['bar'])); $this->assertFalse(isset($scoped['bar']));
$this->assertFalse($container->isScopeActive('foo')); $this->assertFalse($container->isScopeActive('foo'));
$this->assertFalse($container->has('a')); $this->assertFalse($container->has('a'));
} }
@ -347,19 +379,83 @@ class ContainerTest extends \PHPUnit_Framework_TestCase
$a = new \stdClass(); $a = new \stdClass();
$container->set('a', $a, 'foo'); $container->set('a', $a, 'foo');
$services = $this->getField($container, 'scopedServices'); $scoped = $this->getField($container, 'scopedServices');
$this->assertTrue(isset($services['foo']['a'])); $this->assertTrue(isset($scoped['foo']['a']));
$this->assertSame($a, $services['foo']['a']); $this->assertSame($a, $scoped['foo']['a']);
$this->assertTrue($container->has('a')); $this->assertTrue($container->has('a'));
$container->enterScope('foo'); $container->enterScope('foo');
$services = $this->getField($container, 'scopedServices'); $scoped = $this->getField($container, 'scopedServices');
$this->assertFalse(isset($services['a'])); $this->assertFalse(isset($scoped['a']));
$this->assertTrue($container->isScopeActive('foo')); $this->assertTrue($container->isScopeActive('foo'));
$this->assertFalse($container->isScopeActive('bar')); $this->assertFalse($container->isScopeActive('bar'));
$this->assertFalse($container->has('a')); $this->assertFalse($container->has('a'));
$container->enterScope('bar');
$this->assertTrue($container->isScopeActive('bar'));
$container->leaveScope('foo');
$this->assertTrue($container->isScopeActive('foo'));
$this->assertFalse($container->isScopeActive('bar'));
$this->assertTrue($container->has('a'));
}
public function testEnterChildScopeRecursively()
{
$container = new Container();
$container->addScope(new Scope('foo'));
$container->addScope(new Scope('bar', 'foo'));
$container->enterScope('foo');
$container->enterScope('bar');
$this->assertTrue($container->isScopeActive('bar'));
$this->assertFalse($container->has('a'));
$a = new \stdClass();
$container->set('a', $a, 'bar');
$scoped = $this->getField($container, 'scopedServices');
$this->assertTrue(isset($scoped['bar']['a']));
$this->assertSame($a, $scoped['bar']['a']);
$this->assertTrue($container->has('a'));
$container->enterScope('bar');
$scoped = $this->getField($container, 'scopedServices');
$this->assertFalse(isset($scoped['a']));
$this->assertTrue($container->isScopeActive('foo'));
$this->assertTrue($container->isScopeActive('bar'));
$this->assertFalse($container->has('a'));
$container->leaveScope('bar');
$this->assertTrue($container->isScopeActive('foo'));
$this->assertTrue($container->isScopeActive('bar'));
$this->assertTrue($container->has('a'));
}
/**
* @expectedException \InvalidArgumentException
*/
public function testEnterScopeNotAdded()
{
$container = new Container();
$container->enterScope('foo');
}
/**
* @expectedException \RuntimeException
*/
public function testEnterScopeDoesNotAllowInactiveParentScope()
{
$container = new Container();
$container->addScope(new Scope('foo'));
$container->addScope(new Scope('bar', 'foo'));
$container->enterScope('bar');
} }
public function testLeaveScopeNotActive() public function testLeaveScopeNotActive()
@ -422,6 +518,11 @@ class ContainerTest extends \PHPUnit_Framework_TestCase
$this->assertSame(array('foo' => 'container', 'bar' => 'foo'), $this->getField($c, 'scopes')); $this->assertSame(array('foo' => 'container', 'bar' => 'foo'), $this->getField($c, 'scopes'));
$this->assertSame(array('foo' => array('bar'), 'bar' => array()), $this->getField($c, 'scopeChildren')); $this->assertSame(array('foo' => array('bar'), 'bar' => array()), $this->getField($c, 'scopeChildren'));
$c->addScope(new Scope('baz', 'bar'));
$this->assertSame(array('foo' => 'container', 'bar' => 'foo', 'baz' => 'bar'), $this->getField($c, 'scopes'));
$this->assertSame(array('foo' => array('bar', 'baz'), 'bar' => array('baz'), 'baz' => array()), $this->getField($c, 'scopeChildren'));
} }
public function testHasScope() public function testHasScope()
@ -433,6 +534,45 @@ class ContainerTest extends \PHPUnit_Framework_TestCase
$this->assertTrue($c->hasScope('foo')); $this->assertTrue($c->hasScope('foo'));
} }
/**
* @expectedException Exception
* @expectedExceptionMessage Something went terribly wrong!
*/
public function testGetThrowsException()
{
$c = new ProjectServiceContainer();
try {
$c->get('throw_exception');
} catch (\Exception $e) {
// Do nothing.
}
// Retry, to make sure that get*Service() will be called.
$c->get('throw_exception');
}
public function testGetThrowsExceptionOnServiceConfiguration()
{
$c = new ProjectServiceContainer();
try {
$c->get('throws_exception_on_service_configuration');
} catch (\Exception $e) {
// Do nothing.
}
$this->assertFalse($c->initialized('throws_exception_on_service_configuration'));
// Retry, to make sure that get*Service() will be called.
try {
$c->get('throws_exception_on_service_configuration');
} catch (\Exception $e) {
// Do nothing.
}
$this->assertFalse($c->initialized('throws_exception_on_service_configuration'));
}
public function testIsScopeActive() public function testIsScopeActive()
{ {
$c = new Container(); $c = new Container();
@ -449,46 +589,6 @@ class ContainerTest extends \PHPUnit_Framework_TestCase
$this->assertFalse($c->isScopeActive('foo')); $this->assertFalse($c->isScopeActive('foo'));
} }
public function testGetThrowsException()
{
$c = new ProjectServiceContainer();
try {
$c->get('throw_exception');
$this->fail();
} catch (\Exception $e) {
$this->assertEquals('Something went terribly wrong!', $e->getMessage());
}
try {
$c->get('throw_exception');
$this->fail();
} catch (\Exception $e) {
$this->assertEquals('Something went terribly wrong!', $e->getMessage());
}
}
public function testGetThrowsExceptionOnServiceConfiguration()
{
$c = new ProjectServiceContainer();
try {
$c->get('throws_exception_on_service_configuration');
$this->fail('The container can not contain invalid service!');
} catch (\Exception $e) {
$this->assertEquals('Something was terribly wrong while trying to configure the service!', $e->getMessage());
}
$this->assertFalse($c->initialized('throws_exception_on_service_configuration'));
try {
$c->get('throws_exception_on_service_configuration');
$this->fail('The container can not contain invalid service!');
} catch (\Exception $e) {
$this->assertEquals('Something was terribly wrong while trying to configure the service!', $e->getMessage());
}
$this->assertFalse($c->initialized('throws_exception_on_service_configuration'));
}
public function getInvalidParentScopes() public function getInvalidParentScopes()
{ {
return array( return array(
@ -525,6 +625,7 @@ class ContainerTest extends \PHPUnit_Framework_TestCase
class ProjectServiceContainer extends Container class ProjectServiceContainer extends Container
{ {
public $__bar, $__foo_bar, $__foo_baz; public $__bar, $__foo_bar, $__foo_baz;
public $synchronized;
public function __construct() public function __construct()
{ {
@ -533,12 +634,13 @@ class ProjectServiceContainer extends Container
$this->__bar = new \stdClass(); $this->__bar = new \stdClass();
$this->__foo_bar = new \stdClass(); $this->__foo_bar = new \stdClass();
$this->__foo_baz = new \stdClass(); $this->__foo_baz = new \stdClass();
$this->synchronized = false;
$this->aliases = array('alias' => 'bar'); $this->aliases = array('alias' => 'bar');
} }
protected function getScopedService() protected function getScopedService()
{ {
if (!isset($this->scopedServices['foo'])) { if (!$this->isScopeActive('foo')) {
throw new \RuntimeException('Invalid call'); throw new \RuntimeException('Invalid call');
} }
@ -547,13 +649,27 @@ class ProjectServiceContainer extends Container
protected function getScopedFooService() protected function getScopedFooService()
{ {
if (!isset($this->scopedServices['foo'])) { if (!$this->isScopeActive('foo')) {
throw new \RuntimeException('invalid call'); throw new \RuntimeException('invalid call');
} }
return $this->services['scoped_foo'] = $this->scopedServices['foo']['scoped_foo'] = new \stdClass(); return $this->services['scoped_foo'] = $this->scopedServices['foo']['scoped_foo'] = new \stdClass();
} }
protected function getScopedSynchronizedFooService()
{
if (!$this->isScopeActive('foo')) {
throw new \RuntimeException('invalid call');
}
return $this->services['scoped_bar'] = $this->scopedServices['foo']['scoped_bar'] = new \stdClass();
}
protected function synchronizeScopedSynchronizedFooService()
{
$this->synchronized = true;
}
protected function getInactiveService() protected function getInactiveService()
{ {
throw new InactiveScopeException('request', 'request'); throw new InactiveScopeException('request', 'request');

View File

@ -43,6 +43,7 @@ class DefinitionDecoratorTest extends \PHPUnit_Framework_TestCase
{ {
return array( return array(
array('class', 'class'), array('class', 'class'),
array('factory', 'factory'),
array('factoryClass', 'factory_class'), array('factoryClass', 'factory_class'),
array('factoryMethod', 'factory_method'), array('factoryMethod', 'factory_method'),
array('factoryService', 'factory_service'), array('factoryService', 'factory_service'),

View File

@ -27,6 +27,21 @@ class DefinitionTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(array('foo'), $def->getArguments(), '__construct() takes an optional array of arguments as its second argument'); $this->assertEquals(array('foo'), $def->getArguments(), '__construct() takes an optional array of arguments as its second argument');
} }
/**
* @covers Symfony\Component\DependencyInjection\Definition::setFactory
* @covers Symfony\Component\DependencyInjection\Definition::getFactory
*/
public function testSetGetFactory()
{
$def = new Definition('stdClass');
$this->assertSame($def, $def->setFactory('foo'), '->setFactory() implements a fluent interface');
$this->assertEquals('foo', $def->getFactory(), '->getFactory() returns the factory');
$def->setFactory('Foo::bar');
$this->assertEquals(array('Foo', 'bar'), $def->getFactory(), '->setFactory() converts string static method call to the array');
}
public function testSetGetFactoryClass() public function testSetGetFactoryClass()
{ {
$def = new Definition('stdClass'); $def = new Definition('stdClass');

View File

@ -120,6 +120,14 @@ class PhpDumperTest extends \PHPUnit_Framework_TestCase
} }
} }
public function testServicesWithAnonymousFactories()
{
$container = include self::$fixturesPath.'/containers/container19.php';
$dumper = new PhpDumper($container);
$this->assertStringEqualsFile(self::$fixturesPath.'/php/services19.php', $dumper->dump(), '->dump() dumps services with anonymous factories');
}
/** /**
* @expectedException \InvalidArgumentException * @expectedException \InvalidArgumentException
* @expectedExceptionMessage Service id "bar$" cannot be converted to a valid PHP method name. * @expectedExceptionMessage Service id "bar$" cannot be converted to a valid PHP method name.

View File

@ -0,0 +1,22 @@
<?php
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
require_once __DIR__.'/../includes/classes.php';
$container = new ContainerBuilder();
$container
->register('service_from_anonymous_factory', 'Bar\FooClass')
->setFactory(array(new Definition('Bar\FooClass'), 'getInstance'))
;
$anonymousServiceWithFactory = new Definition('Bar\FooClass');
$anonymousServiceWithFactory->setFactory('Bar\FooClass::getInstance');
$container
->register('service_with_method_call_and_factory', 'Bar\FooClass')
->addMethodCall('setBar', array($anonymousServiceWithFactory))
;
return $container;

View File

@ -103,5 +103,20 @@ $container
->register('decorator_service_with_name', 'stdClass') ->register('decorator_service_with_name', 'stdClass')
->setDecoratedService('decorated', 'decorated.pif-pouf') ->setDecoratedService('decorated', 'decorated.pif-pouf')
; ;
$container
->register('new_factory', 'FactoryClass')
->setProperty('foo', 'bar')
->setScope('container')
->setPublic(false)
;
$container
->register('new_factory_service', 'FooBarBaz')
->setProperty('foo', 'bar')
->setFactory(array(new Reference('new_factory'), 'getInstance'))
;
$container
->register('service_from_static_method', 'Bar\FooClass')
->setFactory(array('Bar\FooClass', 'getInstance'))
;
return $container; return $container;

View File

@ -19,6 +19,9 @@ digraph sc {
node_decorated [label="decorated\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; node_decorated [label="decorated\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_decorator_service [label="decorator_service\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; node_decorator_service [label="decorator_service\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_decorator_service_with_name [label="decorator_service_with_name\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; node_decorator_service_with_name [label="decorator_service_with_name\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_new_factory [label="new_factory\nFactoryClass\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_new_factory_service [label="new_factory_service\nFooBarBaz\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_service_from_static_method [label="service_from_static_method\nBar\\FooClass\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_service_container [label="service_container\nSymfony\\Component\\DependencyInjection\\ContainerBuilder\n", shape=record, fillcolor="#9999ff", style="filled"]; node_service_container [label="service_container\nSymfony\\Component\\DependencyInjection\\ContainerBuilder\n", shape=record, fillcolor="#9999ff", style="filled"];
node_foo2 [label="foo2\n\n", shape=record, fillcolor="#ff9999", style="filled"]; node_foo2 [label="foo2\n\n", shape=record, fillcolor="#ff9999", style="filled"];
node_foo3 [label="foo3\n\n", shape=record, fillcolor="#ff9999", style="filled"]; node_foo3 [label="foo3\n\n", shape=record, fillcolor="#ff9999", style="filled"];

View File

@ -16,11 +16,15 @@ use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
*/ */
class ProjectServiceContainer extends Container class ProjectServiceContainer extends Container
{ {
private static $parameters = array(
);
/** /**
* Constructor. * Constructor.
*/ */
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct(new ParameterBag(self::$parameters));
} }
} }

View File

@ -16,15 +16,16 @@ use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
*/ */
class ProjectServiceContainer extends Container class ProjectServiceContainer extends Container
{ {
private $parameters; private static $parameters = array(
'empty_value' => '',
'some_string' => '-',
);
/** /**
* Constructor. * Constructor.
*/ */
public function __construct() public function __construct()
{ {
$this->parameters = $this->getDefaultParameters();
$this->services = $this->services =
$this->scopedServices = $this->scopedServices =
$this->scopeStacks = array(); $this->scopeStacks = array();
@ -40,6 +41,14 @@ class ProjectServiceContainer extends Container
$this->aliases = array(); $this->aliases = array();
} }
/**
* {@inheritdoc}
*/
public function compile()
{
throw new LogicException('You cannot compile a dumped frozen container.');
}
/** /**
* Gets the 'test' service. * Gets the 'test' service.
* *
@ -60,11 +69,11 @@ class ProjectServiceContainer extends Container
{ {
$name = strtolower($name); $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)); throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
} }
return $this->parameters[$name]; return self::$parameters[$name];
} }
/** /**
@ -74,7 +83,7 @@ class ProjectServiceContainer extends Container
{ {
$name = strtolower($name); $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);
} }
/** /**
@ -91,21 +100,9 @@ class ProjectServiceContainer extends Container
public function getParameterBag() public function getParameterBag()
{ {
if (null === $this->parameterBag) { if (null === $this->parameterBag) {
$this->parameterBag = new FrozenParameterBag($this->parameters); $this->parameterBag = new FrozenParameterBag(self::$parameters);
} }
return $this->parameterBag; return $this->parameterBag;
} }
/**
* Gets the default parameters.
*
* @return array An array of the default parameters
*/
protected function getDefaultParameters()
{
return array(
'empty_value' => '',
'some_string' => '-',
);
}
} }

View File

@ -0,0 +1,64 @@
<?php
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\Exception\InactiveScopeException;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
/**
* ProjectServiceContainer
*
* This class has been auto-generated
* by the Symfony Dependency Injection Component.
*/
class ProjectServiceContainer extends Container
{
private static $parameters = array(
);
/**
* Constructor.
*/
public function __construct()
{
parent::__construct(new ParameterBag(self::$parameters));
$this->methodMap = array(
'service_from_anonymous_factory' => 'getServiceFromAnonymousFactoryService',
'service_with_method_call_and_factory' => 'getServiceWithMethodCallAndFactoryService',
);
}
/**
* Gets the 'service_from_anonymous_factory' service.
*
* This service is shared.
* This method always returns the same instance of the service.
*
* @return \Bar\FooClass A Bar\FooClass instance.
*/
protected function getServiceFromAnonymousFactoryService()
{
return $this->services['service_from_anonymous_factory'] = call_user_func(array(new \Bar\FooClass(), 'getInstance'));
}
/**
* Gets the 'service_with_method_call_and_factory' service.
*
* This service is shared.
* This method always returns the same instance of the service.
*
* @return \Bar\FooClass A Bar\FooClass instance.
*/
protected function getServiceWithMethodCallAndFactoryService()
{
$this->services['service_with_method_call_and_factory'] = $instance = new \Bar\FooClass();
$instance->setBar(\Bar\FooClass::getInstance());
return $instance;
}
}

View File

@ -16,22 +16,7 @@ use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
*/ */
class ProjectServiceContainer extends Container class ProjectServiceContainer extends Container
{ {
/** private static $parameters = array(
* Constructor.
*/
public function __construct()
{
parent::__construct(new ParameterBag($this->getDefaultParameters()));
}
/**
* Gets the default parameters.
*
* @return array An array of the default parameters
*/
protected function getDefaultParameters()
{
return array(
'foo' => '%baz%', 'foo' => '%baz%',
'baz' => 'bar', 'baz' => 'bar',
'bar' => 'foo is %%foo bar', 'bar' => 'foo is %%foo bar',
@ -47,5 +32,12 @@ class ProjectServiceContainer extends Container
7 => 'null', 7 => 'null',
), ),
); );
/**
* Constructor.
*/
public function __construct()
{
parent::__construct(new ParameterBag(self::$parameters));
} }
} }

View File

@ -16,12 +16,18 @@ use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
*/ */
class ProjectServiceContainer extends Container class ProjectServiceContainer extends Container
{ {
private static $parameters = array(
'baz_class' => 'BazClass',
'foo_class' => 'Bar\\FooClass',
'foo' => 'bar',
);
/** /**
* Constructor. * Constructor.
*/ */
public function __construct() public function __construct()
{ {
parent::__construct(new ParameterBag($this->getDefaultParameters())); parent::__construct(new ParameterBag(self::$parameters));
$this->methodMap = array( $this->methodMap = array(
'bar' => 'getBarService', 'bar' => 'getBarService',
'baz' => 'getBazService', 'baz' => 'getBazService',
@ -38,7 +44,10 @@ class ProjectServiceContainer extends Container
'foo_with_inline' => 'getFooWithInlineService', 'foo_with_inline' => 'getFooWithInlineService',
'inlined' => 'getInlinedService', 'inlined' => 'getInlinedService',
'method_call1' => 'getMethodCall1Service', 'method_call1' => 'getMethodCall1Service',
'new_factory' => 'getNewFactoryService',
'new_factory_service' => 'getNewFactoryServiceService',
'request' => 'getRequestService', 'request' => 'getRequestService',
'service_from_static_method' => 'getServiceFromStaticMethodService',
); );
$this->aliases = array( $this->aliases = array(
'alias_for_alias' => 'foo', 'alias_for_alias' => 'foo',
@ -265,6 +274,23 @@ class ProjectServiceContainer extends Container
return $instance; return $instance;
} }
/**
* Gets the 'new_factory_service' service.
*
* This service is shared.
* This method always returns the same instance of the service.
*
* @return \FooBarBaz A FooBarBaz instance.
*/
protected function getNewFactoryServiceService()
{
$this->services['new_factory_service'] = $instance = $this->get('new_factory')->getInstance();
$instance->foo = 'bar';
return $instance;
}
/** /**
* Gets the 'request' service. * Gets the 'request' service.
* *
@ -278,6 +304,19 @@ class ProjectServiceContainer extends Container
throw new RuntimeException('You have requested a synthetic service ("request"). The DIC does not know how to construct this service.'); throw new RuntimeException('You have requested a synthetic service ("request"). The DIC does not know how to construct this service.');
} }
/**
* Gets the 'service_from_static_method' service.
*
* This service is shared.
* This method always returns the same instance of the service.
*
* @return \Bar\FooClass A Bar\FooClass instance.
*/
protected function getServiceFromStaticMethodService()
{
return $this->services['service_from_static_method'] = \Bar\FooClass::getInstance();
}
/** /**
* Updates the 'request' service. * Updates the 'request' service.
*/ */
@ -332,16 +371,23 @@ class ProjectServiceContainer extends Container
} }
/** /**
* Gets the default parameters. * Gets the 'new_factory' service.
* *
* @return array An array of the default parameters * This service is shared.
* This method always returns the same instance of the service.
*
* This service is private.
* If you want to be able to request this service from the container directly,
* make it public, otherwise you might end up with broken code.
*
* @return \FactoryClass A FactoryClass instance.
*/ */
protected function getDefaultParameters() protected function getNewFactoryService()
{ {
return array( $this->services['new_factory'] = $instance = new \FactoryClass();
'baz_class' => 'BazClass',
'foo_class' => 'Bar\\FooClass', $instance->foo = 'bar';
'foo' => 'bar',
); return $instance;
} }
} }

View File

@ -16,15 +16,17 @@ use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
*/ */
class ProjectServiceContainer extends Container class ProjectServiceContainer extends Container
{ {
private $parameters; private static $parameters = array(
'baz_class' => 'BazClass',
'foo_class' => 'Bar\\FooClass',
'foo' => 'bar',
);
/** /**
* Constructor. * Constructor.
*/ */
public function __construct() public function __construct()
{ {
$this->parameters = $this->getDefaultParameters();
$this->services = $this->services =
$this->scopedServices = $this->scopedServices =
$this->scopeStacks = array(); $this->scopeStacks = array();
@ -46,7 +48,9 @@ class ProjectServiceContainer extends Container
'foo_bar' => 'getFooBarService', 'foo_bar' => 'getFooBarService',
'foo_with_inline' => 'getFooWithInlineService', 'foo_with_inline' => 'getFooWithInlineService',
'method_call1' => 'getMethodCall1Service', 'method_call1' => 'getMethodCall1Service',
'new_factory_service' => 'getNewFactoryServiceService',
'request' => 'getRequestService', 'request' => 'getRequestService',
'service_from_static_method' => 'getServiceFromStaticMethodService',
); );
$this->aliases = array( $this->aliases = array(
'alias_for_alias' => 'foo', 'alias_for_alias' => 'foo',
@ -55,6 +59,14 @@ class ProjectServiceContainer extends Container
); );
} }
/**
* {@inheritdoc}
*/
public function compile()
{
throw new LogicException('You cannot compile a dumped frozen container.');
}
/** /**
* Gets the 'bar' service. * Gets the 'bar' service.
* *
@ -261,6 +273,26 @@ class ProjectServiceContainer extends Container
return $instance; return $instance;
} }
/**
* Gets the 'new_factory_service' service.
*
* This service is shared.
* This method always returns the same instance of the service.
*
* @return \FooBarBaz A FooBarBaz instance.
*/
protected function getNewFactoryServiceService()
{
$a = new \FactoryClass();
$a->foo = 'bar';
$this->services['new_factory_service'] = $instance = $a->getInstance();
$instance->foo = 'bar';
return $instance;
}
/** /**
* Gets the 'request' service. * Gets the 'request' service.
* *
@ -274,6 +306,19 @@ class ProjectServiceContainer extends Container
throw new RuntimeException('You have requested a synthetic service ("request"). The DIC does not know how to construct this service.'); throw new RuntimeException('You have requested a synthetic service ("request"). The DIC does not know how to construct this service.');
} }
/**
* Gets the 'service_from_static_method' service.
*
* This service is shared.
* This method always returns the same instance of the service.
*
* @return \Bar\FooClass A Bar\FooClass instance.
*/
protected function getServiceFromStaticMethodService()
{
return $this->services['service_from_static_method'] = \Bar\FooClass::getInstance();
}
/** /**
* Updates the 'request' service. * Updates the 'request' service.
*/ */
@ -291,11 +336,11 @@ class ProjectServiceContainer extends Container
{ {
$name = strtolower($name); $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)); throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
} }
return $this->parameters[$name]; return self::$parameters[$name];
} }
/** /**
@ -305,7 +350,7 @@ class ProjectServiceContainer extends Container
{ {
$name = strtolower($name); $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);
} }
/** /**
@ -322,22 +367,9 @@ class ProjectServiceContainer extends Container
public function getParameterBag() public function getParameterBag()
{ {
if (null === $this->parameterBag) { if (null === $this->parameterBag) {
$this->parameterBag = new FrozenParameterBag($this->parameters); $this->parameterBag = new FrozenParameterBag(self::$parameters);
} }
return $this->parameterBag; return $this->parameterBag;
} }
/**
* Gets the default parameters.
*
* @return array An array of the default parameters
*/
protected function getDefaultParameters()
{
return array(
'baz_class' => 'BazClass',
'foo_class' => 'Bar\\FooClass',
'foo' => 'bar',
);
}
} }

View File

@ -52,5 +52,14 @@
<service id="request" class="Request" synthetic="true" synchronized="true" lazy="true"/> <service id="request" class="Request" synthetic="true" synchronized="true" lazy="true"/>
<service id="decorator_service" decorates="decorated" /> <service id="decorator_service" decorates="decorated" />
<service id="decorator_service_with_name" decorates="decorated" decoration-inner-name="decorated.pif-pouf"/> <service id="decorator_service_with_name" decorates="decorated" decoration-inner-name="decorated.pif-pouf"/>
<service id="new_factory1" class="FooBarClass">
<factory function="factory" />
</service>
<service id="new_factory2" class="FooBarClass">
<factory service="baz" method="getClass" />
</service>
<service id="new_factory3" class="FooBarClass">
<factory class="BazClass" method="getInstance" />
</service>
</services> </services>
</container> </container>

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